diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 18 | ||||
-rw-r--r-- | src/compiler.js | 7 | ||||
-rw-r--r-- | src/intertyper.js | 2 | ||||
-rw-r--r-- | src/jsifier.js | 44 | ||||
-rw-r--r-- | src/library.js | 145 | ||||
-rw-r--r-- | src/library_browser.js | 7 | ||||
-rw-r--r-- | src/library_gc.js | 2 | ||||
-rw-r--r-- | src/library_gl.js | 353 | ||||
-rw-r--r-- | src/library_sdl.js | 35 | ||||
-rw-r--r-- | src/modules.js | 22 | ||||
-rw-r--r-- | src/parseTools.js | 88 | ||||
-rw-r--r-- | src/preamble.js | 71 | ||||
-rw-r--r-- | src/relooper/Relooper.cpp | 84 | ||||
-rw-r--r-- | src/relooper/test.txt | 4 | ||||
-rw-r--r-- | src/relooper/test2.txt | 15 | ||||
-rw-r--r-- | src/relooper/test3.txt | 38 | ||||
-rw-r--r-- | src/relooper/test4.txt | 21 | ||||
-rw-r--r-- | src/relooper/test6.txt | 15 | ||||
-rw-r--r-- | src/relooper/test_debug.txt | 15 | ||||
-rw-r--r-- | src/relooper/test_fuzz1.txt | 13 | ||||
-rw-r--r-- | src/relooper/test_fuzz5.txt | 27 | ||||
-rw-r--r-- | src/relooper/test_inf.txt | 651 | ||||
-rw-r--r-- | src/runtime.js | 9 | ||||
-rw-r--r-- | src/settings.js | 30 | ||||
-rw-r--r-- | src/shell.html | 2 |
25 files changed, 1069 insertions, 649 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index adc615fb..ecb5ea6b 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -18,6 +18,7 @@ function recomputeLines(func) { // Handy sets var BRANCH_INVOKE = set('branch', 'invoke'); +var LABEL_ENDERS = set('branch', 'return'); var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic'); var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam'); @@ -88,7 +89,7 @@ function analyzer(data, sidePass) { // Internal line if (!currLabelFinished) { item.functions.slice(-1)[0].labels.slice(-1)[0].lines.push(subItem); // If this line fails, perhaps missing a label? - if (subItem.intertype === 'branch') { + if (subItem.intertype in LABEL_ENDERS) { currLabelFinished = true; } } else { @@ -121,7 +122,8 @@ function analyzer(data, sidePass) { // Legalization if (USE_TYPED_ARRAYS == 2) { function getLegalVars(base, bits, allowLegal) { - if (allowLegal && bits <= 32) return [{ ident: base, bits: bits }]; + bits = bits || 32; // things like pointers are all i32, but show up as 0 bits from getBits + if (allowLegal && bits <= 32) return [{ ident: base + ('i' + bits in Runtime.INT_TYPES ? '' : '$0'), bits: bits }]; if (isNumber(base)) return getLegalLiterals(base, bits); var ret = new Array(Math.ceil(bits/32)); var i = 0; @@ -646,13 +648,7 @@ function analyzer(data, sidePass) { default: throw 'Invalid mathop for legalization: ' + [value.op, item.lineNum, dump(item)]; } // Do the legalization - var sourceElements; - if (sourceBits <= 32) { - // The input is a legal type - sourceElements = [{ ident: value.params[0].ident, bits: sourceBits }]; - } else { - sourceElements = getLegalVars(value.params[0].ident, sourceBits); - } + var sourceElements = getLegalVars(value.params[0].ident, sourceBits, true); if (!isNumber(shifts)) { // We can't statically legalize this, do the operation at runtime TODO: optimize assert(sourceBits == 64, 'TODO: handle nonconstant shifts on != 64 bits'); @@ -684,9 +680,9 @@ function analyzer(data, sidePass) { params: [(signed && j + whole > sourceElements.length) ? signedKeepAlive : null], type: 'i32', }; - if (j == 0 && isUnsignedOp(value.op) && sourceBits < 32) { + if (j == 0 && sourceBits < 32) { // zext sign correction - result.ident = makeSignOp(result.ident, 'i' + sourceBits, 'un', 1, 1); + result.ident = makeSignOp(result.ident, 'i' + sourceBits, isUnsignedOp(value.op) ? 'un' : 're', 1, 1); } if (fraction != 0) { var other = { diff --git a/src/compiler.js b/src/compiler.js index 0b43842e..3047daf1 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -172,20 +172,21 @@ assert(!(USE_TYPED_ARRAYS === 2 && QUANTUM_SIZE !== 4), 'For USE_TYPED_ARRAYS == if (ASM_JS) { assert(!ALLOW_MEMORY_GROWTH, 'Cannot grow asm.js heap'); assert((TOTAL_MEMORY&(TOTAL_MEMORY-1)) == 0, 'asm.js heap must be power of 2'); + assert(DISABLE_EXCEPTION_CATCHING == 1, 'asm.js does not support C++ exceptions yet'); } assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB)); // shared libraries must have named globals // Output some info and warnings based on settings if (phase == 'pre') { - if (!MICRO_OPTS || !RELOOP || ASSERTIONS || CHECK_SIGNS || CHECK_OVERFLOWS || INIT_STACK || INIT_HEAP || + if (!MICRO_OPTS || !RELOOP || ASSERTIONS || CHECK_SIGNS || CHECK_OVERFLOWS || INIT_HEAP || !SKIP_STACK_IN_SMALL || SAFE_HEAP || !DISABLE_EXCEPTION_CATCHING) { print('// Note: Some Emscripten settings will significantly limit the speed of the generated code.'); } else { print('// Note: For maximum-speed code, see "Optimizing Code" on the Emscripten wiki, https://github.com/kripken/emscripten/wiki/Optimizing-Code'); } - if (DOUBLE_MODE || CORRECT_SIGNS || CORRECT_OVERFLOWS || CORRECT_ROUNDINGS) { + if (DOUBLE_MODE || CORRECT_SIGNS || CORRECT_OVERFLOWS || CORRECT_ROUNDINGS || CHECK_HEAP_ALIGN) { print('// Note: Some Emscripten settings may limit the speed of the generated code.'); } } @@ -198,7 +199,7 @@ load('parseTools.js'); load('intertyper.js'); load('analyzer.js'); load('jsifier.js'); -if (RELOOP) load('relooper.js') +if (RELOOP) load(RELOOPER) globalEval(processMacros(preprocess(read('runtime.js')))); Runtime.QUANTUM_SIZE = QUANTUM_SIZE; diff --git a/src/intertyper.js b/src/intertyper.js index c1a98354..6c88e765 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -741,10 +741,12 @@ function intertyper(data, sidePass, baseLineNums) { processItem: function(item) { item.intertype = 'atomic'; if (item.tokens[0].text == 'atomicrmw') { + if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1); item.op = item.tokens[1].text; item.tokens.splice(1, 1); } else { assert(item.tokens[0].text == 'cmpxchg') + if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1); item.op = 'cmpxchg'; } var last = getTokenIndexByText(item.tokens, ';'); diff --git a/src/jsifier.js b/src/jsifier.js index 21d34b67..ff58ece2 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -375,6 +375,7 @@ function JSify(data, functionsOnly, givenFunctions) { var ret = [item]; item.JS = 'var ' + item.ident + ';'; // Set the actual value in a postset, since it may be a global variable. We also order by dependencies there + Variables.globals[item.ident].targetIdent = item.value.ident; var value = Variables.globals[item.ident].resolvedAlias = finalizeLLVMParameter(item.value); var fix = ''; if (BUILD_AS_SHARED_LIB == 2 && !item.private_) { @@ -478,8 +479,7 @@ function JSify(data, functionsOnly, givenFunctions) { ident = '_' + ident; } var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : ''); - // redirected idents just need a var, but no value assigned to them - it would be unused - var contentText = isFunction ? snippet : ('var ' + ident + (redirectedIdent ? '' : '=' + snippet) + ';'); + var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';'); if (ASM_JS) { var sig = LibraryManager.library[ident.substr(1) + '__sig']; if (isFunction && sig && LibraryManager.library[ident.substr(1) + '__asm']) { @@ -506,9 +506,9 @@ function JSify(data, functionsOnly, givenFunctions) { item.JS = ''; } else if (LibraryManager.library.hasOwnProperty(shortident)) { item.JS = addFromLibrary(shortident); - } else { + } else if (!LibraryManager.library.hasOwnProperty(shortident + '__inline')) { item.JS = 'var ' + item.ident + '; // stub for ' + item.ident; - if (WARN_ON_UNDEFINED_SYMBOLS) { + if (WARN_ON_UNDEFINED_SYMBOLS || ASM_JS) { // always warn on undefs in asm, since it breaks validation warn('Unresolved symbol: ' + item.ident); } } @@ -914,7 +914,11 @@ function JSify(data, functionsOnly, givenFunctions) { case VAR_NATIVIZED: if (isNumber(item.ident)) { // Direct write to a memory address; this may be an intentional segfault, if not, it is a bug in the source - return 'throw "fault on write to ' + item.ident + '";'; + if (ASM_JS) { + return 'abort(' + item.ident + ')'; + } else { + return 'throw "fault on write to ' + item.ident + '";'; + } } return item.ident + '=' + value + ';'; // We have the actual value here break; @@ -1200,10 +1204,13 @@ function JSify(data, functionsOnly, givenFunctions) { switch (item.op) { case 'add': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue+' + param2, type, null, null, null, null, ',') + ',tempValue)'; case 'sub': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue-' + param2, type, null, null, null, null, ',') + ',tempValue)'; + case 'or': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue|' + param2, type, null, null, null, null, ',') + ',tempValue)'; + case 'and': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue&' + param2, type, null, null, null, null, ',') + ',tempValue)'; + case 'xor': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue^' + param2, type, null, null, null, null, ',') + ',tempValue)'; case 'xchg': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, param2, type, null, null, null, null, ',') + ',tempValue)'; case 'cmpxchg': { var param3 = finalizeLLVMParameter(item.params[2]); - return '(tempValue=' + makeGetValue(param1, 0, type) + ',(' + makeGetValue(param1, 0, type) + '==' + param2 + ' ? ' + makeSetValue(param1, 0, param3, type, null, null, null, null, ',') + ' : 0),tempValue)'; + return '(tempValue=' + makeGetValue(param1, 0, type) + ',(' + makeGetValue(param1, 0, type) + '==(' + param2 + '|0) ? ' + makeSetValue(param1, 0, param3, type, null, null, null, null, ',') + ' : 0),tempValue)'; } default: throw 'unhandled atomic op: ' + item.op; } @@ -1222,6 +1229,15 @@ function JSify(data, functionsOnly, givenFunctions) { var impl = item.ident ? getVarImpl(item.funcData, item.ident) : VAR_EMULATED; switch (impl) { case VAR_NATIVIZED: { + if (isNumber(item.ident)) { + item.assignTo = null; + // Direct read from a memory address; this may be an intentional segfault, if not, it is a bug in the source + if (ASM_JS) { + return 'abort(' + item.ident + ')'; + } else { + return 'throw "fault on read from ' + item.ident + '";'; + } + } return value; // We have the actual value here } case VAR_EMULATED: return makeGetValue(value, 0, item.type, 0, item.unsigned, 0, item.align); @@ -1245,7 +1261,7 @@ function JSify(data, functionsOnly, givenFunctions) { makeFuncLineActor('insertvalue', function(item) { assert(item.indexes.length == 1); // TODO: see extractvalue var ret = '(', ident; - if (item.ident === 'undef') { + if (item.ident === '0') { item.ident = 'tempValue'; ret += item.ident + ' = [' + makeEmptyStruct(item.type) + '], '; } @@ -1285,6 +1301,7 @@ function JSify(data, functionsOnly, givenFunctions) { // We cannot compile assembly. See comment in intertyper.js:'Call' assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!'); + ident = Variables.resolveAliasToIdent(ident); var shortident = ident.slice(1); var callIdent = LibraryManager.getRootIdent(shortident); if (callIdent) { @@ -1402,6 +1419,9 @@ function JSify(data, functionsOnly, givenFunctions) { if (ASM_JS) { assert(returnType.search(/\("'\[,/) == -1); // XXX need isFunctionType(type, out) callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py + } else if (SAFE_DYNCALLS) { + assert(!ASM_JS, 'cannot emit safe dyncalls in asm'); + callIdent = '(tempInt=' + callIdent + ',tempInt < 0 || tempInt >= FUNCTION_TABLE.length-1 || !FUNCTION_TABLE[tempInt] ? abort("dyncall error: ' + sig + ' " + FUNCTION_TABLE_NAMES[tempInt]) : tempInt)'; } callIdent = Functions.getTable(sig) + '[' + callIdent + ']'; } @@ -1512,7 +1532,7 @@ function JSify(data, functionsOnly, givenFunctions) { print('// ASM_LIBRARY FUNCTIONS'); function fix(f) { // fix indenting to not confuse js optimizer f = f.substr(f.indexOf('f')); // remove initial spaces before 'function' - f = f.substr(0, f.lastIndexOf('\n')+1); // remove spaces and last } + f = f.substr(0, f.lastIndexOf('\n')+1); // remove spaces and last } XXX assumes function has multiple lines return f + '}'; // add unindented } to match function } print(asmLibraryFunctions.map(fix).join('\n')); @@ -1568,9 +1588,11 @@ function JSify(data, functionsOnly, givenFunctions) { var shellParts = read(shellFile).split('{{BODY}}'); print(shellParts[1]); // Print out some useful metadata (for additional optimizations later, like the eliminator) - print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) { - return IGNORED_FUNCTIONS.indexOf(func.ident) < 0; - })) + '\n'); + if (EMIT_GENERATED_FUNCTIONS) { + print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) { + return IGNORED_FUNCTIONS.indexOf(func.ident) < 0; + })) + '\n'); + } PassManager.serialize(); diff --git a/src/library.js b/src/library.js index 900b3fee..a288b739 100644 --- a/src/library.js +++ b/src/library.js @@ -2403,6 +2403,7 @@ LibraryManager.library = { case {{{ cDefine('_SC_STREAM_MAX') }}}: return 16; case {{{ cDefine('_SC_TZNAME_MAX') }}}: return 6; case {{{ cDefine('_SC_THREAD_DESTRUCTOR_ITERATIONS') }}}: return 4; + case {{{ cDefine('_SC_NPROCESSORS_ONLN') }}}: return 1; } ___setErrNo(ERRNO_CODES.EINVAL); return -1; @@ -2491,6 +2492,17 @@ LibraryManager.library = { continue; } + // TODO: Support strings like "%5c" etc. + if (format[formatIndex] === '%' && format[formatIndex+1] == 'c') { + var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; + argIndex += Runtime.getNativeFieldSize('void*'); + fields++; + next = get(); + {{{ makeSetValue('argPtr', 0, 'next', 'i8') }}} + formatIndex += 2; + continue; + } + // remove whitespace while (1) { next = get(); @@ -3352,14 +3364,15 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.ECHILD); return -1; }, - perror__deps: ['puts', 'putc', 'strerror', '__errno_location'], + perror__deps: ['puts', 'fputs', 'fputc', 'strerror', '__errno_location'], perror: function(s) { // void perror(const char *s); // http://pubs.opengroup.org/onlinepubs/000095399/functions/perror.html + var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}; if (s) { - _puts(s); - _putc(':'.charCodeAt(0)); - _putc(' '.charCodeAt(0)); + _fputs(s, stdout); + _fputc(':'.charCodeAt(0), stdout); + _fputc(' '.charCodeAt(0), stdout); } var errnum = {{{ makeGetValue('___errno_location()', '0', 'i32') }}}; _puts(_strerror(errnum)); @@ -4302,8 +4315,9 @@ LibraryManager.library = { ptr = ptr|0; value = value|0; num = num|0; var stop = 0, value4 = 0, stop4 = 0, unaligned = 0; stop = (ptr + num)|0; - if (num|0 >= {{{ SEEK_OPTIMAL_ALIGN_MIN }}}) { + if ((num|0) >= {{{ SEEK_OPTIMAL_ALIGN_MIN }}}) { // This is unaligned, but quite large, so work hard to get to aligned settings + value = value & 0xff; unaligned = ptr & 3; value4 = value | (value << 8) | (value << 16) | (value << 24); stop4 = stop & ~3; @@ -4514,11 +4528,16 @@ LibraryManager.library = { return 0; }, + memcmp__asm: 'true', + memcmp__sig: 'iiii', memcmp: function(p1, p2, num) { - for (var i = 0; i < num; i++) { - var v1 = {{{ makeGetValue('p1', 'i', 'i8', 0, 1) }}}; - var v2 = {{{ makeGetValue('p2', 'i', 'i8', 0, 1) }}}; - if (v1 != v2) return v1 > v2 ? 1 : -1; + p1 = p1|0; p2 = p2|0; num = num|0; + var i = 0, v1 = 0, v2 = 0; + while ((i|0) < (num|0)) { + var v1 = {{{ makeGetValueAsm('p1', 'i', 'i8', true) }}}; + var v2 = {{{ makeGetValueAsm('p2', 'i', 'i8', true) }}}; + if ((v1|0) != (v2|0)) return ((v1|0) > (v2|0) ? 1 : -1)|0; + i = (i+1)|0; } return 0; }, @@ -4619,10 +4638,8 @@ LibraryManager.library = { __strtok_state: 0, strtok__deps: ['__strtok_state', 'strtok_r'], + strtok__postset: '___strtok_state = Runtime.staticAlloc(4);', strtok: function(s, delim) { - if (!___strtok_state) { - ___strtok_state = _malloc(4); - } return _strtok_r(s, delim, ___strtok_state); }, @@ -6048,6 +6065,15 @@ LibraryManager.library = { __timespec_struct_layout: Runtime.generateStructInfo([ ['i32', 'tv_sec'], ['i32', 'tv_nsec']]), + nanosleep__deps: ['usleep', '__timespec_struct_layout'], + nanosleep: function(rqtp, rmtp) { + // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); + var seconds = {{{ makeGetValue('rqtp', '___timespec_struct_layout.tv_sec', 'i32') }}}; + var nanoseconds = {{{ makeGetValue('rqtp', '___timespec_struct_layout.tv_nsec', 'i32') }}}; + {{{ makeSetValue('rmtp', '___timespec_struct_layout.tv_sec', '0', 'i32') }}} + {{{ makeSetValue('rmtp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}} + return _usleep((seconds * 1e6) + (nanoseconds / 1000)); + }, // TODO: Implement these for real. clock_gettime__deps: ['__timespec_struct_layout'], clock_gettime: function(clk_id, tp) { @@ -6687,6 +6713,9 @@ LibraryManager.library = { pthread_mutexattr_destroy: function() {}, pthread_mutex_lock: function() {}, pthread_mutex_unlock: function() {}, + pthread_mutex_trylock: function() { + return 0; + }, pthread_cond_init: function() {}, pthread_cond_destroy: function() {}, pthread_cond_broadcast: function() {}, @@ -6729,17 +6758,27 @@ LibraryManager.library = { pthread_key_create: function(key, destructor) { if (!_pthread_key_create.keys) _pthread_key_create.keys = {}; - _pthread_key_create.keys[key] = null; + // values start at 0 + _pthread_key_create.keys[key] = 0; }, pthread_getspecific: function(key) { - return _pthread_key_create.keys[key]; + return _pthread_key_create.keys[key] || 0; }, pthread_setspecific: function(key, value) { _pthread_key_create.keys[key] = value; }, + pthread_key_delete: ['$ERRNO_CODES'], + pthread_key_delete: function(key) { + if (_pthread_key_create.keys[key]) { + delete _pthread_key_create.keys[key]; + return 0; + } + return ERRNO_CODES.EINVAL; + }, + pthread_cleanup_push: function(routine, arg) { __ATEXIT__.push({ func: function() { Runtime.dynCall('vi', routine, [arg]) } }) _pthread_cleanup_push.level = __ATEXIT__.length; @@ -7244,23 +7283,56 @@ LibraryManager.library = { }, select: function(nfds, readfds, writefds, exceptfds, timeout) { - // only readfds are supported, not writefds or exceptfds + // readfds are supported, + // writefds checks socket open status + // exceptfds not supported // timeout is always 0 - fully async - assert(!writefds && !exceptfds); - var ret = 0; - var l = {{{ makeGetValue('readfds', 0, 'i32') }}}; - var h = {{{ makeGetValue('readfds', 4, 'i32') }}}; - nfds = Math.min(64, nfds); // fd sets have 64 bits - for (var fd = 0; fd < nfds; fd++) { - var bit = fd % 32, int = fd < 32 ? l : h; - if (int & (1 << bit)) { - // index is in the set, check if it is ready for read - var info = Sockets.fds[fd]; - if (!info) continue; - if (info.hasData()) ret++; + assert(!exceptfds); + + function canRead(info) { + // make sure hasData exists. + // we do create it when the socket is connected, + // but other implementations may create it lazily + return info.hasData && info.hasData(); + } + + function canWrite(info) { + // make sure socket exists. + // we do create it when the socket is connected, + // but other implementations may create it lazily + return info.socket && (info.socket.readyState == info.socket.OPEN); + } + + function checkfds(nfds, fds, can) { + if (!fds) return 0; + + var bitsSet = 0; + var dstLow = 0; + var dstHigh = 0; + var srcLow = {{{ makeGetValue('fds', 0, 'i32') }}}; + var srcHigh = {{{ makeGetValue('fds', 4, 'i32') }}}; + nfds = Math.min(64, nfds); // fd sets have 64 bits + + for (var fd = 0; fd < nfds; fd++) { + var mask = 1 << (fd % 32), int = fd < 32 ? srcLow : srcHigh; + if (int & mask) { + // index is in the set, check if it is ready for read + var info = Sockets.fds[fd]; + if (info && can(info)) { + // set bit + fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask); + bitsSet++; + } + } } + + {{{ makeSetValue('fds', 0, 'dstLow', 'i32') }}}; + {{{ makeSetValue('fds', 4, 'dstHigh', 'i32') }}}; + return bitsSet; } - return ret; + + return checkfds(nfds, readfds, canRead) + + checkfds(nfds, writefds, canWrite); }, // pty.h @@ -7306,6 +7378,23 @@ LibraryManager.library = { emscripten_random: function() { return Math.random(); }, + + emscripten_jcache_printf___deps: ['_formatString'], + emscripten_jcache_printf_: function(varargs) { + var MAX = 10240; + if (!_emscripten_jcache_printf_.buffer) { + _emscripten_jcache_printf_.buffer = _malloc(MAX); + } + var i = 0; + do { + var curr = {{{ makeGetValue('varargs', 'i*4', 'i8') }}}; + {{{ makeSetValue('_emscripten_jcache_printf_.buffer', 'i', 'curr', 'i8') }}}; + i++; + assert(i*4 < MAX); + } while (curr != 0); + Module.print(intArrayToString(__formatString(_emscripten_jcache_printf_.buffer, varargs + i*4)).replace('\\n', '')); + Runtime.stackAlloc(-4*i); // free up the stack space we know is ok to free + }, }; function autoAddDeps(object, name) { diff --git a/src/library_browser.js b/src/library_browser.js index e9396d69..5b19a360 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -204,7 +204,12 @@ mergeInto(LibraryManager.library, { var ctx; try { if (useWebGL) { - ctx = canvas.getContext('experimental-webgl', { alpha: false }); + ctx = canvas.getContext('experimental-webgl', { + alpha: false, +#if GL_TESTING + preserveDrawingBuffer: true +#endif + }); } else { ctx = canvas.getContext('2d'); } diff --git a/src/library_gc.js b/src/library_gc.js index 083019ca..f6db74d8 100644 --- a/src/library_gc.js +++ b/src/library_gc.js @@ -4,7 +4,7 @@ if (GC_SUPPORT) { EXPORTED_FUNCTIONS['_realloc'] = 1; var LibraryGC = { - $GC__deps: ['sbrk', 'realloc'], + $GC__deps: ['sbrk', 'realloc', 'calloc'], $GC: { ALLOCATIONS_TO_GC: 1*1024*1024, diff --git a/src/library_gl.js b/src/library_gl.js index 7f297195..4977d2e9 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -19,6 +19,28 @@ var LibraryGL = { uniforms: [], shaders: [], +#if FULL_ES2 + clientBuffers: [], + enabledClientBuffers: [], +#endif + currArrayBuffer: 0, + currElementArrayBuffer: 0, + + byteSizeByTypeRoot: 0x1400, // GL_BYTE + byteSizeByType: [ + 1, // GL_BYTE + 1, // GL_UNSIGNED_BYTE + 2, // GL_SHORT + 2, // GL_UNSIGNED_SHORT + 4, // GL_INT + 4, // GL_UNSIGNED_INT + 4, // GL_FLOAT + 2, // GL_2_BYTES + 3, // GL_3_BYTES + 4, // GL_4_BYTES + 8 // GL_DOUBLE + ], + uniformTable: {}, // name => uniform ID. the uID must be identical until relinking, cannot create a new uID each call to glGetUniformLocation packAlignment: 4, // default alignment is 4 bytes @@ -176,12 +198,48 @@ var LibraryGL = { } }, +#if FULL_ES2 + calcBufLength: function(size, type, stride, count) { + if (stride > 0) { + return count * stride; // XXXvlad this is not exactly correct I don't think + } + var typeSize = GL.byteSizeByType[type - GL.byteSizeByTypeRoot]; + return size * typeSize * count; + }, + + preDrawHandleClientVertexAttribBindings: function(count) { + GL.resetBufferBinding = false; + for (var i = 0; i < GL.maxVertexAttribs; ++i) { + if (!GL.enabledClientBuffers[i] || !GL.clientBuffers[i]) continue; + + GL.resetBufferBinding = true; + + var cb = GL.clientBuffers[i]; + + var buf = Module.ctx.createBuffer(); + Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, buf); + Module.ctx.bufferData(Module.ctx.ARRAY_BUFFER, + HEAPU8.subarray(cb.ptr, cb.ptr + GL.calcBufLength(cb.size, cb.type, cb.stride, count)), + Module.ctx.DYNAMIC_DRAW); + Module.ctx.vertexAttribPointer(i, cb.size, cb.type, cb.normalized, cb.stride, 0); + } + }, + + postDrawHandleClientVertexAttribBindings: function() { + if (GL.resetBufferBinding) { + Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, GL.buffers[GL.currArrayBuffer]); + } + }, +#endif + initExtensions: function() { if (GL.initExtensions.done) return; GL.initExtensions.done = true; if (!Module.useWebGL) return; // an app might link both gl and 2d backends + GL.maxVertexAttribs = Module.ctx.getParameter(Module.ctx.MAX_VERTEX_ATTRIBS); + GL.compressionExt = Module.ctx.getExtension('WEBGL_compressed_texture_s3tc') || Module.ctx.getExtension('MOZ_WEBGL_compressed_texture_s3tc') || Module.ctx.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc'); @@ -456,6 +514,9 @@ var LibraryGL = { var id = {{{ makeGetValue('buffers', 'i*4', 'i32') }}}; Module.ctx.deleteBuffer(GL.buffers[id]); GL.buffers[id] = null; + + if (id == GL.currArrayBuffer) GL.currArrayBuffer = 0; + if (id == GL.currElementArrayBuffer) GL.currElementArrayBuffer = 0; } }, @@ -474,11 +535,9 @@ var LibraryGL = { }, glIsBuffer: function(buffer) { - var fb = GL.buffers[buffer]; - if (typeof(fb) == 'undefined') { - return 0; - } - return Module.ctx.isBuffer(fb); + var b = GL.buffers[buffer]; + if (!b) return 0; + return Module.ctx.isBuffer(b); }, glGenRenderbuffers__sig: 'vii', @@ -509,11 +568,9 @@ var LibraryGL = { }, glIsRenderbuffer: function(renderbuffer) { - var fb = GL.renderbuffers[renderbuffer]; - if (typeof(fb) == 'undefined') { - return 0; - } - return Module.ctx.isRenderbuffer(fb); + var rb = GL.renderbuffers[renderbuffer]; + if (!rb) return 0; + return Module.ctx.isRenderbuffer(rb); }, glGetUniformfv: function(program, location, params) { @@ -554,6 +611,11 @@ var LibraryGL = { }, glGetVertexAttribfv: function(index, pname, params) { +#if FULL_ES2 + if (GL.clientBuffers[index]) { + Module.printErr("glGetVertexAttribfv on client-side array: not supported, bad data returned"); + } +#endif var data = Module.ctx.getVertexAttrib(index, pname); if (typeof data == 'number') { {{{ makeSetValue('params', '0', 'data', 'float') }}}; @@ -565,6 +627,11 @@ var LibraryGL = { }, glGetVertexAttribiv: function(index, pname, params) { +#if FULL_ES2 + if (GL.clientBuffers[index]) { + Module.printErr("glGetVertexAttribiv on client-side array: not supported, bad data returned"); + } +#endif var data = Module.ctx.getVertexAttrib(index, pname); if (typeof data == 'number' || typeof data == 'boolean') { {{{ makeSetValue('params', '0', 'data', 'i32') }}}; @@ -576,6 +643,11 @@ var LibraryGL = { }, glGetVertexAttribPointerv: function(index, pname, pointer) { +#if FULL_ES2 + if (GL.clientBuffers[index]) { + Module.printErr("glGetVertexAttribPointer on client-side array: not supported, bad data returned"); + } +#endif {{{ makeSetValue('pointer', '0', 'Module.ctx.getVertexAttribOffset(index, pname)', 'i32') }}}; }, @@ -731,6 +803,12 @@ var LibraryGL = { glBindBuffer__sig: 'vii', glBindBuffer: function(target, buffer) { + if (target == Module.ctx.ARRAY_BUFFER) { + GL.currArrayBuffer = buffer; + } else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) { + GL.currElementArrayBuffer = buffer; + } + Module.ctx.bindBuffer(target, buffer ? GL.buffers[buffer] : null); }, @@ -858,11 +936,9 @@ var LibraryGL = { }, glIsShader: function(shader) { - var fb = GL.shaders[shader]; - if (typeof(fb) == 'undefined') { - return 0; - } - return Module.ctx.isShader(fb); + var s = GL.shaders[shader]; + if (!s) return 0; + return Module.ctx.isShader(s); }, glCreateProgram__sig: 'i', @@ -975,9 +1051,7 @@ var LibraryGL = { glIsFramebuffer__sig: 'ii', glIsFramebuffer: function(framebuffer) { var fb = GL.framebuffers[framebuffer]; - if (typeof(fb) == 'undefined') { - return 0; - } + if (!fb) return 0; return Module.ctx.isFramebuffer(fb); }, @@ -996,6 +1070,7 @@ var LibraryGL = { // VAO support vaos: [], currentVao: null, + enabledVertexAttribArrays: {}, // helps with vao cleanups init: function() { GLEmulation.fogColor = new Float32Array(4); @@ -1015,6 +1090,7 @@ var LibraryGL = { 0x809E: 1, // GL_SAMPLE_ALPHA_TO_COVERAGE 0x80A0: 1 // GL_SAMPLE_COVERAGE }; + _glEnable = function(cap) { // Clean up the renderer on any change to the rendering state. The optimization of // skipping renderer setup is aimed at the case of multiple glDraw* right after each other @@ -1056,6 +1132,17 @@ var LibraryGL = { return Module.ctx.isEnabled(cap); }; + var glGetBooleanv = _glGetBooleanv; + _glGetBooleanv = function(pname, p) { + var attrib = GLEmulation.getAttributeFromCapability(pname); + if (attrib !== null) { + var result = GL.immediate.enabledClientAttributes[attrib]; + {{{ makeSetValue('p', '0', 'result === true ? 1 : 0', 'i8') }}}; + return; + } + glGetBoole |