diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 14 | ||||
-rw-r--r-- | src/compiler.js | 7 | ||||
-rw-r--r-- | src/intertyper.js | 2 | ||||
-rw-r--r-- | src/jsifier.js | 37 | ||||
-rw-r--r-- | src/library.js | 107 | ||||
-rw-r--r-- | src/library_gc.js | 2 | ||||
-rw-r--r-- | src/library_gl.js | 289 | ||||
-rw-r--r-- | src/library_sdl.js | 35 | ||||
-rw-r--r-- | src/modules.js | 22 | ||||
-rw-r--r-- | src/parseTools.js | 45 | ||||
-rw-r--r-- | src/preamble.js | 57 | ||||
-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 | 4 | ||||
-rw-r--r-- | src/settings.js | 17 | ||||
-rw-r--r-- | src/shell.html | 2 |
24 files changed, 936 insertions, 587 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index c930231f..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'); 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 4af522b4..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_) { @@ -505,7 +506,7 @@ 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 || ASM_JS) { // always warn on undefs in asm, since it breaks validation warn('Unresolved symbol: ' + item.ident); @@ -913,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; @@ -1199,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; } @@ -1221,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); @@ -1244,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) + '], '; } @@ -1284,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) { @@ -1401,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 + ']'; } @@ -1567,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 d0f73fdd..1cd7e3f5 100644 --- a/src/library.js +++ b/src/library.js @@ -3363,14 +3363,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)); @@ -4313,8 +4314,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; @@ -6701,6 +6703,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() {}, @@ -6743,17 +6748,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; @@ -7258,23 +7273,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 @@ -7320,6 +7368,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_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 027b2841..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); }, @@ -1016,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 @@ -1057,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; + } + glGetBooleanv(pname, p); + }; + var glGetIntegerv = _glGetIntegerv; _glGetIntegerv = function(pname, params) { switch (pname) { @@ -1336,27 +1422,15 @@ var LibraryGL = { _glBindBuffer = function(target, buffer) { glBindBuffer(target, buffer); if (target == Module.ctx.ARRAY_BUFFER) { - GL.currArrayBuffer = buffer; if (GLEmulation.currentVao) { assert(GLEmulation.currentVao.arrayBuffer == buffer || GLEmulation.currentVao.arrayBuffer == 0 || buffer == 0, 'TODO: support for multiple array buffers in vao'); GLEmulation.currentVao.arrayBuffer = buffer; } } else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) { - GL.currElementArrayBuffer = buffer; if (GLEmulation.currentVao) GLEmulation.currentVao.elementArrayBuffer = buffer; } }; - var glDeleteBuffers = _glDeleteBuffers; - _glDeleteBuffers = function(n, buffers) { - glDeleteBuffers(n, buffers); - for (var i = 0; i < n; i++) { - var buffer = {{{ makeGetValue('buffers', 'i*4', 'i32') }}}; - if (buffer == GL.currArrayBuffer) GL.currArrayBuffer = 0; - if (buffer == GL.currElementArrayBuffer) GL.currElementArrayBuffer = 0; - } - }; - var glGetFloatv = _glGetFloatv; _glGetFloatv = function(pname, params) { if (pname == 0x0BA6) { // GL_MODELVIEW_MATRIX @@ -1411,6 +1485,22 @@ var LibraryGL = { }; }, + getAttributeFromCapability: function(cap) { + var attrib = null; + switch (cap) { + case 0x8078: // GL_TEXTURE_COORD_ARRAY + case 0x0de1: // GL_TEXTURE_2D - XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support it + attrib = GL.immediate.TEXTURE0 + GL.immediate.clientActiveTexture; break; + case 0x8074: // GL_VERTEX_ARRAY + attrib = GL.immediate.VERTEX; break; + case 0x8075: // GL_NORMAL_ARRAY + attrib = GL.immediate.NORMAL; break; + case 0x8076: // GL_COLOR_ARRAY + attrib = GL.immediate.COLOR; break; + } + return attrib; + }, + getProcAddress: function(name) { name = name.replace('EXT', '').replace('ARB', ''); // Do the translation carefully because of closure @@ -1544,7 +1634,7 @@ var LibraryGL = { // GL Immediate mode $GLImmediate__postset: 'GL.immediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(function() { GL.immediate.init() });', - $GLImmediate__deps: ['$Browser', '$GL'], + $GLImmediate__deps: ['$Browser', '$GL', '$GLEmulation'], $GLImmediate: { MAX_TEXTURES: 7, @@ -1593,17 +1683,6 @@ var LibraryGL = { clientActiveTexture: 0, clientColor: null, - 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 - ], - setClientAttribute: function(name, size, type, stride, pointer) { var attrib = this.clientAttributes[name]; attrib.name = name; @@ -1679,7 +1758,7 @@ var LibraryGL = { #endif this.enabledClientAttributes[name] = true; this.setClientAttribute(name, size, type, 0, this.rendererComponentPointer); - this.rendererComponentPointer += size * this.byteSizeByType[type - this.byteSizeByTypeRoot]; + this.rendererComponentPointer += size * GL.byteSizeByType[type - GL.byteSizeByTypeRoot]; } else { this.rendererComponents[name]++; } @@ -1703,7 +1782,7 @@ var LibraryGL = { cacheItem = temp ? temp : (cacheItem[attribute.name] = GL.immediate.rendererCacheItemTemplate.slice()); temp = cacheItem[attribute.size]; cacheItem = temp ? temp : (cacheItem[attribute.size] = GL.immediate.rendererCacheItemTemplate.slice()); - var typeIndex = attribute.type - GL.immediate.byteSizeByTypeRoot; // ensure it starts at 0 to keep the cache items dense + var typeIndex = attribute.type - GL.byteSizeByTypeRoot; // ensure it starts at 0 to keep the cache items dense temp = cacheItem[typeIndex]; cacheItem = temp ? temp : (cacheItem[typeIndex] = GL.immediate.rendererCacheItemTemplate.slice()); } @@ -2139,7 +2218,7 @@ var LibraryGL = { for (var i = 0; i < attributes.length; i++) { var attribute = attributes[i]; if (!attribute) break; - var size = attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot]; + var size = attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot]; if (size % 4 != 0) size += 4 - (size % 4); // align everything attribute.offset = bytes; bytes += size; @@ -2151,7 +2230,7 @@ var LibraryGL = { for (var i = 0; i < attributes.length; i++) { var attribute = attributes[i]; if (!attribute) break; - var size4 = Math.floor((attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot])/4); + var size4 = Math.floor((attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot])/4); for (var j = 0; j < count; j++) { for (var k = 0; k < size4; k++) { // copy in chunks of 4 bytes, our alignment makes this possible HEAP32[((start + attribute.offset + bytes*j)>>2) + k] = HEAP32[(attribute.pointer>>2) + j*size4 + k]; @@ -2169,7 +2248,7 @@ var LibraryGL = { assert((attribute.offset - bytes)%4 == 0); // XXX assuming 4-alignment bytes += attribute.offset - bytes; } - bytes += attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot]; + bytes += attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot]; if (bytes % 4 != 0) bytes += 4 - (bytes % 4); // XXX assuming 4-alignment } assert(beginEnd || bytes <= stride); // if not begin-end, explicit stride should make sense with total byte size @@ -2304,8 +2383,10 @@ var LibraryGL = { glVertex2fv: function(p) { _glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, 0); }, + + glVertex3i: 'glVertex3f', - glVertex2i: function() { throw 'glVertex2i: TODO' }, + glVertex2i: 'glVertex3f', glTexCoord2i: function(u, v) { #if ASSERTIONS @@ -2469,22 +2550,12 @@ var LibraryGL = { // ClientState/gl*Pointer glEnableClientState: function(cap, disable) { - var attrib; - switch(cap) { - case 0x8078: // GL_TEXTURE_COORD_ARRAY - case 0x0de1: // GL_TEXTURE_2D - XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support it - attrib = GL.immediate.TEXTURE0 + GL.immediate.clientActiveTexture; break; - case 0x8074: // GL_VERTEX_ARRAY - attrib = GL.immediate.VERTEX; break; - case 0x8075: // GL_NORMAL_ARRAY - attrib = GL.immediate.NORMAL; break; - case 0x8076: // GL_COLOR_ARRAY - attrib = GL.immediate.COLOR; break; - default: + var attrib = GLEmulation.getAttributeFromCapability(cap); + if (attrib === null) { #if ASSERTIONS - Module.printErr('WARNING: unhandled clientstate: ' + cap); + Module.printErr('WARNING: unhandled clientstate: ' + cap); #endif - return; + return; } if (disable && GL.immediate.enabledClientAttributes[attrib]) { GL.immediate.enabledClientAttributes[attrib] = false; @@ -2776,11 +2847,81 @@ var LibraryGL = { glTexCoord3f: function() { throw 'glTexCoord3f: TODO' }, glGetTexLevelParameteriv: function() { throw 'glGetTexLevelParameteriv: TODO' }, - // signatures of simple pass-through functions, see later - glActiveTexture__sig: 'vi', + glShadeModel: function() { Runtime.warnOnce('TODO: glShadeModel') }, + + glVertexAttribPointer__sig: 'viiiiii', + glVertexAttribPointer: function(index, size, type, normalized, stride, ptr) { +#if FULL_ES2 + if (!GL.currArrayBuffer) { + GL.clientBuffers[index] = { size: size, type: type, normalized: normalized, stride: stride, ptr: ptr }; + return; + } + + GL.clientBuffers[index] = null; +#endif + Module.ctx.vertexAttribPointer(index, size, type, normalized, stride, ptr); + }, + glEnableVertexAttribArray__sig: 'vi', + glEnableVertexAttribArray: function(index) { +#if FULL_ES2 + GL.enabledClientBuffers[index] = true; +#endif + Module.ctx.enableVertexAttribArray(index); + }, + glDisableVertexAttribArray__sig: 'vi', - glVertexAttribPointer__sig: 'viiiiii', + glDisableVertexAttribArray: function(index) { +#if FULL_ES2 + GL.enabledClientBuffers[index] = false; +#endif + Module.ctx.disableVertexAttribArray(index); + }, + + glDrawArrays: function(mode, first, count) { +#if FULL_ES2 + // bind any client-side buffers + GL.preDrawHandleClientVertexAttribBindings(count); +#endif + + Module.ctx.drawArrays(mode, first, count); + +#if FULL_ES2 + GL.postDrawHandleClientVertexAttribBindings(); +#endif + }, + + glDrawElements: function(mode, count, type, indices) { +#if FULL_ES2 + var buf; + if (!GL.currElementArrayBuffer) { + buf = Module.ctx.createBuffer(); + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, buf); + Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER, + HEAPU8.subarray(indices, indices + GL.calcBufLength(1, type, 0, count)), + Module.ctx.DYNAMIC_DRAW); + // the index is now 0 + indices = 0; + } + + // bind any client-side buffers + GL.preDrawHandleClientVertexAttribBindings(count); +#endif + + Module.ctx.drawElements(mode, count, type, indices); + +#if FULL_ES2 + GL.postDrawHandleClientVertexAttribBindings(count); + + if (!GL.currElementArrayBuffer) { + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null); + Module.ctx.deleteBuffer(buf); + } +#endif + }, + + // signatures of simple pass-through functions, see later + glActiveTexture__sig: 'vi', glCheckFramebufferStatus__sig: 'ii', glRenderbufferStorage__sig: 'viiii', @@ -2802,19 +2943,18 @@ var LibraryGL = { }; // Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name -[[0, 'shadeModel getError* finish flush'], - [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'], +[[0, 'getError* finish flush'], + [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'], [2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint polygonOffset'], - [3, 'texParameteri texParameterf drawArrays vertexAttrib2f stencilFunc stencilOp'], - [4, 'viewport clearColor scissor vertexAttrib3f colorMask drawElements renderbufferStorage blendFuncSeparate blendColor stencilFuncSeparate stencilOpSeparate'], + [3, 'texParameteri texParameterf vertexAttrib2f stencilFunc stencilOp'], + [4, 'viewport clearColor scissor vertexAttrib3f colorMask renderbufferStorage blendFuncSeparate blendColor stencilFuncSeparate stencilOpSeparate'], [5, 'vertexAttrib4f'], - [6, 'vertexAttribPointer'], [8, 'copyTexImage2D copyTexSubImage2D']].forEach(function(data) { var num = data[0]; var names = data[1]; var args = range(num).map(function(i) { return 'x' + i }).join(', '); - var plainStub = '(function(' + args + ') { ' + (num > 0 ? 'Module.ctx.NAME(' + args + ')' : '') + ' })'; - var returnStub = '(function(' + args + ') { ' + (num > 0 ? 'return Module.ctx.NAME(' + args + ')' : '') + ' })'; + var plainStub = '(function(' + args + ') { Module.ctx.NAME(' + args + ') })'; + var returnStub = '(function(' + args + ') { return Module.ctx.NAME(' + args + ') })'; names.split(' ').forEach(function(name) { var stub = plainStub; if (name[name.length-1] == '*') { @@ -2847,5 +2987,10 @@ LibraryGL.$GLEmulation__deps.push(function() { for (var func in Functions.getIndex.tentative) Functions.getIndex(func); }); +if (FORCE_GL_EMULATION) { + LibraryGL.glDrawElements__deps = LibraryGL.glDrawElements__deps.concat('$GLEmulation'); + LibraryGL.glDrawArrays__deps = LibraryGL.glDrawArrays__deps.concat('$GLEmulation'); +} + mergeInto(LibraryManager.library, LibraryGL); diff --git a/src/library_sdl.js b/src/library_sdl.js index e02e1e62..96ae6fa2 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -44,6 +44,8 @@ var LibrarySDL = { ctrlKey: false, altKey: false, + textInput: false, + startTime: null, mouseX: 0, mouseY: 0, @@ -173,6 +175,11 @@ var LibrarySDL = { ['i16', 'mod'], ['i32', 'unicode'] ]), + TextInputEvent: Runtime.generateStructInfo([ + ['i32', 'type'], + ['i32', 'windowID'], + ['b256', 'text'], + ]), MouseMotionEvent: Runtime.generateStructInfo([ ['i32', 'type'], ['i32', 'windowID'], @@ -373,7 +380,7 @@ var LibrarySDL = { } } // fall through - case 'keydown': case 'keyup': case 'mousedown': case 'mouseup': case 'DOMMouseScroll': case 'mousewheel': + case 'keydown': case 'keyup': case 'keypress': case 'mousedown': case 'mouseup': case 'DOMMouseScroll': case 'mousewheel': if (event.type == 'DOMMouseScroll' || event.type == 'mousewheel') { var button = (event.type == 'DOMMouseScroll' ? event.detail : -event.wheelDelta) > 0 ? 4 : 3; var event2 = { @@ -397,6 +404,10 @@ var LibrarySDL = { SDL.DOMButtons[event.button] = 0; } + if (event.type == 'keypress' && !SDL.textInput) { + break; + } + SDL.events.push(event); if (SDL.events.length >= 10000) { Module.printErr('SDL event queue full, dropping earliest event'); @@ -476,6 +487,15 @@ var LibrarySDL = { break; } + case 'keypress': { + {{{ makeSetValue('ptr', 'SDL.structs.TextInputEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}} + // Not filling in windowID for now + var cStr = intArrayFromString(String.fromCharCode(event.charCode)); + for (var i = 0; i < cStr.length; ++i) { + {{{ makeSetValue('ptr', 'SDL.structs.TextInputEvent.text + i', 'cStr[i]', 'i8') }}}; + } + break; + } case 'mousedown': case 'mouseup': if (event.type == 'mousedown') { // SDL_BUTTON(x) is defined as (1 << ((x)-1)). SDL buttons are 1-3, @@ -618,6 +638,7 @@ var LibrarySDL = { // Initialize this structure carefully for closure SDL.DOMEventToSDLEvent['keydown'] = 0x300 /* SDL_KEYDOWN */; SDL.DOMEventToSDLEvent['keyup'] = 0x301 /* SDL_KEYUP */; + SDL.DOMEventToSDLEvent['keypress'] = 0x303 /* SDL_TEXTINPUT */; SDL.DOMEventToSDLEvent['mousedown'] = 0x401 /* SDL_MOUSEBUTTONDOWN */; SDL.DOMEventToSDLEvent['mouseup'] = 0x402 /* SDL_MOUSEBUTTONUP */; SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */; @@ -1175,14 +1196,20 @@ var LibrarySDL = { SDL_CondWait: function() {}, SDL_DestroyCond: function() {}, - SDL_StartTextInput: function() {}, // TODO - SDL_StopTextInput: function() {}, // TODO + SDL_StartTextInput: function() { + SDL.textInput = true; + }, + SDL_StopTextInput: function() { + SDL.textInput = false; + }, // SDL Mixer Mix_Init: function(flags) { + if (!flags) return 0; return 8; /* MIX_INIT_OGG */ }, + Mix_Quit: function(){}, Mix_OpenAudio: function(frequency, format, channels, chunksize) { SDL.allocateChannels(32); @@ -1288,6 +1315,8 @@ var LibrarySDL = { // the browser has already preloaded the audio file. var channelInfo = SDL.channels[channel]; channelInfo.audio = audio = audio.cloneNode(true); + audio.numChannels = info.audio.numChannels; + audio.frequency = info.audio.frequency; if (SDL.channelFinished) { audio['onended'] = function() { // TODO: cache these Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel); diff --git a/src/modules.js b/src/modules.js index 695abbe7..afdbc21e 100644 --- a/src/modules.js +++ b/src/modules.js @@ -179,7 +179,16 @@ var Variables = { globals: {}, indexedGlobals: {}, // for indexed globals, ident ==> index // Used in calculation of indexed globals - nextIndexedOffset: 0 + nextIndexedOffset: 0, + + resolveAliasToIdent: function(ident) { + while (1) { + var varData = Variables.globals[ident]; + if (!(varData && varData.targetIdent)) break; + ident = varData.targetIdent; // might need to eval to turn (6) into 6 + } + return ident; + }, }; var Types = { @@ -330,12 +339,23 @@ var Functions = { } } } + if (table.length > 20) { + // add some newlines in the table, for readability + var j = 10; + while (j+10 < table.length) { + table[j] += '\n'; + j += 10; + } + } var indices = table.toString().replace('"', ''); if (BUILD_AS_SHARED_LIB) { // Shared libraries reuse the parent's function table. tables[t] = Functions.getTable(t) + '.push.apply(' + Functions.getTable(t) + ', [' + indices + ']);\n'; } else { tables[t] = 'var ' + Functions.getTable(t) + ' = [' + indices + '];\n'; + if (SAFE_DYNCALLS) { + tables[t] += 'var FUNCTION_TABLE_NAMES = ' + JSON.stringify(table).replace(/\n/g, '').replace(/,0/g, ',0\n') + ';\n'; + } } } if (!generated && !ASM_JS) { diff --git a/src/parseTools.js b/src/parseTools.js index 357d33a1..7f4f3a18 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -78,6 +78,7 @@ function toNiceIdent(ident) { assert(ident); if (parseFloat(ident) == ident) return ident; if (ident == 'null') return '0'; // see parseNumerical + if (ident == 'undef') return '0'; return ident.replace('%', '$').replace(/["&\\ \.@:<>,\*\[\]\(\)-]/g, '_'); } @@ -691,7 +692,7 @@ function makeCopyI64(value) { function parseArbitraryInt(str, bits) { // We parse the string into a vector of digits, base 10. This is convenient to work on. - assert(bits % 32 == 0 || ('i' + (bits % 32)) in Runtime.INT_TYPES, 'Arbitrary-sized ints must tails that are of legal size'); + assert(bits > 0); // NB: we don't check that the value in str can fit in this amount of bits function str2vec(s) { // index 0 is the highest value var ret = []; @@ -984,18 +985,24 @@ function checkSafeHeap() { function getHeapOffset(offset, type, forceAsm) { if (USE_TYPED_ARRAYS !== 2) { return offset; - } else { - if (Runtime.getNativeFieldSize(type) > 4) { - type = 'i32'; // XXX we emulate 64-bit values as 32 - } - var shifts = Math.log(Runtime.getNativeTypeSize(type))/Math.LN2; - offset = '(' + offset + ')'; - if (shifts != 0) { - return '(' + offset + '>>' + shifts + ')'; + } + + if (Runtime.getNativeFieldSize(type) > 4) { + type = 'i32'; // XXX we emulate 64-bit values as 32 + } + + var sz = Runtime.getNativeTypeSize(type); + var shifts = Math.log(sz)/Math.LN2; + offset = '(' + offset + ')'; + if (shifts != 0) { + if (CHECK_HEAP_ALIGN) { + return '(CHECK_ALIGN_' + sz + '(' + offset + ')>>' + shifts + ')'; } else { - // we need to guard against overflows here, HEAP[U]8 expects a guaranteed int - return isJSVar(offset) ? offset : '(' + offset + '|0)'; + return '(' + offset + '>>' + shifts + ')'; } + } else { + // we need to guard against overflows here, HEAP[U]8 expects a guaranteed int + return isJSVar(offset) ? offset : '(' + offset + '|0)'; } } @@ -1364,11 +1371,15 @@ var TWO_TWENTY = Math.pow(2, 20); function getFastValue(a, op, b, type) { a = a.toString(); b = b.toString(); + a = a == 'true' ? '1' : (a == 'false' ? '0' : a); + b = b == 'true' ? '1' : (b == 'false' ? '0' : b); if (isNumber(a) && isNumber(b)) { if (op == 'pow') { return Math.pow(a, b).toString(); } else { - return eval(a + op + '(' + b + ')').toString(); // parens protect us from "5 - -12" being seen as "5--12" which is "(5--)12" + var value = eval(a + op + '(' + b + ')'); // parens protect us from "5 - -12" being seen as "5--12" which is "(5--)12" + if (op == '/' && type in Runtime.INT_TYPES) value = value|0; // avoid emitting floats + return value.toString(); } } if (op == 'pow') { @@ -1548,7 +1559,7 @@ function makePointer(slab, pos, allocator, type, ptr) { var ret = ''; var index = 0; while (index < array.length) { - ret = (ret ? ret + '.concat(' : '') + '[' + array.slice(index, index + chunkSize).map(JSON.stringify) + ']' + (ret ? ')' : ''); + ret = (ret ? ret + '.concat(' : '') + '[' + array.slice(index, index + chunkSize).map(JSON.stringify) + ']' + (ret ? ')\n' : ''); index += chunkSize; } return ret; @@ -1829,9 +1840,10 @@ function makeSignOp(value, type, op, force, ignore) { if (!CHECK_SIGNS || ignore) { if (bits === 32) { if (op === 're') { - return '((' + value + ')|0)'; + return '(' + getFastValue(value, '|', '0') + ')'; } else { - return '((' + value + ')>>>0)'; + + return '(' + getFastValue(value, '>>>', '0') + ')'; // Alternatively, we can consider the lengthier // return makeInlineCalculation('VALUE >= 0 ? VALUE : ' + Math.pow(2, bits) + ' + VALUE', value, 'tempBigInt'); // which does not always turn us into a 32-bit *un*signed value @@ -1840,7 +1852,7 @@ function makeSignOp(value, type, op, force, ignore) { if (op === 're') { return makeInlineCalculation('(VALUE << ' + (32-bits) + ') >> ' + (32-bits), value, 'tempInt'); } else { - return '((' + value + ')&' + (Math.pow(2, bits)-1) + ')'; + return '(' + getFastValue(value, '&', Math.pow(2, bits)-1) + ')'; } } else { // bits > 32 if (op === 're') { @@ -2191,7 +2203,6 @@ function processMathop(item) { case 'ne': case 'eq': { // We must sign them, so we do not compare -1 to 255 (could have unsigned them both too) // since LLVM tells us if <=, >= etc. comparisons are signed, but not == and !=. - assert(paramTypes[0] == paramTypes[1]); idents[0] = makeSignOp(idents[0], paramTypes[0], 're'); idents[1] = makeSignOp(idents[1], paramTypes[1], 're'); return idents[0] + (variant === 'eq' ? '==' : '!=') + idents[1]; diff --git a/src/preamble.js b/src/preamble.js index a7731e7f..9bc68d8f 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -150,6 +150,25 @@ function SAFE_HEAP_COPY_HISTORY(dest, src) { //========================================== #endif +#if CHECK_HEAP_ALIGN +//======================================== +// Debugging tools - alignment check +//======================================== +function CHECK_ALIGN_8(addr) { + assert((addr & 7) == 0, "address must be 8-byte aligned, is " + addr + "!"); + return addr; +} +function CHECK_ALIGN_4(addr) { + assert((addr & 3) == 0, "address must be 4-byte aligned, is " + addr + "!"); + return addr; +} +function CHECK_ALIGN_2(addr) { + assert((addr & 1) == 0, "address must be 2-byte aligned!"); + return addr; +} +#endif + + #if CHECK_OVERFLOWS //======================================== // Debugging tools - Mathop overflows @@ -251,11 +270,9 @@ Module["ccall"] = ccall; // Returns the C function with a specified identifier (for C++, you need to do manual name mangling) function getCFunc(ident) { try { - var func = eval('_' + ident); + var func = globalScope['Module']['_' + ident]; // closure exported function + if (!func) func = eval('_' + ident); // explicit lookup } catch(e) { - try { - func = globalScope['Module']['_' + ident]; // closure exported function - } catch(e) {} } assert(func, 'Cannot call unknown function ' + ident + ' (perhaps LLVM optimizations or closure removed it?)'); return func; @@ -395,14 +412,6 @@ Module['ALLOC_STACK'] = ALLOC_STACK; Module['ALLOC_STATIC'] = ALLOC_STATIC; Module['ALLOC_NONE'] = ALLOC_NONE; -// Simple unoptimized memset - necessary during startup -var _memset = function(ptr, value, num) { - var stop = ptr + num; - while (ptr < stop) { - {{{ makeSetValue('ptr++', 0, 'value', 'i8', null, true) }}}; - } -} - // allocate(): This is for internal use. You can use it yourself as well, but the interface // is a little tricky (see docs right below). The reason is that it is optimized // for multiple syntaxes to save space in generated code. So you should @@ -436,7 +445,18 @@ function allocate(slab, types, allocator, ptr) { } if (zeroinit) { - _memset(ret, 0, size); + var ptr = ret, stop; +#if USE_TYPED_ARRAYS == 2 + assert((ret & 3) == 0); + stop = ret + (size & ~3); + for (; ptr < stop; ptr += 4) { + {{{ makeSetValue('ptr', '0', '0', 'i32', null, true) }}}; + } +#endif + stop = ret + size; + while (ptr < stop) { + {{{ makeSetValue('ptr++', '0', '0', 'i8', null, true) }}}; + } return ret; } @@ -447,7 +467,7 @@ function allocate(slab, types, allocator, ptr) { } #endif - var i = 0, type; + var i = 0, type, typeSize, previousType; while (i < size) { var curr = slab[i]; @@ -469,7 +489,13 @@ function allocate(slab, types, allocator, ptr) { #endif setValue(ret+i, curr, type); - i += Runtime.getNativeTypeSize(type); + + // no need to look up size unless type changes, so cache it + if (previousType !== type) { + typeSize = Runtime.getNativeTypeSize(type); + previousType = type; + } + i += typeSize; } return ret; @@ -543,6 +569,7 @@ function enlargeMemory() { while (TOTAL_MEMORY <= STATICTOP) { // Simple heuristic. Override enlargeMemory() if your program has something more optimal for it TOTAL_MEMORY = alignMemoryPage(2*TOTAL_MEMORY); } + assert(TOTAL_MEMORY <= Math.pow(2, 30)); // 2^30==1GB is a practical maximum - 2^31 is already close to possible negative numbers etc. #if USE_TYPED_ARRAYS == 1 var oldIHEAP = IHEAP; Module['HEAP'] = Module['IHEAP'] = HEAP = IHEAP = new Int32Array(TOTAL_MEMORY); diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp index ae8577b1..1a7acc15 100644 --- a/src/relooper/Relooper.cpp +++ b/src/relooper/Relooper.cpp @@ -909,48 +909,54 @@ void Relooper::Calculate(Block *Entry) { } std::stack<Shape*> &LoopStack = *((std::stack<Shape*>*)Closure); - SHAPE_SWITCH(Root, { - MultipleShape *Fused = Shape::IsMultiple(Root->Next); - // If we are fusing a Multiple with a loop into this Simple, then visit it now - if (Fused && Fused->NeedLoop) { - LoopStack.push(Fused); - RECURSE_MULTIPLE_MANUAL(FindLabeledLoops, Fused); - } - for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) { - Block *Target = iter->first; - Branch *Details = iter->second; - if (Details->Type != Branch::Direct) { - assert(LoopStack.size() > 0); - if (Details->Ancestor != LoopStack.top()) { - LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor); - Labeled->Labeled = true; - Details->Labeled = true; - } else { - Details->Labeled = false; + Shape *Next = Root; + while (Next) { + Root = Next; + Next = NULL; + + SHAPE_SWITCH(Root, { + MultipleShape *Fused = Shape::IsMultiple(Root->Next); + // If we are fusing a Multiple with a loop into this Simple, then visit it now + if (Fused && Fused->NeedLoop) { + LoopStack.push(Fused); + RECURSE_MULTIPLE_MANUAL(FindLabeledLoops, Fused); + } + for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) { + Block *Target = iter->first; + Branch *Details = iter->second; + if (Details->Type != Branch::Direct) { + assert(LoopStack.size() > 0); + if (Details->Ancestor != LoopStack.top()) { + LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor); + Labeled->Labeled = true; + Details->Labeled = true; + } else { + Details->Labeled = false; + } } } - } - if (Fused && Fused->NeedLoop) { - LoopStack.pop(); - if (Fused->Next) FindLabeledLoops(Fused->Next); - } else { - if (Root->Next) FindLabeledLoops(Root->Next); - } - }, { - if (Multiple->NeedLoop) { - LoopStack.push(Multiple); - } - RECURSE_MULTIPLE(FindLabeledLoops); - if (Multiple->NeedLoop) { + if (Fused && Fused->NeedLoop) { + LoopStack.pop(); + Next = Fused->Next; + } else { + Next = Root->Next; + } + }, { + if (Multiple->NeedLoop) { + LoopStack.push(Multiple); + } + RECURSE_MULTIPLE(FindLabeledLoops); + if (Multiple->NeedLoop) { + LoopStack.pop(); + } + Next = Root->Next; + }, { + LoopStack.push(Loop); + RECURSE_LOOP(FindLabeledLoops); LoopStack.pop(); - } - if (Root->Next) FindLabeledLoops(Root->Next); - }, { - LoopStack.push(Loop); - RECURSE_LOOP(FindLabeledLoops); - LoopStack.pop(); - if (Root->Next) FindLabeledLoops(Root->Next); - }); + Next = Root->Next; + }); + } if (First) { delete (std::stack<Shape*>*)Closure; diff --git a/src/relooper/test.txt b/src/relooper/test.txt index 12d0ef39..b7c8794d 100644 --- a/src/relooper/test.txt +++ b/src/relooper/test.txt @@ -54,7 +54,7 @@ while(1) { // code 2 if (!($2)) { var $x_1 = $x_0; - label = 18; + label = 19; break; } // code 3 @@ -64,7 +64,7 @@ while(1) { var $i_0 = $7;var $x_0 = $5; } } -if (label == 18) { +if (label == 19) { // code 7 } // code 4 diff --git a/src/relooper/test2.txt b/src/relooper/test2.txt index a847e806..c77ce491 100644 --- a/src/relooper/test2.txt +++ b/src/relooper/test2.txt @@ -1,11 +1,12 @@ ep -L1: -if (ep -> LBB1) { - LBB1 - if (!(LBB1 -> LBB2)) { - break L1; +do { + if (ep -> LBB1) { + LBB1 + if (!(LBB1 -> LBB2)) { + break; + } + LBB2 } - LBB2 -} +} while(0); LBB3 diff --git a/src/relooper/test3.txt b/src/relooper/test3.txt index 7d06f06a..696542ef 100644 --- a/src/relooper/test3.txt +++ b/src/relooper/test3.txt @@ -1,25 +1,27 @@ ep -L1: -if (ep -> LBB1) { - LBB1 - if (!(LBB1 -> LBB2)) { - break L1; +do { + if (ep -> LBB1) { + LBB1 + if (!(LBB1 -> LBB2)) { + break; + } + LBB2 } - LBB2 -} +} while(0); LBB3 -L5: -if (LBB3 -> LBB4) { - LBB4 - if (!(LBB4 -> LBB5)) { - break L5; - } - while(1) { - LBB5 - if (LBB5 -> LBB6) { - break L5; +L5: do { + if (LBB3 -> LBB4) { + LBB4 + if (!(LBB4 -> LBB5)) { + break; + } + while(1) { + LBB5 + if (LBB5 -> LBB6) { + break L5; + } } } -} +} while(0); LBB6 diff --git a/src/relooper/test4.txt b/src/relooper/test4.txt index 2ab3265a..f0bfb972 100644 --- a/src/relooper/test4.txt +++ b/src/relooper/test4.txt @@ -1,16 +1,17 @@ //19 -L1: -if ( 1 ) { - //20 - if (!( 1 )) { +do { + if ( 1 ) { + //20 + if (!( 1 )) { + label = 4; + break; + } + //21 + break; + } else { label = 4; - break L1; } - //21 - break L1; -} else { - label = 4; -} +} while(0); if (label == 4) { //22 } diff --git a/src/relooper/test6.txt b/src/relooper/test6.txt index 0ec7e666..c5effd08 100644 --- a/src/relooper/test6.txt +++ b/src/relooper/test6.txt @@ -1,11 +1,12 @@ //0 -L1: -if (check(0)) { - //1 - if (!(check(1))) { - break L1; +do { + if (check(0)) { + //1 + if (!(check(1))) { + break; + } + //2 } - //2 -} +} while(0); //3 diff --git a/src/relooper/test_debug.txt b/src/relooper/test_debug.txt index 02377fb7..1c7d0508 100644 --- a/src/relooper/test_debug.txt +++ b/src/relooper/test_debug.txt @@ -83,13 +83,14 @@ int main() { // === Optimizing shapes === // Fusing Multiple to Simple ep -L1: -if (ep -> LBB1) { - LBB1 - if (!(LBB1 -> LBB2)) { - break L1; +do { + if (ep -> LBB1) { + LBB1 + if (!(LBB1 -> LBB2)) { + break; + } + LBB2 } - LBB2 -} +} while(0); LBB3 diff --git a/src/relooper/test_fuzz1.txt b/src/relooper/test_fuzz1.txt index 09edb594..5122257e 100644 --- a/src/relooper/test_fuzz1.txt +++ b/src/relooper/test_fuzz1.txt @@ -3,12 +3,13 @@ print('entry'); var label; var state; var decisions = [4, 1, 7, 2, 6, 6, 8]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] } print(5); state = check(); print(6); state = check(); -L3: -if (state == 7) { - print(7); state = check(); - label = 3; - break L3; -} +do { + if (state == 7) { + print(7); state = check(); + label = 3; + break; + } +} while(0); L5: while(1) { if (label == 3) { label = 0; diff --git a/src/relooper/test_fuzz5.txt b/src/relooper/test_fuzz5.txt index 7c795d53..9548205c 100644 --- a/src/relooper/test_fuzz5.txt +++ b/src/relooper/test_fuzz5.txt @@ -3,21 +3,22 @@ print('entry'); var label; var state; var decisions = [133, 98, 134, 143, 162, 187, 130, 87, 91, 49, 102, 47, 9, 132, 179, 176, 157, 25, 64, 161, 57, 107, 16, 167, 185, 45, 191, 180, 23, 131]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] } L1: while(1) { print(7); state = check(); - L3: - if (state % 3 == 1) { - label = 3; - } else if (state % 3 == 0) { - print(8); state = check(); - if (state % 2 == 0) { - label = 5; - break L3; + do { + if (state % 3 == 1) { + label = 3; + } else if (state % 3 == 0) { + print(8); state = check(); + if (state % 2 == 0) { + label = 5; + break; + } else { + label = 7; + break; + } } else { - label = 7; - break L3; + break L1; } - } else { - break L1; - } + } while(0); while(1) { if (label == 3) { label = 0; diff --git a/src/relooper/test_inf.txt b/src/relooper/test_inf.txt index 3e292433..379d2083 100644 --- a/src/relooper/test_inf.txt +++ b/src/relooper/test_inf.txt @@ -5,34 +5,35 @@ if (uint(i4) >= uint(i5)) { code 1 } code 3 -L5: -if (!(i2 == 0)) { - code 4 - while(1) { - code 5 - if (uint(i6) >= uint(i7)) { - code 7 - } else { - code 6 - } - code 8 - if (uint(i6) >= uint(i7)) { - code 10 - } else { - code 9 - } - code 11 - if (uint(i5) >= uint(i6)) { - code 13 - } else { - code 12 - } - code 14 - if (!(i2 != 0)) { - break L5; +L5: do { + if (!(i2 == 0)) { + code 4 + while(1) { + code 5 + if (uint(i6) >= uint(i7)) { + code 7 + } else { + code 6 + } + code 8 + if (uint(i6) >= uint(i7)) { + code 10 + } else { + code 9 + } + code 11 + if (uint(i5) >= uint(i6)) { + code 13 + } else { + code 12 + } + code 14 + if (!(i2 != 0)) { + break L5; + } } } -} +} while(0); code 15 if (uint(i4) >= uint(i5)) { code 17 @@ -40,178 +41,179 @@ if (uint(i4) >= uint(i5)) { code 16 } code 18 -L26: -if (!(i2 == 0)) { - code 19 - while(1) { - code 20 - if (uint(i5) >= uint(i6)) { - code 22 - } else { - code 21 - } - code 23 - if (uint(i5) >= uint(i6)) { - code 25 - } else { - code 24 - } - code 26 - if (uint(i5) >= uint(i6)) { - code 28 - } else { - code 27 - } - code 29 - if (uint(i5) >= uint(i6)) { - code 31 - } else { - code 30 - } - code 32 - if (uint(i5) >= uint(i6)) { - code 34 - } else { - code 33 - } - code 35 - if (uint(i5) >= uint(i6)) { - code 37 - } else { - code 36 - } - code 38 - if (uint(i5) >= uint(i6)) { - code 40 - } else { - code 39 - } - code 41 - if (uint(i5) >= uint(i6)) { - code 43 - } else { - code 42 - } - code 44 - if (uint(i5) >= uint(i6)) { - code 46 - } else { - code 45 - } - code 47 - if (uint(i5) >= uint(i6)) { - code 49 - } else { - code 48 - } - code 50 - if (uint(i5) >= uint(i6)) { - code 52 - } else { - code 51 - } - code 53 - if (uint(i5) >= uint(i6)) { - code 55 - } else { - code 54 - } - code 56 - if (uint(i5) >= uint(i6)) { - code 58 - } else { - code 57 - } - code 59 - if (uint(i5) >= uint(i6)) { - code 61 - } else { - code 60 - } - code 62 - if (uint(i5) >= uint(i6)) { - code 64 - } else { - code 63 - } - code 65 - if (uint(i5) >= uint(i6)) { - code 67 - } else { - code 66 - } - code 68 - if (uint(i5) >= uint(i6)) { - code 70 - } else { - code 69 - } - code 71 - if (uint(i5) >= uint(i6)) { - code 73 - } else { - code 72 - } - code 74 - if (uint(i5) >= uint(i6)) { - code 76 - } else { - code 75 - } - code 77 - if (uint(i5) >= uint(i6)) { - code 79 - } else { - code 78 - } - code 80 - if (uint(i5) >= uint(i6)) { - code 82 - } else { - code 81 - } - code 83 - if (uint(i5) >= uint(i6)) { - code 85 - } else { - code 84 - } - code 86 - if (uint(i5) >= uint(i6)) { - code 88 - } else { - code 87 - } - code 89 - if (uint(i5) >= uint(i6)) { - code 91 - } else { - code 90 - } - code 92 - if (uint(i5) >= uint(i6)) { - code 94 - } else { - code 93 - } - code 95 - if (uint(i5) >= uint(i6)) { - code 97 - } else { - code 96 - } - code 98 - if (uint(i5) >= uint(i6)) { - code 100 - } else { - code 99 - } - code 101 - if (!(i2 != 0)) { - break L26; +L26: do { + if (!(i2 == 0)) { + code 19 + while(1) { + code 20 + if (uint(i5) >= uint(i6)) { + code 22 + } else { + code 21 + } + code 23 + if (uint(i5) >= uint(i6)) { + code 25 + } else { + code 24 + } + code 26 + if (uint(i5) >= uint(i6)) { + code 28 + } else { + code 27 + } + code 29 + if (uint(i5) >= uint(i6)) { + code 31 + } else { + code 30 + } + code 32 + if (uint(i5) >= uint(i6)) { + code 34 + } else { + code 33 + } + code 35 + if (uint(i5) >= uint(i6)) { + code 37 + } else { + code 36 + } + code 38 + if (uint(i5) >= uint(i6)) { + code 40 + } else { + code 39 + } + code 41 + if (uint(i5) >= uint(i6)) { + code 43 + } else { + code 42 + } + code 44 + if (uint(i5) >= uint(i6)) { + code 46 + } else { + code 45 + } + code 47 + if (uint(i5) >= uint(i6)) { + code 49 + } else { + code 48 + } + code 50 + if (uint(i5) >= uint(i6)) { + code 52 + } else { + code 51 + } + code 53 + if (uint(i5) >= uint(i6)) { + code 55 + } else { + code 54 + } + code 56 + if (uint(i5) >= uint(i6)) { + code 58 + } else { + code 57 + } + code 59 + if (uint(i5) >= uint(i6)) { + code 61 + } else { + code 60 + } + code 62 + if (uint(i5) >= uint(i6)) { + code 64 + } else { + code 63 + } + code 65 + if (uint(i5) >= uint(i6)) { + code 67 + } else { + code 66 + } + code 68 + if (uint(i5) >= uint(i6)) { + code 70 + } else { + code 69 + } + code 71 + if (uint(i5) >= uint(i6)) { + code 73 + } else { + code 72 + } + code 74 + if (uint(i5) >= uint(i6)) { + code 76 + } else { + code 75 + } + code 77 + if (uint(i5) >= uint(i6)) { + code 79 + } else { + code 78 + } + code 80 + if (uint(i5) >= uint(i6)) { + code 82 + } else { + code 81 + } + code 83 + if (uint(i5) >= uint(i6)) { + code 85 + } else { + code 84 + } + code 86 + if (uint(i5) >= uint(i6)) { + code 88 + } else { + code 87 + } + code 89 + if (uint(i5) >= uint(i6)) { + code 91 + } else { + code 90 + } + code 92 + if (uint(i5) >= uint(i6)) { + code 94 + } else { + code 93 + } + code 95 + if (uint(i5) >= uint(i6)) { + code 97 + } else { + code 96 + } + code 98 + if (uint(i5) >= uint(i6)) { + code 100 + } else { + code 99 + } + code 101 + if (!(i2 != 0)) { + break L26; + } } } -} +} while(0); code 102 if (uint(i4) >= uint(i5)) { code 104 @@ -219,136 +221,137 @@ if (uint(i4) >= uint(i5)) { code 103 } code 105 -L143: -if (!(i2 == 0)) { - code 106 - while(1) { - code 107 - if (uint(i5) >= uint(i6)) { - code 109 - } else { - code 108 - } - code 110 - if (uint(i5) >= uint(i6)) { - code 112 - } else { - code 111 - } - code 113 - if (uint(i5) >= uint(i6)) { - code 115 - } else { - code 114 - } - code 116 - if (uint(i5) >= uint(i6)) { - code 118 - } else { - code 117 - } - code 119 - if (uint(i5) >= uint(i6)) { - code 121 - } else { - code 120 - } - code 122 - if (uint(i5) >= uint(i6)) { - code 124 - } else { - code 123 - } - code 125 - if (uint(i5) >= uint(i6)) { - code 127 - } else { - code 126 - } - code 128 - if (uint(i5) >= uint(i6)) { - code 130 - } else { - code 129 - } - code 131 - if (uint(i5) >= uint(i6)) { - code 133 - } else { - code 132 - } - code 134 - if (uint(i5) >= uint(i6)) { - code 136 - } else { - code 135 - } - code 137 - if (uint(i5) >= uint(i6)) { - code 139 - } else { - code 138 - } - code 140 - if (uint(i5) >= uint(i6)) { - code 142 - } else { - code 141 - } - code 143 - if (uint(i5) >= uint(i6)) { - code 145 - } else { - code 144 - } - code 146 - if (uint(i5) >= uint(i6)) { - code 148 - } else { - code 147 - } - code 149 - if (uint(i5) >= uint(i6)) { - code 151 - } else { - code 150 - } - code 152 - if (uint(i5) >= uint(i6)) { - code 154 - } else { - code 153 - } - code 155 - if (uint(i5) >= uint(i6)) { - code 157 - } else { - code 156 - } - code 158 - if (uint(i5) >= uint(i6)) { - code 160 - } else { - code 159 - } - code 161 - if (uint(i5) >= uint(i6)) { - code 163 - } else { - code 162 - } - code 164 - if (uint(i5) >= uint(i6)) { - code 166 - } else { - code 165 - } - code 167 - if (!(i2 != 0)) { - break L143; +L143: do { + if (!(i2 == 0)) { + code 106 + while(1) { + code 107 + if (uint(i5) >= uint(i6)) { + code 109 + } else { + code 108 + } + code 110 + if (uint(i5) >= uint(i6)) { + code 112 + } else { + code 111 + } + code 113 + if (uint(i5) >= uint(i6)) { + code 115 + } else { + code 114 + } + code 116 + if (uint(i5) >= uint(i6)) { + code 118 + } else { + code 117 + } + code 119 + if (uint(i5) >= uint(i6)) { + code 121 + } else { + code 120 + } + code 122 + if (uint(i5) >= uint(i6)) { + code 124 + } else { + code 123 + } + code 125 + if (uint(i5) >= uint(i6)) { + code 127 + } else { + code 126 + } + code 128 + if (uint(i5) >= uint(i6)) { + code 130 + } else { + code 129 + } + code 131 + if (uint(i5) >= uint(i6)) { + code 133 + } else { + code 132 + } + code 134 + if (uint(i5) >= uint(i6)) { + code 136 + } else { + code 135 + } + code 137 + if (uint(i5) >= uint(i6)) { + code 139 + } else { + code 138 + } + code 140 + if (uint(i5) >= uint(i6)) { + code 142 + } else { + code 141 + } + code 143 + if (uint(i5) >= uint(i6)) { + code 145 + } else { + code 144 + } + code 146 + if (uint(i5) >= uint(i6)) { + code 148 + } else { + code 147 + } + code 149 + if (uint(i5) >= uint(i6)) { + code 151 + } else { + code 150 + } + code 152 + if (uint(i5) >= uint(i6)) { + code 154 + } else { + code 153 + } + code 155 + if (uint(i5) >= uint(i6)) { + code 157 + } else { + code 156 + } + code 158 + if (uint(i5) >= uint(i6)) { + code 160 + } else { + code 159 + } + code 161 + if (uint(i5) >= uint(i6)) { + code 163 + } else { + code 162 + } + code 164 + if (uint(i5) >= uint(i6)) { + code 166 + } else { + code 165 + } + code 167 + if (!(i2 != 0)) { + break L143; + } } } -} +} while(0); code 168 if (uint(i4) >= uint(i5)) { code 170 diff --git a/src/runtime.js b/src/runtime.js index e902d27b..7f97da35 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -25,7 +25,7 @@ var RuntimeGenerator = { sep = sep || ';'; if (USE_TYPED_ARRAYS === 2) 'STACKTOP = (STACKTOP + STACKTOP|0 % ' + ({{{ QUANTUM_SIZE }}} - (isNumber(size) ? Math.min(size, {{{ QUANTUM_SIZE }}}) : {{{ QUANTUM_SIZE }}})) + ')' + sep; // The stack is always QUANTUM SIZE aligned, so we may not need to force alignment here - var ret = RuntimeGenerator.alloc(size, 'STACK', INIT_STACK, sep, USE_TYPED_ARRAYS != 2 || (isNumber(size) && parseInt(size) % {{{ QUANTUM_SIZE }}} == 0)); + var ret = RuntimeGenerator.alloc(size, 'STACK', false, sep, USE_TYPED_ARRAYS != 2 || (isNumber(size) && parseInt(size) % {{{ QUANTUM_SIZE }}} == 0)); if (ASSERTIONS) { ret += sep + 'assert(STACKTOP|0 < STACK_MAX|0)'; } @@ -45,7 +45,7 @@ var RuntimeGenerator = { if (ASSERTIONS) { ret += '; assert(STACKTOP < STACK_MAX)'; } - if (INIT_STACK) { + if (false) { ret += '; _memset(' + asmCoercion('__stackBase__', 'i32') + ', 0, ' + initial + ')'; } return ret; diff --git a/src/settings.js b/src/settings.js index 7caa5b92..1bfcf92a 100644 --- a/src/settings.js +++ b/src/settings.js @@ -37,7 +37,6 @@ var VERBOSE = 0; // When set to 1, will generate more verbose output during comp var INVOKE_RUN = 1; // Whether we will call run(). Disable if you embed the generated // code in your own, and will call run() yourself at the right time -var INIT_STACK = 0; // Whether to initialize memory on the stack to 0. var INIT_HEAP = 0; // Whether to initialize memory anywhere other than the stack to 0. var TOTAL_STACK = 5*1024*1024; // The total stack size. There is no way to enlarge the stack, so this // value must be large enough for the program's requirements. If @@ -59,6 +58,8 @@ var ALLOW_MEMORY_GROWTH = 0; // If false, we abort with an error if we try to al // Code embetterments var MICRO_OPTS = 1; // Various micro-optimizations, like nativizing variables var RELOOP = 0; // Recreate js native loops from llvm data +var RELOOPER = 'relooper.js'; // Loads the relooper from this path relative to compiler.js + var USE_TYPED_ARRAYS = 2; // Use typed arrays for the heap. See https://github.com/kripken/emscripten/wiki/Code-Generation-Modes/ // 0 means no typed arrays are used. // 1 has two heaps, IHEAP (int32) and FHEAP (double), @@ -107,10 +108,11 @@ var SKIP_STACK_IN_SMALL = 1; // When enabled, does not push/pop the stack at all // In particular, be careful with the autodebugger! (We do turn // this off automatically in that case, though.) var INLINE_LIBRARY_FUNCS = 1; // Will inline library functions that have __inline defined -var INLINING_LIMIT = 50; // A limit on inlining. If 0, we will inline normally in LLVM and +var INLINING_LIMIT = 0; // A limit on inlining. If 0, we will inline normally in LLVM and // closure. If greater than 0, we will *not* inline in LLVM, and // we will prevent inlining of functions of this size or larger - // in closure. + // in closure. 50 is a reasonable setting if you do not want + // inlining var CATCH_EXIT_CODE = 0; // If set, causes exit() to throw an exception object which is caught // in a try..catch block and results in the exit status being // returned from run(). If zero (the default), the program is just @@ -128,6 +130,11 @@ var SAFE_HEAP = 0; // Check each write to the heap, for example, this will give // that 3 is the option you usually want here. var SAFE_HEAP_LOG = 0; // Log out all SAFE_HEAP operations +var CHECK_HEAP_ALIGN = 0; // Check heap accesses for alignment, but don't do as + // near extensive (or slow) checks as SAFE_HEAP. + +var SAFE_DYNCALLS = 0; // Show stack traces on missing function pointer/virtual method calls + var ASM_HEAP_LOG = 0; // Simple heap logging, like SAFE_HEAP_LOG but cheaper, and in asm.js var CORRUPTION_CHECK = 0; // When enabled, will emit a buffer area at the beginning and @@ -161,6 +168,8 @@ var GL_DEBUG = 0; // Print out all calls into WebGL. As with LIBRARY_DEBUG, you var GL_TESTING = 0; // When enabled, sets preserveDrawingBuffer in the context, to allow tests to work (but adds overhead) var GL_MAX_TEMP_BUFFER_SIZE = 2097152; // How large GL emulation temp buffers are var GL_UNSAFE_OPTS = 1; // Enables some potentially-unsafe optimizations in GL emulation code +var FULL_ES2 = 0; // Forces support for all GLES2 features, not just the WebGL-friendly subset. +var FORCE_GL_EMULATION = 0; // Forces inclusion of full GL emulation code. var DISABLE_EXCEPTION_CATCHING = 0; // Disables generating code to actually catch exceptions. If the code you // are compiling does not actually rely on catching exceptions (but the @@ -327,6 +336,8 @@ var EXPLICIT_ZEXT = 0; // If 1, generate an explicit conversion of zext i1 to i3 var NECESSARY_BLOCKADDRS = []; // List of (function, block) for all block addresses that are taken. +var EMIT_GENERATED_FUNCTIONS = 0; // whether to emit the list of generated functions, needed for external JS optimization passes + // Compiler debugging options var DEBUG_TAGS_SHOWING = []; // Some useful items: diff --git a/src/shell.html b/src/shell.html index 3a0171de..4f39b26a 100644 --- a/src/shell.html +++ b/src/shell.html @@ -32,6 +32,7 @@ var element = document.getElementById('output'); element.value = ''; // clear browser cache return function(text) { + text = Array.prototype.slice.call(arguments).join(' '); // These replacements are necessary if you render to raw HTML //text = text.replace(/&/g, "&"); //text = text.replace(/</g, "<"); @@ -42,6 +43,7 @@ }; })(), printErr: function(text) { + text = Array.prototype.slice.call(arguments).join(' '); if (0) { // XXX disabled for safety typeof dump == 'function') { dump(text + '\n'); // fast, straight to the real console } else { |