diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 167 | ||||
-rw-r--r-- | src/compiler.js | 23 | ||||
-rw-r--r-- | src/intertyper.js | 19 | ||||
-rw-r--r-- | src/jsifier.js | 208 | ||||
-rw-r--r-- | src/library.js | 595 | ||||
-rw-r--r-- | src/library_browser.js | 78 | ||||
-rw-r--r-- | src/library_gc.js | 4 | ||||
-rw-r--r-- | src/library_gl.js | 123 | ||||
-rw-r--r-- | src/library_glut.js | 28 | ||||
-rw-r--r-- | src/library_sdl.js | 20 | ||||
-rw-r--r-- | src/long.js | 33 | ||||
-rw-r--r-- | src/modules.js | 88 | ||||
-rw-r--r-- | src/parseTools.js | 291 | ||||
-rw-r--r-- | src/postamble.js | 8 | ||||
-rw-r--r-- | src/preamble.js | 73 | ||||
-rw-r--r-- | src/relooper/Relooper.cpp | 16 | ||||
-rw-r--r-- | src/relooper/Relooper.h | 4 | ||||
-rw-r--r-- | src/relooper/emscripten/Makefile | 18 | ||||
-rw-r--r-- | src/relooper/emscripten/glue.js | 3 | ||||
-rw-r--r-- | src/relooper/test.txt | 4 | ||||
-rw-r--r-- | src/runtime.js | 86 | ||||
-rw-r--r-- | src/settings.js | 25 | ||||
-rw-r--r-- | src/utility.js | 6 |
23 files changed, 1263 insertions, 657 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index 014579f4..0ad3e017 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -19,7 +19,7 @@ function recomputeLines(func) { var BRANCH_INVOKE = set('branch', 'invoke'); var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic'); -var UNUNFOLDABLE = set('value', 'type', 'phiparam'); +var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam'); // Analyzer @@ -120,12 +120,14 @@ function analyzer(data, sidePass) { processItem: function(data) { // Legalization if (USE_TYPED_ARRAYS == 2) { - function getLegalVars(base, bits) { - assert(!isNumber(base)); + function getLegalVars(base, bits, allowLegal) { + if (allowLegal && bits <= 32) return [{ ident: base, bits: bits }]; + if (isNumber(base)) return getLegalLiterals(base, bits); var ret = new Array(Math.ceil(bits/32)); var i = 0; + if (base == 'zeroinitializer' || base == 'undef') base = 0; while (bits > 0) { - ret[i] = { ident: base + '$' + i, bits: Math.min(32, bits) }; + ret[i] = { ident: base ? base + '$' + i : '0', bits: Math.min(32, bits) }; bits -= 32; i++; } @@ -142,6 +144,23 @@ function analyzer(data, sidePass) { } return ret; } + function getLegalStructuralParts(value) { + return value.params.slice(0); + } + function getLegalParams(params, bits) { + return params.map(function(param) { + var value = param.value || param; + if (isNumber(value.ident)) { + return getLegalLiterals(value.ident, bits); + } else if (value.intertype == 'structvalue') { + return getLegalStructuralParts(value).map(function(part) { + return { ident: part.ident, bits: part.type.substr(1) }; + }); + } else { + return getLegalVars(value.ident, bits); + } + }); + } // Uses the right factor to multiply line numbers by so that they fit in between // the line[i] and the line after it function interpLines(lines, i, toAdd) { @@ -191,6 +210,7 @@ function analyzer(data, sidePass) { // Legalize lines in labels var tempId = 0; func.labels.forEach(function(label) { + if (dcheck('legalizer')) dprint('zz legalizing: \n' + dump(label.lines)); var i = 0, bits; while (i < label.lines.length) { var item = label.lines[i]; @@ -207,8 +227,12 @@ function analyzer(data, sidePass) { if (isIllegalType(item.valueType) || isIllegalType(item.type)) { isIllegal = true; } + if ((item.intertype == 'load' || item.intertype == 'store') && isStructType(item.valueType)) { + isIllegal = true; // storing an entire structure is illegal + } }); if (!isIllegal) { + //if (dcheck('legalizer')) dprint('no need to legalize \n' + dump(item)); i++; continue; } @@ -222,10 +246,10 @@ function analyzer(data, sidePass) { if (subItem != item && (!(subItem.intertype in UNUNFOLDABLE) || (subItem.intertype == 'value' && isNumber(subItem.ident) && isIllegalType(subItem.type)))) { if (item.intertype == 'phi') { - assert(subItem.intertype == 'value', 'We can only unfold illegal constants in phis'); + assert(subItem.intertype == 'value' || subItem.intertype == 'structvalue', 'We can only unfold illegal constants in phis'); // we must handle this in the phi itself, if we unfold normally it will not be pushed back with the phi } else { - var tempIdent = '$$emscripten$temp$' + (tempId++); + var tempIdent = '$$etemp$' + (tempId++); subItem.assignTo = tempIdent; unfolded.unshift(subItem); fixUnfolded(subItem); @@ -234,7 +258,7 @@ function analyzer(data, sidePass) { } else if (subItem.intertype == 'switch' && isIllegalType(subItem.type)) { subItem.switchLabels.forEach(function(switchLabel) { if (switchLabel.value[0] != '$') { - var tempIdent = '$$emscripten$temp$' + (tempId++); + var tempIdent = '$$etemp$' + (tempId++); unfolded.unshift({ assignTo: tempIdent, intertype: 'value', @@ -258,8 +282,7 @@ function analyzer(data, sidePass) { case 'store': { var toAdd = []; bits = getBits(item.valueType); - var elements; - elements = getLegalVars(item.value.ident, bits); + var elements = getLegalParams([item.value], bits)[0]; var j = 0; elements.forEach(function(element) { var tempVar = '$st$' + i + '$' + j; @@ -290,32 +313,43 @@ function analyzer(data, sidePass) { i += removeAndAdd(label.lines, i, toAdd); continue; } - // call, return: Return value is in an unlegalized array literal. Not fully optimal. + // call, return: Return the first 32 bits, the rest are in temp case 'call': { bits = getBits(value.type); var elements = getLegalVars(item.assignTo, bits); var toAdd = [value]; // legalize parameters legalizeFunctionParameters(value.params); - if (value.assignTo) { + if (value.assignTo && isIllegalType(item.type)) { // legalize return value - var j = 0; - toAdd = toAdd.concat(elements.map(function(element) { - return { + value.assignTo = elements[0].ident; + for (var j = 1; j < elements.length; j++) { + var element = elements[j]; + toAdd.push({ intertype: 'value', assignTo: element.ident, - type: 'i' + bits, - ident: value.assignTo + '[' + (j++) + ']' - }; - })); + type: element.bits, + ident: 'tempRet' + (j - 1) + }); + assert(j<10); // TODO: dynamically create more than 10 tempRet-s + } } i += removeAndAdd(label.lines, i, toAdd); continue; } + case 'landingpad': { + // not much to legalize + i++; + continue; + } case 'return': { bits = getBits(item.type); var elements = getLegalVars(item.value.ident, bits); - item.value.ident = '[' + elements.map(function(element) { return element.ident }).join(',') + ']'; + item.value.ident = '('; + for (var j = 1; j < elements.length; j++) { + item.value.ident += 'tempRet' + (j-1) + '=' + elements[j].ident + ','; + } + item.value.ident += elements[0].ident + ')'; i++; continue; } @@ -341,6 +375,21 @@ function analyzer(data, sidePass) { i += removeAndAdd(label.lines, i, toAdd); continue; } + case 'structvalue': { + bits = getBits(value.type); + var elements = getLegalVars(item.assignTo, bits); + var toAdd = []; + for (var j = 0; j < item.params.length; j++) { + toAdd[j] = { + intertype: 'value', + assignTo: elements[j].ident, + type: 'i32', + ident: item.params[j].ident + }; + } + i += removeAndAdd(label.lines, i, toAdd); + continue; + } case 'load': { bits = getBits(value.valueType); var elements = getLegalVars(item.assignTo, bits); @@ -382,13 +431,9 @@ function analyzer(data, sidePass) { var toAdd = []; var elements = getLegalVars(item.assignTo, bits); var j = 0; - var literalValues = {}; // special handling of literals - we cannot unfold them normally - value.params.map(function(param) { - if (isNumber(param.value.ident)) { - literalValues[param.value.ident] = getLegalLiterals(param.value.ident, bits); - } - }); + var values = getLegalParams(value.params, bits); elements.forEach(function(element) { + var k = 0; toAdd.push({ intertype: 'phi', assignTo: element.ident, @@ -399,7 +444,7 @@ function analyzer(data, sidePass) { label: param.label, value: { intertype: 'value', - ident: (param.value.ident in literalValues) ? literalValues[param.value.ident][j].ident : (param.value.ident + '$' + j), + ident: values[k++][j].ident, type: 'i' + element.bits, } }; @@ -414,6 +459,62 @@ function analyzer(data, sidePass) { i++; continue; // special case, handled in makeComparison } + case 'extractvalue': { // XXX we assume 32-bit alignment in extractvalue/insertvalue, + // but in theory they can run on packed structs too (see use getStructuralTypePartBits) + // potentially legalize the actual extracted value too if it is >32 bits, not just the extraction in general + var index = item.indexes[0][0].text; + var parts = getStructureTypeParts(item.type); + var indexedType = parts[index]; + var targetBits = getBits(indexedType); + var sourceBits = getBits(item.type); + var elements = getLegalVars(item.assignTo, targetBits, true); // possibly illegal + var sourceElements = getLegalVars(item.ident, sourceBits); // definitely illegal + var toAdd = []; + var sourceIndex = 0; + for (var partIndex = 0; partIndex < parts.length; partIndex++) { + if (partIndex == index) { + for (var j = 0; j < elements.length; j++) { + toAdd.push({ + intertype: 'value', + assignTo: elements[j].ident, + type: 'i' + elements[j].bits, + ident: sourceElements[sourceIndex+j].ident + }); + } + break; + } + sourceIndex += getStructuralTypePartBits(parts[partIndex])/32; + } + i += removeAndAdd(label.lines, i, toAdd); + continue; + } + case 'insertvalue': { + var index = item.indexes[0][0].text; // the modified index + var parts = getStructureTypeParts(item.type); + var indexedType = parts[index]; + var indexBits = getBits(indexedType); + var bits = getBits(item.type); // source and target + bits = getBits(value.type); + var toAdd = []; + var elements = getLegalVars(item.assignTo, bits); + var sourceElements = getLegalVars(item.ident, bits); + var indexElements = getLegalVars(item.value.ident, indexBits, true); // possibly legal + var sourceIndex = 0; + for (var partIndex = 0; partIndex < parts.length; partIndex++) { + var currNum = getStructuralTypePartBits(parts[partIndex])/32; + for (var j = 0; j < currNum; j++) { + toAdd.push({ + intertype: 'value', + assignTo: elements[sourceIndex+j].ident, + type: 'i' + elements[sourceIndex+j].bits, + ident: partIndex == index ? indexElements[j].ident : sourceElements[sourceIndex+j].ident + }); + } + sourceIndex += currNum; + } + i += removeAndAdd(label.lines, i, toAdd); + continue; + } case 'bitcast': { var inType = item.type2; var outType = item.type; @@ -477,17 +578,16 @@ function analyzer(data, sidePass) { } case 'select': { sourceBits = targetBits = getBits(value.params[1].type); - var otherElementsA = getLegalVars(value.params[1].ident, sourceBits); - var otherElementsB = getLegalVars(value.params[2].ident, sourceBits); + var params = getLegalParams(value.params.slice(1), sourceBits); processor = function(result, j) { return { intertype: 'mathop', op: 'select', - type: 'i' + otherElementsA[j].bits, + type: 'i' + params[0][j].bits, params: [ value.params[0], - { intertype: 'value', ident: otherElementsA[j].ident, type: 'i' + otherElementsA[j].bits }, - { intertype: 'value', ident: otherElementsB[j].ident, type: 'i' + otherElementsB[j].bits } + { intertype: 'value', ident: params[0][j].ident, type: 'i' + params[0][j].bits }, + { intertype: 'value', ident: params[1][j].ident, type: 'i' + params[1][j].bits } ] }; }; @@ -554,9 +654,10 @@ function analyzer(data, sidePass) { // We can't statically legalize this, do the operation at runtime TODO: optimize assert(sourceBits == 64, 'TODO: handle nonconstant shifts on != 64 bits'); value.intertype = 'value'; - value.ident = 'Runtime.bitshift64(' + sourceElements[0].ident + ', ' + + value.ident = 'Runtime' + (ASM_JS ? '_' : '.') + 'bitshift64(' + sourceElements[0].ident + ', ' + sourceElements[1].ident + ',"' + value.op + '",' + value.params[1].ident + '$0);' + - 'var ' + value.assignTo + '$0 = ' + value.assignTo + '[0], ' + value.assignTo + '$1 = ' + value.assignTo + '[1];'; + 'var ' + value.assignTo + '$0 = ' + makeGetTempDouble(0) + ', ' + value.assignTo + '$1 = ' + makeGetTempDouble(1) + ';'; + value.assignTo = null; i++; continue; } diff --git a/src/compiler.js b/src/compiler.js index 35f746a5..118ca83a 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -174,18 +174,25 @@ RUNTIME_DEBUG = LIBRARY_DEBUG || GL_DEBUG; // Settings sanity checks assert(!(USE_TYPED_ARRAYS === 2 && QUANTUM_SIZE !== 4), 'For USE_TYPED_ARRAYS == 2, must have normal QUANTUM_SIZE of 4'); +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(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB)); // shared libraries must have named globals // Output some info and warnings based on settings -if (!MICRO_OPTS || !RELOOP || ASSERTIONS || CHECK_SIGNS || CHECK_OVERFLOWS || INIT_STACK || INIT_HEAP || - !SKIP_STACK_IN_SMALL || SAFE_HEAP || PGO || PROFILE || !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 (phase == 'pre') { + if (!MICRO_OPTS || !RELOOP || ASSERTIONS || CHECK_SIGNS || CHECK_OVERFLOWS || INIT_STACK || INIT_HEAP || + !SKIP_STACK_IN_SMALL || SAFE_HEAP || PGO || PROFILE || !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) { - print('// Note: Some Emscripten settings may limit the speed of the generated code.'); + if (DOUBLE_MODE || CORRECT_SIGNS || CORRECT_OVERFLOWS || CORRECT_ROUNDINGS) { + print('// Note: Some Emscripten settings may limit the speed of the generated code.'); + } } // Load compiler code diff --git a/src/intertyper.js b/src/intertyper.js index 5af291c7..5bca9236 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -69,11 +69,13 @@ function intertyper(data, sidePass, baseLineNums) { if (mainPass && (line[0] == '%' || line[0] == '@')) { // If this isn't a type, it's a global variable, make a note of the information now, we will need it later - var testType = /[@%\w\d\.\" $-]+ = type .*/.exec(line); + var parts = line.split(' = '); + assert(parts.length >= 2); + var left = parts[0], right = parts.slice(1).join(' = '); + var testType = /^type .*/.exec(right); if (!testType) { - var global = /([@%\w\d\.\" $-]+) = .*/.exec(line); - var globalIdent = toNiceIdent(global[1]); - var testAlias = /[@%\w\d\.\" $-]+ = (hidden )?alias .*/.exec(line); + var globalIdent = toNiceIdent(left); + var testAlias = /^(hidden )?alias .*/.exec(right); Variables.globals[globalIdent] = { name: globalIdent, alias: !!testAlias, @@ -125,6 +127,7 @@ function intertyper(data, sidePass, baseLineNums) { // We need this early, to know basic function info - ident, params, varargs ident: toNiceIdent(func.ident), params: func.params, + returnType: func.returnType, hasVarArgs: func.hasVarArgs, lineNum: currFunctionLineNum, lines: currFunctionLines @@ -520,7 +523,12 @@ function intertyper(data, sidePass, baseLineNums) { if (item.tokens[3].item) { var subTokens = item.tokens[3].item.tokens; splitTokenList(subTokens).forEach(function(segment) { - ret.ctors.push(segment[1].tokens.slice(-1)[0].text); + var ctor = toNiceIdent(segment[1].tokens.slice(-1)[0].text); + ret.ctors.push(ctor); + if (ASM_JS) { // must export the global constructors from asm.js module, so mark as implemented and exported + Functions.implementedFunctions[ctor] = 'v'; + EXPORTED_FUNCTIONS[ctor] = 1; + } }); } } else if (!external) { @@ -741,6 +749,7 @@ function intertyper(data, sidePass, baseLineNums) { } var last = getTokenIndexByText(item.tokens, ';'); item.params = splitTokenList(item.tokens.slice(1, last)).map(parseLLVMSegment); + item.type = item.params[1].type; this.forwardItem(item, 'Reintegrator'); } }); diff --git a/src/jsifier.js b/src/jsifier.js index 70329b3f..e41bcf67 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -9,6 +9,8 @@ var STRUCT_LIST = set('struct', 'list'); var UNDERSCORE_OPENPARENS = set('_', '('); var RELOOP_IGNORED_LASTS = set('return', 'unreachable', 'resume'); +var addedLibraryItems = {}; + // JSifier function JSify(data, functionsOnly, givenFunctions) { var mainPass = !functionsOnly; @@ -42,7 +44,9 @@ function JSify(data, functionsOnly, givenFunctions) { var pre = processMacros(preprocess(read(preFile).replace('{{RUNTIME}}', getRuntime()))); print(pre); - Functions.implementedFunctions = set(data.unparsedFunctions.map(function(func) { return func.ident })); + data.unparsedFunctions.forEach(function(func) { + Functions.implementedFunctions[func.ident] = Functions.getSignature(func.returnType, func.params.map(function(param) { return param.type })); + }); } } @@ -258,7 +262,7 @@ function JSify(data, functionsOnly, givenFunctions) { var ret = [item]; if (item.ident == '_llvm_global_ctors') { item.JS = '\n__ATINIT__ = __ATINIT__.concat([\n' + - item.ctors.map(function(ctor) { return ' { func: ' + toNiceIdent(ctor) + ' }' }).join(',\n') + + item.ctors.map(function(ctor) { return ' { func: function() { ' + ctor + '() } }' }).join(',\n') + '\n]);\n'; return ret; } else { @@ -273,12 +277,13 @@ function JSify(data, functionsOnly, givenFunctions) { item.JS = makeGlobalDef(item.ident); } - if (item.external) { + if (item.external && !ASM_JS) { // ASM_JS considers externs to be globals // Import external global variables from the library if available. var shortident = item.ident.slice(1); if (LibraryManager.library[shortident] && LibraryManager.library[shortident].length && !BUILD_AS_SHARED_LIB) { + if (addedLibraryItems[shortident]) return ret; var val = LibraryManager.library[shortident]; var padding; if (Runtime.isNumberType(item.type) || isPointerType(item.type)) { @@ -302,7 +307,17 @@ function JSify(data, functionsOnly, givenFunctions) { index = makeGlobalUse(item.ident); // index !== null indicates we are indexing this allocator = 'ALLOC_NONE'; } - constant = parseConst(item.value, item.type, item.ident); + if (item.external) { + assert(ASM_JS); + if (Runtime.isNumberType(item.type) || isPointerType(item.type)) { + constant = zeros(Runtime.getNativeFieldSize(item.type)); + } else { + constant = makeEmptyStruct(item.type); + } + constant = JSON.stringify(constant); + } else { + constant = parseConst(item.value, item.type, item.ident); + } if (typeof constant === 'string' && constant[0] != '[') { constant = [constant]; // A single item. We may need a postset for it. } @@ -333,13 +348,17 @@ function JSify(data, functionsOnly, givenFunctions) { } js += '\n' + makePointer('[0]', null, allocator, ['void*'], index) + ';'; } - if (EXPORT_ALL || (item.ident in EXPORTED_GLOBALS)) { + if (!ASM_JS && (EXPORT_ALL || (item.ident in EXPORTED_GLOBALS))) { js += '\nModule["' + item.ident + '"] = ' + item.ident + ';'; } if (BUILD_AS_SHARED_LIB == 2 && !item.private_) { // TODO: make the assert conditional on ASSERTIONS js += 'if (globalScope) { assert(!globalScope["' + item.ident + '"]); globalScope["' + item.ident + '"] = ' + item.ident + ' }'; } + if (item.external && !NAMED_GLOBALS) { + assert(ASM_JS); + js = 'var ' + item.ident + ' = ' + js; // force an explicit naming, even if unnamed globals, for asm forwarding + } return ret.concat({ intertype: 'GlobalVariable', JS: js, @@ -377,8 +396,6 @@ function JSify(data, functionsOnly, givenFunctions) { } }); - var addedLibraryItems = {}; - // functionStub substrate.addActor('FunctionStub', { processItem: function(item) { @@ -399,13 +416,19 @@ function JSify(data, functionsOnly, givenFunctions) { var isFunction = false; if (typeof snippet === 'string') { - if (LibraryManager.library[snippet]) { + var target = LibraryManager.library[snippet]; + if (target) { // Redirection for aliases. We include the parent, and at runtime make ourselves equal to it. // This avoid having duplicate functions with identical content. redirectedIdent = snippet; deps.push(snippet); snippet = '_' + snippet; } + // In asm, we need to know about library functions. If there is a target, though, then no + // need to consider this a library function - we will call directly to it anyhow + if (ASM_JS && !redirectedIdent && (typeof target == 'function' || /Math\..+/.exec(snippet))) { + Functions.libraryFunctions[ident] = 1; + } } else if (typeof snippet === 'object') { snippet = stringifyWithFunctions(snippet); } else if (typeof snippet === 'function') { @@ -420,6 +443,7 @@ function JSify(data, functionsOnly, givenFunctions) { snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.print("[library call:' + ident + ': " + Array.prototype.slice.call(arguments).map(Runtime.prettyPrint) + "]"); '); snippet = snippet.substr(0, snippet.length-1) + '}).apply(this, arguments); if (Runtime.debug && typeof ret !== "undefined") Module.print(" [ return:" + Runtime.prettyPrint(ret)); return ret; }'; } + if (ASM_JS) Functions.libraryFunctions[ident] = 1; } var postsetId = ident + '__postset'; @@ -435,6 +459,18 @@ function JSify(data, functionsOnly, givenFunctions) { if (redirectedIdent) { deps = deps.concat(LibraryManager.library[redirectedIdent + '__deps'] || []); } + if (ASM_JS) { + // In asm, dependencies implemented in C might be needed by JS library functions. + // We don't know yet if they are implemented in C or not. To be safe, export such + // special cases. + [LIBRARY_DEPS_TO_AUTOEXPORT].forEach(function(special) { + deps.forEach(function(dep) { + if (dep == special && !EXPORTED_FUNCTIONS[dep]) { + EXPORTED_FUNCTIONS[dep] = 1; + } + }); + }); + } // $ident's are special, we do not prefix them with a '_'. if (ident[0] === '$') { ident = ident.substr(1); @@ -442,7 +478,8 @@ function JSify(data, functionsOnly, givenFunctions) { ident = '_' + ident; } var text = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : ''); - text += isFunction ? snippet : 'var ' + ident + '=' + snippet + ';'; + // redirected idents just need a var, but no value assigned to them - it would be unused + text += isFunction ? snippet : ('var ' + ident + (redirectedIdent ? '' : '=' + snippet) + ';'); if (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS)) { text += '\nModule["' + ident + '"] = ' + ident + ';'; } @@ -532,8 +569,8 @@ function JSify(data, functionsOnly, givenFunctions) { func.JS = '\n'; var paramIdents = func.params.map(function(param) { - return (param.intertype == 'varargs') ? null : toNiceIdent(param.ident); - }).filter(function(param) { return param != null; }) + return toNiceIdent(param.ident); + }); if (CLOSURE_ANNOTATIONS) { func.JS += '/**\n'; @@ -551,6 +588,34 @@ function JSify(data, functionsOnly, givenFunctions) { func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n'; + if (ASM_JS) { + // spell out argument types + func.params.forEach(function(param) { + func.JS += ' ' + param.ident + ' = ' + asmCoercion(param.ident, param.type) + ';\n'; + }); + + // spell out local variables + var vars = values(func.variables).filter(function(v) { return v.origin != 'funcparam' }); + if (vars.length > 0) { + var chunkSize = 8; + var chunks = []; + var i = 0; + while (i < vars.length) { + chunks.push(vars.slice(i, i+chunkSize)); + i += chunkSize; + } + for (i = 0; i < chunks.length; i++) { + func.JS += ' var ' + chunks[i].map(function(v) { + if (v.type != 'i64') { + return v.ident + ' = ' + asmInitializer(v.type); //, func.variables[v.ident].impl); + } else { + return v.ident + '$0 = 0, ' + v.ident + '$1 = 1'; + } + }).join(', ') + ';\n'; + } + } + } + if (PROFILE) { func.JS += ' if (PROFILING) { ' + 'var __parentProfilingNode__ = PROFILING_NODE; PROFILING_NODE = PROFILING_NODE.children["' + func.ident + '"]; ' @@ -560,6 +625,21 @@ function JSify(data, functionsOnly, givenFunctions) { + '}\n'; } + if (true) { // TODO: optimize away when not needed + if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */'; + func.JS += ' var label = 0;\n'; + } + + if (ASM_JS) { + var hasByVal = false; + func.params.forEach(function(param) { + hasByVal = hasByVal || param.byVal; + }); + if (hasByVal) { + func.JS += ' var tempParam = 0;\n'; + } + } + // Prepare the stack, if we need one. If we have other stack allocations, force the stack to be set up. func.JS += ' ' + RuntimeGenerator.stackEnter(func.initialStack, func.otherStackAllocations) + ';\n'; @@ -574,18 +654,13 @@ function JSify(data, functionsOnly, givenFunctions) { if (param.byVal) { var type = removePointing(param.type); var typeInfo = Types.types[type]; - func.JS += ' var tempParam = ' + param.ident + '; ' + param.ident + ' = ' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' + + func.JS += ' ' + (ASM_JS ? '' : 'var ') + 'tempParam = ' + param.ident + '; ' + param.ident + ' = ' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' + makeCopyValues(param.ident, 'tempParam', typeInfo.flatSize, 'null', null, param.byVal) + ';\n'; } }); if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " Module.print(INDENT + ' Entering: " + func.ident + ": ' + Array.prototype.slice.call(arguments)); INDENT += ' ';\n"; - if (true) { // TODO: optimize away when not needed - if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */'; - func.JS += ' var label;\n'; - } - // Walk function blocks and generate JS function walkBlock(block, indent) { if (!block) return ''; @@ -664,6 +739,8 @@ function JSify(data, functionsOnly, givenFunctions) { //Relooper.setDebug(1); Relooper.init(); + if (ASM_JS) Relooper.setAsmJSMode(1); + var blockMap = {}; // add blocks for (var i = 0; i < block.labels.length; i++) { @@ -716,12 +793,12 @@ function JSify(data, functionsOnly, givenFunctions) { if (PRINT_SPLIT_FILE_MARKER) { func.JS += '\n//FUNCTION_END_MARKER_OF_SOURCE_FILE_' + associatedSourceFile + '\n'; } - - if (EXPORT_ALL || (func.ident in EXPORTED_FUNCTIONS)) { + + if (!ASM_JS && (EXPORT_ALL || (func.ident in EXPORTED_FUNCTIONS))) { func.JS += 'Module["' + func.ident + '"] = ' + func.ident + ';'; } - if (INLINING_LIMIT && func.lines.length >= INLINING_LIMIT) { + if (!ASM_JS && INLINING_LIMIT && func.lines.length >= INLINING_LIMIT) { func.JS += func.ident + '["X"]=1;'; } @@ -767,8 +844,9 @@ function JSify(data, functionsOnly, givenFunctions) { var valueJS = item.JS; item.JS = ''; if (CLOSURE_ANNOTATIONS) item.JS += '/** @type {number} */ '; - item.JS += (item.overrideSSA ? '' : 'var ') + toNiceIdent(item.assignTo); - + if (!ASM_JS || item.intertype != 'alloca' || item.funcData.variables[item.assignTo].impl == VAR_EMULATED) { // asm only needs non-allocas + item.JS += ((ASM_JS || item.overrideSSA) ? '' : 'var ') + toNiceIdent(item.assignTo); + } var value = parseNumerical(valueJS); var impl = getVarImpl(item.funcData, item.assignTo); switch (impl) { @@ -798,7 +876,7 @@ function JSify(data, functionsOnly, givenFunctions) { return substrate.addActor('Intertype:' + intertype, { processItem: function(item) { item.JS = func(item); - if (!item.JS) throw "No JS generated for " + dump(item); + if (!item.JS) throw "No JS generated for " + dump((item.funcData=null,item)); if (item.assignTo) { makeAssign(item); if (!item.JS) throw "No assign JS generated for " + dump(item); @@ -814,7 +892,7 @@ function JSify(data, functionsOnly, givenFunctions) { return ';'; }); makeFuncLineActor('var', function(item) { // assigns into phis become simple vars - return 'var ' + item.ident + ';'; + return ASM_JS ? ';' : ('var ' + item.ident + ';'); }); makeFuncLineActor('store', function(item) { var value = finalizeLLVMParameter(item.value); @@ -908,7 +986,7 @@ function JSify(data, functionsOnly, givenFunctions) { var labelSets = phiSets[label]; // FIXME: Many of the |var |s here are not needed, but without them we get slowdowns with closure compiler. TODO: remove this workaround. if (labelSets.length == 1) { - return 'var ' + labelSets[0].ident + ' = ' + labelSets[0].valueJS + ';'; + return (ASM_JS ? '' : 'var ') + labelSets[0].ident + ' = ' + labelSets[0].valueJS + ';'; } // TODO: eliminate unneeded sets (to undefined etc.) var deps = {}; // for each ident we will set, which others it depends on @@ -1044,33 +1122,36 @@ function JSify(data, functionsOnly, givenFunctions) { } ret += 'return'; if (item.value) { - ret += ' ' + finalizeLLVMParameter(item.value); + ret += ' ' + asmCoercion(finalizeLLVMParameter(item.value), item.type); } return ret + ';'; }); makeFuncLineActor('resume', function(item) { // If there is no current exception, set this one as it (during a resume, the current exception can be wiped out) + var ptr = makeStructuralAccess(item.ident, 0); return (EXCEPTION_DEBUG ? 'Module.print("Resuming exception");' : '') + - 'if (' + makeGetValue('_llvm_eh_exception.buf', 0, 'void*') + ' == 0) { ' + makeSetValue('_llvm_eh_exception.buf', 0, item.ident + '.f0', 'void*') + ' } ' + - 'throw ' + item.ident + '.f0;'; + 'if (' + makeGetValue('_llvm_eh_exception.buf', 0, 'void*') + ' == 0) { ' + makeSetValue('_llvm_eh_exception.buf', 0, ptr, 'void*') + ' } ' + + 'throw ' + ptr + ';'; }); makeFuncLineActor('invoke', function(item) { // Wrapping in a function lets us easily return values if we are // in an assignment var phiSets = calcPhiSets(item); var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type); - var ret = '(function() { try { __THREW__ = false; return ' + var ret = '(function() { try { __THREW__ = 0; return ' + call_ + ' ' + '} catch(e) { ' + 'if (typeof e != "number") throw e; ' - + 'if (ABORT) throw e; __THREW__ = true; ' + + 'if (ABORT) throw e; __THREW__ = 1; ' + (EXCEPTION_DEBUG ? 'Module.print("Exception: " + e + ", currently at: " + (new Error().stack)); ' : '') + 'return null } })();'; if (item.assignTo) { ret = 'var ' + item.assignTo + ' = ' + ret; - if (isIllegalType(item.type)) { - assert(item.type == 'i64', 'Can only handle i64 invoke among illegal invokes'); - ret += 'var ' + item.assignTo + '$0 = ' + item.assignTo + '[0], ' + item.assignTo + '$1 = ' + item.assignTo + '[1];'; + if (USE_TYPED_ARRAYS == 2 && isIllegalType(item.type)) { + var bits = getBits(item.type); + for (var i = 0; i < bits/32; i++) { + ret += 'var ' + item.assignTo + '$' + i + ' = ' + (i == 0 ? item.assignTo : 'tempRet' + (i-1)) + ';' + } } item.assignTo = null; } @@ -1098,7 +1179,12 @@ function JSify(data, functionsOnly, givenFunctions) { }); makeFuncLineActor('landingpad', function(item) { var catchTypeArray = item.catchables.map(finalizeLLVMParameter).join(','); - return '___cxa_find_matching_catch('+ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') +',' + makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') + ',[' + catchTypeArray +'])'; + var ret = '___cxa_find_matching_catch('+ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') +',' + makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') + ',[' + catchTypeArray +'])'; + if (USE_TYPED_ARRAYS == 2) { + ret = makeVarDef(item.assignTo) + '$0 = ' + ret + '; ' + item.assignTo + '$1 = tempRet0;'; + item.assignTo = null; + } + return ret; }); makeFuncLineActor('load', function(item) { var value = finalizeLLVMParameter(item.pointer); @@ -1140,7 +1226,7 @@ function JSify(data, functionsOnly, givenFunctions) { makeFuncLineActor('alloca', function(item) { if (typeof item.allocatedIndex === 'number') { if (item.allocatedSize === 0) return ''; // This will not actually be shown - it's nativized - return getFastValue('__stackBase__', '+', item.allocatedIndex.toString()); + return asmCoercion(getFastValue('__stackBase__', '+', item.allocatedIndex.toString()), 'i32'); } else { return RuntimeGenerator.stackAlloc(getFastValue(calcAllocatedSize(item.allocatedType), '*', item.allocatedNum)); } @@ -1163,7 +1249,16 @@ 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!'); - var shortident = LibraryManager.getRootIdent(ident.slice(1)) || ident.slice(1); // ident may not be in library, if all there is is ident__inline + var shortident = ident.slice(1); + var callIdent = LibraryManager.getRootIdent(shortident); + if (callIdent) { + shortident = callIdent; // ident may not be in library, if all there is is ident__inline, but in this case it is + if (callIdent.indexOf('.') < 0) { + callIdent = '_' + callIdent; // Not Math.*, so add the normal prefix + } + } else { + callIdent = ident; + } var args = []; var argsTypes = []; var varargs = []; @@ -1173,6 +1268,7 @@ function JSify(data, functionsOnly, givenFunctions) { var useJSArgs = (shortident + '__jsargs') in LibraryManager.library; var hasVarArgs = isVarArgsFunctionType(type); var normalArgs = (hasVarArgs && !useJSArgs) ? countNormalArgs(type) : -1; + var byPointer = getVarData(funcData, ident); params.forEach(function(param, i) { var val = finalizeParam(param); @@ -1196,6 +1292,10 @@ function JSify(data, functionsOnly, givenFunctions) { }); args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) }); + if (ASM_JS && shortident in Functions.libraryFunctions) { + args = args.map(function(arg, i) { return asmCoercion(arg, argsTypes[i]) }); + } + varargs = varargs.map(function(vararg, i) { if (ignoreFunctionIndexizing.indexOf(i) >= 0) return vararg; return vararg === 0 ? 0 : indexizeFunctions(vararg, varargsTypes[i]) @@ -1226,6 +1326,7 @@ function JSify(data, functionsOnly, givenFunctions) { }).filter(function(arg) { return arg !== null; }).join(',') + ',tempInt)'; + varargs = asmCoercion(varargs, 'i32'); } args = args.concat(varargs); @@ -1238,11 +1339,26 @@ function JSify(data, functionsOnly, givenFunctions) { return inline.apply(null, args); // Warning: inlining does not prevent recalculation of the arguments. They should be simple identifiers } - if (getVarData(funcData, ident)) { - ident = 'FUNCTION_TABLE[' + ident + ']'; + var returnType; + if (byPointer || ASM_JS) returnType = type.split(' ')[0]; + + if (byPointer) { + var sig = Functions.getSignature(returnType, argsTypes); + 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 + } + callIdent = Functions.getTable(sig) + '[' + callIdent + ']'; } - return ident + '(' + args.join(', ') + ')'; + var ret = callIdent + '(' + args.join(', ') + ')'; + if (ASM_JS) { // TODO: do only when needed (library functions and Math.*?) XXX && shortident in Functions.libraryFunctions) { + ret = asmCoercion(ret, returnType); + if (shortident == 'abort' && funcData.returnType != 'void') { + ret += '; return 0'; // special case: abort() can happen without return, breaking the return type of asm functions. ensure a return + } + } + return ret; } makeFuncLineActor('getelementptr', function(item) { return finalizeLLVMFunctionCall(item) }); makeFuncLineActor('call', function(item) { @@ -1297,8 +1413,9 @@ function JSify(data, functionsOnly, givenFunctions) { if (phase == 'pre' && !Variables.generatedGlobalBase) { Variables.generatedGlobalBase = true; if (Variables.nextIndexedOffset > 0) { - // Variables have been calculated, print out the base generation before we print them - print('var GLOBAL_BASE = STATICTOP;\n'); + // Variables have been calculated, get to base stuff before we print them + // GLOBAL_BASE is statically known to be equal to STACK_MAX and to TOTAL_STACK, assert on this + print('assert(STATICTOP == STACK_MAX); assert(STACK_MAX == TOTAL_STACK);\n'); print('STATICTOP += ' + Variables.nextIndexedOffset + ';\n'); print('assert(STATICTOP < TOTAL_MEMORY);\n'); } @@ -1343,8 +1460,7 @@ function JSify(data, functionsOnly, givenFunctions) { } if (phase == 'pre' || phase == 'funcs') { - // serialize out the data that later passes need - PassManager.serialize(); // XXX for funcs pass, do not serialize it all. I think we just need which were indexized. + PassManager.serialize(); return; } @@ -1371,11 +1487,11 @@ function JSify(data, functionsOnly, givenFunctions) { var postParts = processMacros(preprocess(read(postFile))).split('{{GLOBAL_VARS}}'); print(postParts[0]); - print(Functions.generateIndexing()); // done last, as it may rely on aliases set in postsets + Functions.generateIndexing(); // done last, as it may rely on aliases set in postsets // Load runtime-linked libraries RUNTIME_LINKED_LIBS.forEach(function(lib) { - print('eval(Module["read"]("' + lib + '"))(FUNCTION_TABLE.length, this);'); + print('eval(Module["read"]("' + lib + '"))(' + Functions.getTable('x') + '.length, this);'); }); print(postParts[1]); @@ -1387,6 +1503,8 @@ function JSify(data, functionsOnly, givenFunctions) { return IGNORED_FUNCTIONS.indexOf(func.ident) < 0; })) + '\n'); + PassManager.serialize(); + return null; } diff --git a/src/library.js b/src/library.js index 0b59b404..be57e445 100644 --- a/src/library.js +++ b/src/library.js @@ -20,10 +20,11 @@ LibraryManager.library = { // File system base. // ========================================================================== - stdin: 0, - stdout: 0, - stderr: 0, - _impure_ptr: 0, + // keep this low in memory, because we flatten arrays with them in them + stdin: 'allocate(1, "i32*", ALLOC_STACK)', + stdout: 'allocate(1, "i32*", ALLOC_STACK)', + stderr: 'allocate(1, "i32*", ALLOC_STACK)', + _impure_ptr: 'allocate(1, "i32*", ALLOC_STACK)', $FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'], $FS__postset: '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + @@ -572,10 +573,10 @@ LibraryManager.library = { eof: false, ungotten: [] }; - // Allocate these on the stack (and never free, we are called from ATINIT or earlier), to keep their locations low - _stdin = allocate([1], 'void*', ALLOC_STACK); - _stdout = allocate([2], 'void*', ALLOC_STACK); - _stderr = allocate([3], 'void*', ALLOC_STACK); + assert(Math.max(_stdin, _stdout, _stderr) < 128); // make sure these are low, we flatten arrays with these + {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 1, 'void*') }}}; + {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 2, 'void*') }}}; + {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 3, 'void*') }}}; // Other system paths FS.createPath('/', 'dev/shm/tmp', true, true); // temp files @@ -591,9 +592,9 @@ LibraryManager.library = { FS.checkStreams(); assert(FS.streams.length < 1024); // at this early stage, we should not have a large set of file descriptors - just a few #endif - __impure_ptr = allocate([ allocate( + allocate([ allocate( {{{ Runtime.QUANTUM_SIZE === 4 ? '[0, 0, 0, 0, _stdin, 0, 0, 0, _stdout, 0, 0, 0, _stderr, 0, 0, 0]' : '[0, _stdin, _stdout, _stderr]' }}}, - 'void*', ALLOC_STATIC) ], 'void*', ALLOC_STATIC); + 'void*', ALLOC_STATIC) ], 'void*', ALLOC_NONE, {{{ makeGlobalUse('__impure_ptr') }}}); }, quit: function() { @@ -1226,6 +1227,15 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/009695399/functions/creat.html return _open(path, {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_TRUNC') }}}, allocate([mode, 0, 0, 0], 'i32', ALLOC_STACK)); }, + mkstemp__deps: ['creat'], + mkstemp: function(template) { + if (!_mkstemp.counter) _mkstemp.counter = 0; + var c = (_mkstemp.counter++).toString(); + var rep = 'XXXXXX'; + while (c.length < rep.length) c = '0' + c; + writeArrayToMemory(intArrayFromString(c), template + Pointer_stringify(template).indexOf(rep)); + return _creat(template, 0600); + }, fcntl__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__flock_struct_layout'], fcntl: function(fildes, cmd, varargs, dup2) { // int fcntl(int fildes, int cmd, ...); @@ -3068,7 +3078,7 @@ LibraryManager.library = { getchar: function() { // int getchar(void); // http://pubs.opengroup.org/onlinepubs/000095399/functions/getchar.html - return _fgetc({{{ makeGetValue('_stdin', '0', 'void*') }}}); + return _fgetc({{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}}); }, fgetpos__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], fgetpos: function(stream, pos) { @@ -3113,7 +3123,7 @@ LibraryManager.library = { gets: function(s) { // char *gets(char *s); // http://pubs.opengroup.org/onlinepubs/000095399/functions/gets.html - return _fgets(s, 1e6, {{{ makeGetValue('_stdin', '0', 'void*') }}}); + return _fgets(s, 1e6, {{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}}); }, fileno: function(stream) { // int fileno(FILE *stream); @@ -3185,7 +3195,7 @@ LibraryManager.library = { putchar: function(c) { // int putchar(int c); // http://pubs.opengroup.org/onlinepubs/000095399/functions/putchar.html - return _fputc(c, {{{ makeGetValue('_stdout', '0', 'void*') }}}); + return _fputc(c, {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}); }, putchar_unlocked: 'putchar', fputs__deps: ['write', 'strlen'], @@ -3199,7 +3209,7 @@ LibraryManager.library = { // int puts(const char *s); // http://pubs.opengroup.org/onlinepubs/000095399/functions/puts.html // NOTE: puts() always writes an extra newline. - var stdout = {{{ makeGetValue('_stdout', '0', 'void*') }}}; + var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}; var ret = _fputs(s, stdout); if (ret < 0) { return ret; @@ -3467,7 +3477,7 @@ LibraryManager.library = { scanf: function(format, varargs) { // int scanf(const char *restrict format, ... ); // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html - var stdin = {{{ makeGetValue('_stdin', '0', 'void*') }}}; + var stdin = {{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}}; return _fscanf(stdin, format, varargs); }, sscanf__deps: ['_scanString'], @@ -3506,7 +3516,7 @@ LibraryManager.library = { printf: function(format, varargs) { // int printf(const char *restrict format, ...); // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html - var stdout = {{{ makeGetValue('_stdout', '0', 'void*') }}}; + var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}; return _fprintf(stdout, format, varargs); }, sprintf__deps: ['snprintf'], @@ -3535,7 +3545,7 @@ LibraryManager.library = { _ZNSo3putEc: 'putchar', _ZNSo5flushEv__deps: ['fflush', 'stdout'], _ZNSo5flushEv: function() { - _fflush({{{ makeGetValue('_stdout', '0', 'void*') }}}); + _fflush({{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}); }, // ========================================================================== @@ -3593,6 +3603,10 @@ LibraryManager.library = { // stdlib.h // ========================================================================== + // tiny, fake malloc/free implementation. If the program actually uses malloc, + // a compiled version will be used; this will only be used if the runtime + // needs to allocate something, for which this is good enough if otherwise + // no malloc is needed. malloc: function(bytes) { /* Over-allocate to make sure it is byte-aligned by 8. * This will leak memory, but this is only the dummy @@ -3602,14 +3616,7 @@ LibraryManager.library = { ptr = Runtime.staticAlloc(bytes + 8); return (ptr+8) & 0xFFFFFFF8; }, - _Znwj: 'malloc', - _Znaj: 'malloc', - _Znam: 'malloc', - _Znwm: 'malloc', - free: function(){}, - _ZdlPv: 'free', - _ZdaPv: 'free', calloc__deps: ['malloc'], calloc: function(n, s) { @@ -3637,7 +3644,9 @@ LibraryManager.library = { }, bsearch: function(key, base, num, size, compar) { - var cmp = FUNCTION_TABLE[compar]; + var cmp = function(x, y) { + return Runtime.dynCall('iii', compar, [x, y]) + }; var left = 0; var right = num; var mid, test, addr; @@ -3831,7 +3840,7 @@ LibraryManager.library = { #if USE_TYPED_ARRAYS == 2 if (bits == 64) { - ret = [{{{ splitI64('ret') }}}]; + {{{ makeStructuralReturn(splitI64('ret')) }}}; } #endif @@ -3882,13 +3891,7 @@ LibraryManager.library = { } if (!ok) { ___setErrNo(ERRNO_CODES.EINVAL); - return [0, 0]; - } - - try { - i64Math.fromString(Pointer_stringify(start, str - start), finalBase, min, max, unsign); - } catch(e) { - ___setErrNo(ERRNO_CODES.ERANGE); // not quite correct + {{{ makeStructuralReturn(['0', '0']) }}}; } // Set end pointer. @@ -3896,9 +3899,13 @@ LibraryManager.library = { {{{ makeSetValue('endptr', 0, 'str', '*') }}} } - var ret = i64Math.result.slice(0); + try { + i64Math.fromString(Pointer_stringify(start, str - start), finalBase, min, max, unsign); + } catch(e) { + ___setErrNo(ERRNO_CODES.ERANGE); // not quite correct + } - return ret; + {{{ makeStructuralReturn([makeGetTempDouble(0), makeGetTempDouble(1)]) }}}; }, #endif strtoll__deps: ['_parseInt64'], @@ -3939,11 +3946,13 @@ LibraryManager.library = { }, qsort__deps: ['memcpy'], - qsort: function(base, num, size, comparator) { + qsort: function(base, num, size, cmp) { if (num == 0 || size == 0) return; // forward calls to the JavaScript sort method // first, sort the items logically - comparator = FUNCTION_TABLE[comparator]; + var comparator = function(x, y) { + return Runtime.dynCall('iii', cmp, [x, y]); + } var keys = []; for (var i = 0; i < num; i++) keys.push(i); keys.sort(function(a, b) { @@ -3959,9 +3968,10 @@ LibraryManager.library = { _free(temp); }, - environ: null, - __environ: null, - __buildEnvironment__deps: ['environ', '__environ'], + environ: 'allocate(1, "i32*", ALLOC_STACK)', + __environ__deps: ['environ'], + __environ: '_environ', + __buildEnvironment__deps: ['__environ'], __buildEnvironment: function(env) { // WARNING: Arbitrary limit! var MAX_ENV_VALUES = 64; @@ -3970,7 +3980,8 @@ LibraryManager.library = { // Statically allocate memory for the environment. var poolPtr; var envPtr; - if (_environ === null) { + if (!___buildEnvironment.called) { + ___buildEnvironment.called = true; // Set default values. Use string keys for Closure Compiler compatibility. ENV['USER'] = 'root'; ENV['PATH'] = '/'; @@ -3983,11 +3994,9 @@ LibraryManager.library = { envPtr = allocate(MAX_ENV_VALUES * {{{ Runtime.QUANTUM_SIZE }}}, 'i8*', ALLOC_STATIC); {{{ makeSetValue('envPtr', '0', 'poolPtr', 'i8*') }}} - _environ = allocate([envPtr], 'i8**', ALLOC_STATIC); - // Set up global variable alias. - ___environ = _environ; + {{{ makeSetValue(makeGlobalUse('_environ'), 0, 'envPtr', 'i8*') }}}; } else { - envPtr = {{{ makeGetValue('_environ', '0', 'i8**') }}}; + envPtr = {{{ makeGetValue(makeGlobalUse('_environ'), '0', 'i8**') }}}; poolPtr = {{{ makeGetValue('envPtr', '0', 'i8*') }}}; } @@ -4182,55 +4191,22 @@ LibraryManager.library = { memcpy__inline: function (dest, src, num, align) { var ret = ''; #if ASSERTIONS - ret += "assert(" + num + " % 1 === 0, 'memcpy given ' + " + num + " + ' bytes to copy. Problem with quantum=1 corrections perhaps?');"; + ret += "assert(" + num + " % 1 === 0);"; //, 'memcpy given ' + " + num + " + ' bytes to copy. Problem with quantum=1 corrections perhaps?');"; #endif ret += makeCopyValues(dest, src, num, 'null', null, align); return ret; }, - memcpy: function (dest, src, num, align) { -#if ASSERTIONS - assert(num % 1 === 0, 'memcpy given ' + num + ' bytes to copy. Problem with quantum=1 corrections perhaps?'); -#endif -#if USE_TYPED_ARRAYS == 2 - if (num >= {{{ SEEK_OPTIMAL_ALIGN_MIN }}} && src % 2 == dest % 2) { - // This is unaligned, but quite large, and potentially alignable, so work hard to get to aligned settings - if (src % 4 == dest % 4) { - var stop = src + num; - while (src % 4) { // no need to check for stop, since we have large num - HEAP8[dest++] = HEAP8[src++]; - } - var src4 = src >> 2, dest4 = dest >> 2, stop4 = stop >> 2; - while (src4 < stop4) { - HEAP32[dest4++] = HEAP32[src4++]; - } - src = src4 << 2; - dest = dest4 << 2; - while (src < stop) { - HEAP8[dest++] = HEAP8[src++]; - } - } else { - var stop = src + num; - if (src % 2) { // no need to check for stop, since we have large num - HEAP8[dest++] = HEAP8[src++]; - } - var src2 = src >> 1, dest2 = dest >> 1, stop2 = stop >> 1; - while (src2 < stop2) { - HEAP16[dest2++] = HEAP16[src2++]; - } - src = src2 << 1; - dest = dest2 << 1; - if (src < stop) { - HEAP8[dest++] = HEAP8[src++]; - } - } - } else { - while (num--) { - HEAP8[dest++] = HEAP8[src++]; - } + + memcpy: function (dest, src, num) { + // simple version, in general it should not be used - we should pull it in from libc + if (!_memcpy.shown) { + _memcpy.shown = true; + Module.printErr('warning: library.js memcpy should not be running, it is only for testing!'); } -#else - {{{ makeCopyValues('dest', 'src', 'num', 'null', null, 'align') }}}; #endif + while (num--) { + HEAP8[dest++] = HEAP8[src++]; + } }, llvm_memcpy_i32: 'memcpy', @@ -4250,7 +4226,7 @@ LibraryManager.library = { {{{ makeCopyValues('dest', 'src', 1, 'null', null, 1) }}}; } } else { - _memcpy(dest, src, num, align); + _memcpy(dest, src, num); } }, llvm_memmove_i32: 'memmove', @@ -4504,6 +4480,7 @@ LibraryManager.library = { } while (val); return 0; }, + index: 'strchr', strrchr__deps: ['strlen'], strrchr: function(ptr, chr) { @@ -4514,6 +4491,7 @@ LibraryManager.library = { } while (ptr2 >= ptr); return 0; }, + rindex: 'strrchr', strdup: function(ptr) { var len = String_len(ptr); @@ -4801,9 +4779,8 @@ LibraryManager.library = { // ========================================================================== llvm_va_start__inline: function(ptr) { - // varargs - we received a pointer to the varargs as a final 'extra' parameter - var data = 'arguments[' + Framework.currItem.funcData.ident + '.length]'; - return makeSetValue(ptr, 0, data, 'void*'); + // varargs - we received a pointer to the varargs as a final 'extra' parameter called 'varrp' + return makeSetValue(ptr, 0, 'varrp', 'void*'); }, llvm_va_end: function() {}, @@ -4832,7 +4809,7 @@ LibraryManager.library = { var retl = _llvm_bswap_i32(h)>>>0; var reth = _llvm_bswap_i32(l)>>>0; #if USE_TYPED_ARRAYS == 2 - return [retl, reth]; + {{{ makeStructuralReturn(['retl', 'reth']) }}}; #else throw 'unsupported'; #endif @@ -4852,7 +4829,7 @@ LibraryManager.library = { var ret = _llvm_ctlz_i32(h); if (ret == 32) ret += _llvm_ctlz_i32(l); #if USE_TYPED_ARRAYS == 2 - return [ret, 0]; + {{{ makeStructuralReturn(['ret', '0']) }}}; #else return ret; #endif @@ -4896,13 +4873,13 @@ LibraryManager.library = { __cxa_throw: function(ptr, type, destructor) { if (!___cxa_throw.initialized) { try { - {{{ makeSetValue('__ZTVN10__cxxabiv119__pointer_type_infoE', '0', '0', 'i32') }}}; // Workaround for libcxxabi integration bug + {{{ makeSetValue(makeGlobalUse('__ZTVN10__cxxabiv119__pointer_type_infoE'), '0', '0', 'i32') }}}; // Workaround for libcxxabi integration bug } catch(e){} try { - {{{ makeSetValue('__ZTVN10__cxxabiv117__class_type_infoE', '0', '1', 'i32') }}}; // Workaround for libcxxabi integration bug + {{{ makeSetValue(makeGlobalUse('__ZTVN10__cxxabiv117__class_type_infoE'), '0', '1', 'i32') }}}; // Workaround for libcxxabi integration bug } catch(e){} try { - {{{ makeSetValue('__ZTVN10__cxxabiv120__si_class_type_infoE', '0', '2', 'i32') }}}; // Workaround for libcxxabi integration bug + {{{ makeSetValue(makeGlobalUse('__ZTVN10__cxxabiv120__si_class_type_infoE'), '0', '2', 'i32') }}}; // Workaround for libcxxabi integration bug } catch(e){} ___cxa_throw.initialized = true; } @@ -4951,14 +4928,18 @@ LibraryManager.library = { return; } // Clear state flag. - __THREW__ = false; +#if ASM_JS + asm.setThrew(0); +#else + __THREW__ = 0; +#endif // Clear type. {{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, '0', 'void*') }}} // Call destructor if one is registered then clear it. var ptr = {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; var destructor = {{{ makeGetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, 'void*') }}}; if (destructor) { - FUNCTION_TABLE[destructor](ptr); + Runtime.dynCall('vi', destructor, [ptr]); {{{ makeSetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, '0', 'i32') }}} } // Free ptr if it isn't null. @@ -4996,20 +4977,20 @@ LibraryManager.library = { __cxa_is_number_type: function(type) { var isNumber = false; - try { if (type == __ZTIi) isNumber = true } catch(e){} - try { if (type == __ZTIj) isNumber = true } catch(e){} - try { if (type == __ZTIl) isNumber = true } catch(e){} - try { if (type == __ZTIm) isNumber = true } catch(e){} - try { if (type == __ZTIx) isNumber = true } catch(e){} - try { if (type == __ZTIy) isNumber = true } catch(e){} - try { if (type == __ZTIf) isNumber = true } catch(e){} - try { if (type == __ZTId) isNumber = true } catch(e){} - try { if (type == __ZTIe) isNumber = true } catch(e){} - try { if (type == __ZTIc) isNumber = true } catch(e){} - try { if (type == __ZTIa) isNumber = true } catch(e){} - try { if (type == __ZTIh) isNumber = true } catch(e){} - try { if (type == __ZTIs) isNumber = true } catch(e){} - try { if (type == __ZTIt) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIi') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIj') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIl') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIm') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIx') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIy') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIf') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTId') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIe') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIc') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIa') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIh') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIs') }}}) isNumber = true } catch(e){} + try { if (type == {{{ makeGlobalUse('__ZTIt') }}}) isNumber = true } catch(e){} return isNumber; }, @@ -5040,12 +5021,12 @@ LibraryManager.library = { // return the type of the catch block which should be called. for (var i = 0; i < typeArray.length; i++) { if (___cxa_does_inherit(typeArray[i], throwntype, thrown)) - return { f0:thrown, f1:typeArray[i] }; + {{{ makeStructuralReturn(['thrown', 'typeArray[i]']) }}}; } // Shouldn't happen unless we have bogus data in typeArray // or encounter a type for which emscripten doesn't have suitable // typeinfo defined. Best-efforts match just in case. - return { f0:thrown, f1 :throwntype }; + {{{ makeStructuralReturn(['thrown', 'throwntype']) }}}; }, // Recursively walks up the base types of 'possibilityType' @@ -5087,6 +5068,8 @@ LibraryManager.library = { } }, + _ZNSt9exceptionD2Ev: function(){}, // XXX a dependency of dlmalloc, but not actually needed if libcxx is not anyhow included + // RTTI hacks for exception handling, defining type_infos for common types. // The values are dummies. We simply use the addresses of these statically // allocated variables as unique identifiers. @@ -5111,73 +5094,51 @@ LibraryManager.library = { llvm_uadd_with_overflow_i8: function(x, y) { x = x & 0xff; y = y & 0xff; - return { - f0: (x+y) & 0xff, - f1: x+y > 255 - }; + {{{ makeStructuralReturn(['(x+y) & 0xff', 'x+y > 255']) }}}; }, llvm_umul_with_overflow_i8: function(x, y) { x = x & 0xff; y = y & 0xff; - return { - f0: (x*y) & 0xff, - f1: x*y > 255 - }; + {{{ makeStructuralReturn(['(x*y) & 0xff', 'x*y > 255']) }}}; }, llvm_uadd_with_overflow_i16: function(x, y) { x = x & 0xffff; y = y & 0xffff; - return { - f0: (x+y) & 0xffff, - f1: x+y > 65535 - }; + {{{ makeStructuralReturn(['(x+y) & 0xffff', 'x+y > 65535']) }}}; }, llvm_umul_with_overflow_i16: function(x, y) { x = x & 0xffff; y = y & 0xffff; - return { - f0: (x*y) & 0xffff, - f1: x*y > 65535 - }; + {{{ makeStructuralReturn(['(x*y) & 0xffff', 'x*y > 65535']) }}}; }, llvm_uadd_with_overflow_i32: function(x, y) { x = x>>>0; y = y>>>0; - return { - f0: (x+y)>>>0, - f1: x+y > 4294967295 - }; + {{{ makeStructuralReturn(['(x+y)>>>0', 'x+y > 4294967295']) }}}; }, llvm_umul_with_overflow_i32: function(x, y) { x = x>>>0; y = y>>>0; - return { - f0: (x*y)>>>0, - f1: x*y > 4294967295 - }; + {{{ makeStructuralReturn(['(x*y)>>>0', 'x*y > 4294967295']) }}}; }, llvm_uadd_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }], llvm_uadd_with_overflow_i64: function(xl, xh, yl, yh) { i64Math.add(xl, xh, yl, yh); - return { - f0: i64Math.result, - f1: 0 // XXX Need to hack support for this in long.js - }; + {{{ makeStructuralReturn(['HEAP32[tempDoublePtr>>2]', 'HEAP32[tempDoublePtr+4>>2]', '0']) }}}; + // XXX Need to hack support for second param in long.js }, llvm_umul_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }], llvm_umul_with_overflow_i64: function(xl, xh, yl, yh) { - i64Math.mul(xl, xh, yl, yh); - return { - f0: i64Math.result, - f1: 0 // XXX Need to hack support for this in long.js - }; + i64Math.multiply(xl, xh, yl, yh); + {{{ makeStructuralReturn(['HEAP32[tempDoublePtr>>2]', 'HEAP32[tempDoublePtr+4>>2]', '0']) }}}; + // XXX Need to hack support for second param in long.js }, llvm_stacksave: function() { @@ -5617,7 +5578,7 @@ LibraryManager.library = { } try { - var lib_module = eval(lib_data)(FUNCTION_TABLE.length); + var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length); } catch (e) { #if ASSERTIONS Module.printErr('Error in loading dynamic library: ' + e); @@ -5690,9 +5651,9 @@ LibraryManager.library = { } else { var result = lib.module[symbol]; if (typeof result == 'function') { - FUNCTION_TABLE.push(result); - FUNCTION_TABLE.push(0); - result = FUNCTION_TABLE.length - 2; + {{{ Functions.getTable('x') }}}.push(result); + {{{ Functions.getTable('x') }}}.push(0); + result = {{{ Functions.getTable('x') }}}.length - 2; lib.cached_functions = result; } return result; @@ -5760,11 +5721,11 @@ LibraryManager.library = { 'tm_gmtoff', 'tm_zone'], '%struct.tm'), // Statically allocated time struct. - __tm_current: 0, + __tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STACK)', // Statically allocated timezone strings. __tm_timezones: {}, // Statically allocated time strings. - __tm_formatted: 0, + __tm_formatted: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STACK)', mktime__deps: ['__tm_struct_layout', 'tzset'], mktime: function(tmPtr) { @@ -5787,7 +5748,6 @@ LibraryManager.library = { gmtime__deps: ['malloc', '__tm_struct_layout', '__tm_current', 'gmtime_r'], gmtime: function(time) { - if (!___tm_current) ___tm_current = _malloc(___tm_struct_layout.__size__); return _gmtime_r(time, ___tm_current); }, @@ -5821,8 +5781,8 @@ LibraryManager.library = { timegm__deps: ['mktime'], timegm: function(tmPtr) { _tzset(); - var offset = {{{ makeGetValue('__timezone', 0, 'i32') }}}; - var daylight = {{{ makeGetValue('__daylight', 0, 'i32') }}}; + var offset = {{{ makeGetValue(makeGlobalUse('__timezone'), 0, 'i32') }}}; + var daylight = {{{ makeGetValue(makeGlobalUse('__daylight'), 0, 'i32') }}}; daylight = (daylight == 1) ? 60 * 60 : 0; var ret = _mktime(tmPtr) + offset - daylight; return ret; @@ -5830,7 +5790,6 @@ LibraryManager.library = { localtime__deps: ['malloc', '__tm_struct_layout', '__tm_current', 'localtime_r'], localtime: function(time) { - if (!___tm_current) ___tm_current = _malloc(___tm_struct_layout.__size__); return _localtime_r(time, ___tm_current); }, @@ -5866,7 +5825,6 @@ LibraryManager.library = { asctime__deps: ['malloc', '__tm_formatted', 'asctime_r'], asctime: function(tmPtr) { - if (!___tm_formatted) ___tm_formatted = _malloc(26); return _asctime_r(tmPtr, ___tm_formatted); }, @@ -5901,29 +5859,27 @@ LibraryManager.library = { // TODO: Initialize these to defaults on startup from system settings. // Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm) - _tzname: null, - _daylight: null, - _timezone: null, + _tzname: 'allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STACK)', + _daylight: 'allocate(1, "i32*", ALLOC_STACK)', + _timezone: 'allocate(1, "i32*", ALLOC_STACK)', tzset__deps: ['_tzname', '_daylight', '_timezone'], tzset: function() { // TODO: Use (malleable) environment variables instead of system settings. - if (__tzname) return; // glibc does not need the double __ + if (_tzset.called) return; + _tzset.called = true; - __timezone = _malloc({{{ Runtime.QUANTUM_SIZE }}}); - {{{ makeSetValue('__timezone', '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}} + {{{ makeSetValue(makeGlobalUse('__timezone'), '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}} - __daylight = _malloc({{{ Runtime.QUANTUM_SIZE }}}); var winter = new Date(2000, 0, 1); var summer = new Date(2000, 6, 1); - {{{ makeSetValue('__daylight', '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}} + {{{ makeSetValue(makeGlobalUse('__daylight'), '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}} var winterName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | winter.toString().match(/\(([A-Z]+)\)/)[1]; var summerName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | summer.toString().match(/\(([A-Z]+)\)/)[1]; var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL); var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL); - __tzname = _malloc(2 * {{{ Runtime.QUANTUM_SIZE }}}); // glibc does not need the double __ - {{{ makeSetValue('__tzname', '0', 'winterNamePtr', 'i32') }}} - {{{ makeSetValue('__tzname', Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}} + {{{ makeSetValue(makeGlobalUse('__tzname'), '0', 'winterNamePtr', 'i32') }}} + {{{ makeSetValue(makeGlobalUse('__tzname'), Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}} }, stime__deps: ['$ERRNO_CODES', '__setErrNo'], @@ -6084,18 +6040,26 @@ LibraryManager.library = { }, sigemptyset: function(set) { // int sigemptyset(sigset_t *set); - // TODO: Implement for real; don't hardcode offsets. - {{{ makeSetValue('set', '0', '0', 'i32') }}} - {{{ makeSetValue('set', '4', '0', 'i32') }}} - {{{ makeSetValue('set', '8', '0', 'i32') }}} - {{{ makeSetValue('set', '12', '0', 'i32') }}} + {{{ makeSetValue('set', '0', '0', 'i32') }}}; + return 0; + }, + sigfillset: function(set) { + {{{ makeSetValue('set', '0', '-1>>>0', 'i32') }}}; + return 0; + }, + sigaddset: function(set, signum) { + {{{ makeSetValue('set', '0', makeGetValue('set', '0', 'i32') + '| (1 << (signum-1))', 'i32') }}}; + return 0; + }, + sigdelset: function(set, signum) { + {{{ makeSetValue('set', '0', makeGetValue('set', '0', 'i32') + '& (~(1 << (signum-1)))', 'i32') }}}; return 0; }, - sigfillset: 'sigemptyset', - sigdelset: 'sigemptyset', + sigismember: function(set, signum) { + return {{{ makeGetValue('set', '0', 'i32') }}} & (1 << (signum-1)); + }, sigaction: function(set) { - // int sigemptyset(sigset_t *set); - // TODO: Implement for real. + // TODO: return 0; }, sigprocmask: 'sigaction', @@ -6141,6 +6105,8 @@ LibraryManager.library = { return 0; }, + freelocale: function(locale) {}, + uselocale: function(locale) { return 0; }, @@ -6580,6 +6546,8 @@ LibraryManager.library = { pthread_mutexattr_destroy: function() {}, pthread_mutex_lock: function() {}, pthread_mutex_unlock: function() {}, + pthread_cond_init: function() {}, + pthread_cond_destroy: function() {}, pthread_cond_broadcast: function() {}, pthread_self: function() { //FIXME: assumes only a single thread @@ -6613,7 +6581,7 @@ LibraryManager.library = { pthread_once: function(ptr, func) { if (!_pthread_once.seen) _pthread_once.seen = {}; if (ptr in _pthread_once.seen) return; - FUNCTION_TABLE[func](); + Runtime.dynCall('v', func); _pthread_once.seen[ptr] = 1; }, @@ -6631,7 +6599,7 @@ LibraryManager.library = { }, pthread_cleanup_push: function(routine, arg) { - __ATEXIT__.push({ func: function() { FUNCTION_TABLE[routine](arg) } }) + __ATEXIT__.push({ func: function() { Runtime.dynCall('vi', routine, [arg]) } }) _pthread_cleanup_push.level = __ATEXIT__.length; }, @@ -6777,8 +6745,12 @@ LibraryManager.library = { $Sockets__deps: ['__setErrNo', '$ERRNO_CODES'], $Sockets: { + BACKEND_WEBSOCKETS: 0, + BACKEND_WEBRTC: 1, BUFFER_SIZE: 10*1024, // initial size MAX_BUFFER_SIZE: 10*1024*1024, // maximum size we will grow the buffer + + backend: 0, // default to websockets nextFd: 1, fds: {}, sockaddr_in_layout: Runtime.generateStructInfo([ @@ -6796,24 +6768,138 @@ LibraryManager.library = { ['i32', 'msg_controllen'], ['i32', 'msg_flags'], ]), + + backends: { + 0: { // websockets + connect: function(info) { + console.log('opening ws://' + info.host + ':' + info.port); + info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']); + info.socket.binaryType = 'arraybuffer'; + + var i32Temp = new Uint32Array(1); + var i8Temp = new Uint8Array(i32Temp.buffer); + + info.inQueue = []; + if (!info.stream) { + var partialBuffer = null; // inQueue contains full dgram messages; this buffers incomplete data. Must begin with the beginning of a message + } + + info.socket.onmessage = function(event) { + assert(typeof event.data !== 'string' && event.data.byteLength); // must get binary data! + var data = new Uint8Array(event.data); // make a typed array view on the array buffer +#if SOCKET_DEBUG + Module.print(['onmessage', data.length, '|', Array.prototype.slice.call(data)]); +#endif + if (info.stream) { + info.inQueue.push(data); + } else { + // we added headers with message sizes, read those to find discrete messages + if (partialBuffer) { + // append to the partial buffer + var newBuffer = new Uint8Array(partialBuffer.length + data.length); + newBuffer.set(partialBuffer); + newBuffer.set(data, partialBuffer.length); + // forget the partial buffer and work on data + data = newBuffer; + partialBuffer = null; + } + var currPos = 0; + while (currPos+4 < data.length) { + i8Temp.set(data.subarray(currPos, currPos+4)); + var currLen = i32Temp[0]; + assert(currLen > 0); + if (currPos + 4 + currLen > data.length) { + break; // not enough data has arrived + } + currPos += 4; +#if SOCKET_DEBUG + Module.print(['onmessage message', currLen, '|', Array.prototype.slice.call(data.subarray(currPos, currPos+currLen))]); +#endif + info.inQueue.push(data.subarray(currPos, currPos+currLen)); + currPos += currLen; + } + // If data remains, buffer it + if (currPos < data.length) { + partialBuffer = data.subarray(currPos); + } + } + } + function send(data) { + // TODO: if browser accepts views, can optimize this +#if SOCKET_DEBUG + Module.print('sender actually sending ' + Array.prototype.slice.call(data)); +#endif + // ok to use the underlying buffer, we created data and know that the buffer starts at the beginning + info.socket.send(data.buffer); + } + var outQueue = []; + var intervalling = false, interval; + function trySend() { + if (info.socket.readyState != info.socket.OPEN) { + if (!intervalling) { + intervalling = true; + console.log('waiting for socket in order to send'); + interval = setInterval(trySend, 100); + } + return; + } + for (var i = 0; i < outQueue.length; i++) { + send(outQueue[i]); + } + outQueue.length = 0; + if (intervalling) { + intervalling = false; + clearInterval(interval); + } + } + info.sender = function(data) { + if (!info.stream) { + // add a header with the message size + var header = new Uint8Array(4); + i32Temp[0] = data.length; + header.set(i8Temp); + outQueue.push(header); + } + outQueue.push(new Uint8Array(data)); + trySend(); + }; + } + }, + 1: { // webrtc + } + } + }, + + emscripten_set_network_backend__deps: ['$Sockets'], + emscripten_set_network_backend: function(backend) { + Sockets.backend = backend; }, socket__deps: ['$Sockets'], socket: function(family, type, protocol) { var fd = Sockets.nextFd++; + assert(fd < 64); // select() assumes socket fd values are in 0..63 + var stream = type == {{{ cDefine('SOCK_STREAM') }}}; + if (protocol) { + assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if stream, must be tcp + } + if (Sockets.backend == Sockets.BACKEND_WEBRTC) { + assert(!stream); // If WebRTC, we can only support datagram, not stream + } Sockets.fds[fd] = { - connected: false + connected: false, + stream: stream }; return fd; }, - connect__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs', 'gethostbyname'], + connect__deps: ['$Sockets', '_inet_ntop_raw', 'htons', 'gethostbyname'], connect: function(fd, addr, addrlen) { var info = Sockets.fds[fd]; if (!info) return -1; info.connected = true; info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32'); - info.port = _ntohs(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16')); + info.port = _htons(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16')); info.host = __inet_ntop_raw(info.addr); // Support 'fake' ips from gethostbyname var parts = info.host.split('.'); @@ -6823,69 +6909,7 @@ LibraryManager.library = { info.host = _gethostbyname.table[low + 0xff*high]; assert(info.host, 'problem translating fake ip ' + parts); } - console.log('opening ws://' + info.host + ':' + info.port); - info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']); - info.socket.binaryType = 'arraybuffer'; - info.buffer = new Uint8Array(Sockets.BUFFER_SIZE); - info.bufferWrite = info.bufferRead = 0; - info.socket.onmessage = function (event) { - assert(typeof event.data !== 'string' && event.data.byteLength); // must get binary data! - var data = new Uint8Array(event.data); // make a typed array view on the array buffer - var len = data.length; -#if SOCKET_DEBUG - Module.print(['onmessage', window.location, data, len, '|', Array.prototype.slice.call(data)]); -#endif - for (var i = 0; i < len; i++) { // TODO: typed array set, carefully with ranges, or other trick - info.buffer[info.bufferWrite++] = data[i]; - if (info.bufferWrite == info.buffer.length) info.bufferWrite = 0; - if (info.bufferWrite == info.bufferRead) { - // grow the buffer - var currLen = info.buffer.length; - if (currLen > Sockets.MAX_BUFFER_SIZE) throw 'socket buffer overflow'; - var newBuffer = new Uint8Array(currLen*2); - for (var j = 0; j < currLen; j++) { - newBuffer[j] = info.buffer[(info.bufferRead + j)%currLen]; - } - info.bufferRead = 0; - info.bufferWrite = currLen; - info.buffer = newBuffer; - } - } - } - info.sendQueue = new Uint8Array(1024); - info.sendQueueUsed = 0; - info.senderWaiting = false; - info.sender = function(data, justQueue) { - if (data) { -#if SOCKET_DEBUG - Module.print(['sender', data, data.length, '|', Array.prototype.slice.call(data)]); -#endif - if (info.sendQueueUsed + data.length >= info.sendQueue.length) { - var newQueue = new Uint8Array(2*Math.max(info.sendQueue.length, data.length)); - newQueue.set(info.sendQueue); - info.sendQueue = newQueue; - } - info.sendQueue.set(data, info.sendQueueUsed); // must copy, because while this waits memory can change! - info.sendQueueUsed += data.length; - } else { - info.senderWaiting = false; // we are a setTimeout callback - if (info.sendQueueUsed == 0) return; - } - if (info.socket.readyState != info.socket.OPEN) { - if (!info.senderWaiting) { - console.log('waiting for socket in order to send'); - setTimeout(info.sender, 100); - info.senderWaiting = true; - } - return; - } - if (justQueue) return; -#if SOCKET_DEBUG - Module.print('sender actually sending ' + info.sendQueueUsed); -#endif - info.socket.send(new Uint8Array(info.sendQueue.subarray(0, info.sendQueueUsed)).buffer); // TODO: if browser accepts views, can optimize this - info.sendQueueUsed = 0; - }; + Sockets.backends[Sockets.backend].connect(info); return 0; }, @@ -6893,25 +6917,19 @@ LibraryManager.library = { recv: function(fd, buf, len, flags) { var info = Sockets.fds[fd]; if (!info) return -1; - if (info.bufferWrite == info.bufferRead) { + if (info.inQueue.length == 0) { ___setErrNo(ERRNO_CODES.EAGAIN); // no data, and all sockets are nonblocking, so this is the right behavior return 0; // should this be -1 like the spec says? } - var ret = 0; + var buffer = info.inQueue.shift(); #if SOCKET_DEBUG - Module.print('pre-recv: ' + [len, info.bufferWrite, info.bufferRead]); + Module.print('recv: ' + [Array.prototype.slice.call(buffer)]); #endif - while (info.bufferWrite != info.bufferRead && len > 0) { - // write out a byte - {{{ makeSetValue('buf++', '0', 'info.buffer[info.bufferRead++]', 'i8') }}}; - if (info.bufferRead == info.buffer.length) info.bufferRead = 0; - len--; - ret++; + if (len < buffer.length) { + buffer = buffer.subarray(0, len); } -#if SOCKET_DEBUG - Module.print('recv: ' + [ret, len, buf] + ' : ' + Array.prototype.slice.call(HEAPU8.subarray(buf-ret, buf))); -#endif - return ret; + HEAPU8.set(buffer, buf); + return buffer.length; }, send__deps: ['$Sockets'], @@ -6934,10 +6952,15 @@ LibraryManager.library = { } var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}}; var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; - var ret = 0; #if SOCKET_DEBUG Module.print('sendmsg vecs: ' + num); #endif + var totalSize = 0; + for (var i = 0; i < num; i++) { + totalSize += {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; + } + var buffer = new Uint8Array(totalSize); + var ret = 0; for (var i = 0; i < num; i++) { var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; #if SOCKET_DEBUG @@ -6945,10 +6968,10 @@ LibraryManager.library = { #endif if (!currNum) continue; var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}}; - info.sender(HEAPU8.subarray(currBuf, currBuf+currNum), true); + buffer.set(HEAPU8.subarray(currBuf, currBuf+currNum), ret); ret += currNum; } - info.sender(null); // flush all of these together. Important they get sent as a single socket message + info.sender(buffer); // send all the iovs as a single message return ret; }, @@ -6965,12 +6988,12 @@ LibraryManager.library = { assert(name, 'sendmsg on non-connected socket, and no name/address in the message'); _connect(fd, name, {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}}); } - var bytes = info.bufferWrite - info.bufferRead; - if (bytes < 0) bytes += info.buffer.length; - if (bytes == 0) { + if (info.inQueue.length == 0) { ___setErrNo(ERRNO_CODES.EWOULDBLOCK); return -1; } + var buffer = info.inQueue.shift(); + var bytes = buffer.length; #if SOCKET_DEBUG Module.print('recvmsg bytes: ' + bytes); #endif @@ -6982,7 +7005,7 @@ LibraryManager.library = { var ret = bytes; var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}}; var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; - var data = ''; + var bufferPos = 0; for (var i = 0; i < num && bytes > 0; i++) { var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; #if SOCKET_DEBUG @@ -6995,7 +7018,14 @@ LibraryManager.library = { #if SOCKET_DEBUG Module.print('recvmsg call recv ' + currNum); #endif - assert(_recv(fd, currBuf, currNum, 0) == currNum); + HEAPU8.set(buffer.subarray(bufferPos, bufferPos + currNum), currBuf); + bufferPos += currNum; + } + if (info.stream) { + // This is tcp (reliable), so if not all was read, keep it + if (bufferPos < bytes) { + info.inQueue.unshift(buffer.subArray(bufferPos)); + } } return ret; }, @@ -7022,11 +7052,12 @@ LibraryManager.library = { ioctl: function(fd, request, varargs) { var info = Sockets.fds[fd]; if (!info) return -1; - var start = info.bufferRead; - var end = info.bufferWrite; - if (end < start) end += info.buffer.length; + var bytes = 0; + if (info.inQueue.length > 0) { + bytes = info.inQueue[0].length; + } var dest = {{{ makeGetValue('varargs', '0', 'i32') }}}; - {{{ makeSetValue('dest', '0', 'end - start', 'i32') }}}; + {{{ makeSetValue('dest', '0', 'bytes', 'i32') }}}; return 0; }, @@ -7058,6 +7089,26 @@ LibraryManager.library = { return fd; }, + select: function(nfds, readfds, writefds, exceptfds, timeout) { + // only readfds are supported, not writefds or exceptfds + // 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.inQueue.length > 0) ret++; + } + } + return ret; + }, + // ========================================================================== // emscripten.h // ========================================================================== diff --git a/src/library_browser.js b/src/library_browser.js index b14099f6..fd09f478 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -377,11 +377,11 @@ mergeInto(LibraryManager.library, { _file.substr(index +1), _url, true, true, function() { - if (onload) FUNCTION_TABLE[onload](file); + if (onload) Runtime.dynCall('vi', onload, [file]); }, function() { - if (onerror) FUNCTION_TABLE[onerror](file); - } + if (onerror) Runtime.dynCall('vi', onerror, [file]); + } ); }, @@ -396,6 +396,55 @@ mergeInto(LibraryManager.library, { }, true /* no need for run dependency, this is async but will not do any prepare etc. step */ ); }, + emscripten_async_wget2: function(url, file, request, param, arg, onload, onerror, onprogress) { + var _url = Pointer_stringify(url); + var _file = Pointer_stringify(file); + var _request = Pointer_stringify(request); + var _param = Pointer_stringify(param); + var index = _file.lastIndexOf('/'); + + var http = new XMLHttpRequest(); + http.open(_request, _url, true); + http.responseType = 'arraybuffer'; + + // LOAD + http.onload = function(e) { + if (http.status == 200) { + FS.createDataFile( _file.substr(0, index), _file.substr(index + 1), new Uint8Array(http.response), true, true); + if (onload) FUNCTION_TABLE[onload](arg, file); + } else { + if (onerror) FUNCTION_TABLE[onerror](arg, http.status); + } + }; + + // ERROR + http.onerror = function(e) { + if (onerror) FUNCTION_TABLE[onerror](arg, http.status); + }; + + // PROGRESS + http.onprogress = function(e) { + var percentComplete = (e.position / e.totalSize)*100; + if (onprogress) FUNCTION_TABLE[onprogress](arg, percentComplete); + }; + + // Useful because the browser can limit the number of redirection + try { + if (http.channel instanceof Ci.nsIHttpChannel) + http.channel.redirectionLimit = 0; + } catch (ex) { /* whatever */ } + + if (_request == "POST") { + //Send the proper header information along with the request + http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + http.setRequestHeader("Content-length", _param.length); + http.setRequestHeader("Connection", "close"); + http.send(_param); + } else { + http.send(null); + } + }, + emscripten_async_prepare: function(file, onload, onerror) { var _file = Pointer_stringify(file); var data = FS.analyzePath(_file); @@ -406,10 +455,10 @@ mergeInto(LibraryManager.library, { _file.substr(index +1), new Uint8Array(data.object.contents), true, true, function() { - if (onload) FUNCTION_TABLE[onload](file); + if (onload) Runtime.dynCall('vi', onload, [file]); }, function() { - if (onerror) FUNCTION_TABLE[onerror](file); + if (onerror) Runtime.dynCall('vi', onerror, [file]); }, true // don'tCreateFile - it's already there ); @@ -428,10 +477,10 @@ mergeInto(LibraryManager.library, { {{{ makeHEAPView('U8', 'data', 'data + size') }}}, true, true, function() { - if (onload) FUNCTION_TABLE[onload](arg, cname); + if (onload) Runtime.dynCall('vii', onload, [arg, cname]); }, function() { - if (onerror) FUNCTION_TABLE[onerror](arg); + if (onerror) Runtime.dynCall('vi', onerror, [arg]); }, true // don'tCreateFile - it's already there ); @@ -451,7 +500,6 @@ mergeInto(LibraryManager.library, { emscripten_set_main_loop: function(func, fps, simulateInfiniteLoop) { Module['noExitRuntime'] = true; - var jsFunc = FUNCTION_TABLE[func]; Browser.mainLoop.runner = function() { if (Browser.mainLoop.queue.length > 0) { var start = Date.now(); @@ -484,7 +532,7 @@ mergeInto(LibraryManager.library, { Module['preMainLoop'](); } - jsFunc(); + Runtime.dynCall('v', func); if (Module['postMainLoop']) { Module['postMainLoop'](); @@ -528,12 +576,16 @@ mergeInto(LibraryManager.library, { }, _emscripten_push_main_loop_blocker: function(func, arg, name) { - Browser.mainLoop.queue.push({ func: FUNCTION_TABLE[func], arg: arg, name: Pointer_stringify(name), counted: true }); + Browser.mainLoop.queue.push({ func: function() { + Runtime.dynCall('vi', func, [arg]); + }, name: Pointer_stringify(name), counted: true }); Browser.mainLoop.updateStatus(); }, _emscripten_push_uncounted_main_loop_blocker: function(func, arg, name) { - Browser.mainLoop.queue.push({ func: FUNCTION_TABLE[func], arg: arg, name: Pointer_stringify(name), counted: false }); + Browser.mainLoop.queue.push({ func: function() { + Runtime.dynCall('vi', func, [arg]); + }, name: Pointer_stringify(name), counted: false }); Browser.mainLoop.updateStatus(); }, @@ -547,7 +599,7 @@ mergeInto(LibraryManager.library, { Module['noExitRuntime'] = true; function wrapper() { - Runtime.getFuncWrapper(func)(arg); + Runtime.getFuncWrapper(func, 'vi')(arg); } if (millis >= 0) { @@ -631,7 +683,7 @@ mergeInto(LibraryManager.library, { if (callback) { callbackId = info.callbacks.length; info.callbacks.push({ - func: Runtime.getFuncWrapper(callback), + func: Runtime.getFuncWrapper(callback, 'viii'), arg: arg }); info.awaited++; diff --git a/src/library_gc.js b/src/library_gc.js index a06e2f01..fe4cbf63 100644 --- a/src/library_gc.js +++ b/src/library_gc.js @@ -1,5 +1,7 @@ if (GC_SUPPORT) { + EXPORTED_FUNCTIONS['_calloc'] = 1; + var LibraryGC = { $GC__deps: ['sbrk'], $GC: { @@ -50,7 +52,7 @@ if (GC_SUPPORT) { free: function(ptr) { // does not check if anything refers to it, this is a forced free var finalizer = GC.finalizers[ptr]; if (finalizer) { - Runtime.getFuncWrapper(finalizer)(ptr, GC.finalizerArgs[ptr]); + Runtime.getFuncWrapper(finalizer, 'vii')(ptr, GC.finalizerArgs[ptr]); GC.finalizers[ptr] = 0; } _free(ptr); diff --git a/src/library_gl.js b/src/library_gl.js index 0f28a6a0..267a6185 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -1275,11 +1275,12 @@ var LibraryGL = { getProcAddress: function(name) { name = name.replace('EXT', '').replace('ARB', ''); // Do the translation carefully because of closure + var sig = '', func; switch (name) { - case 'glCreateShaderObject': case 'glCreateShader': func = _glCreateShader; break; - case 'glCreateProgramObject': case 'glCreateProgram': func = _glCreateProgram; break; - case 'glAttachObject': case 'glAttachShader': func = _glAttachShader; break; - case 'glUseProgramObject': case 'glUseProgram': func = _glUseProgram; break; + case 'glCreateShaderObject': case 'glCreateShader': func = _glCreateShader; sig = 'ii'; break; + case 'glCreateProgramObject': case 'glCreateProgram': func = _glCreateProgram; sig = 'ii'; break; + case 'glAttachObject': case 'glAttachShader': func = _glAttachShader; sig = 'vi'; break; + case 'glUseProgramObject': case 'glUseProgram': func = _glUseProgram; sig = 'vi'; break; case 'glDeleteObject': func = function(id) { if (GL.programs[id]) { _glDeleteProgram(id); @@ -1288,7 +1289,7 @@ var LibraryGL = { } else { Module.printErr('WARNING: deleteObject received invalid id: ' + id); } - }; break; + }; sig = 'vi'; break; case 'glGetObjectParameteriv': func = function(id, type, result) { if (GL.programs[id]) { if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB @@ -1305,7 +1306,7 @@ var LibraryGL = { } else { Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id); } - }; break; + }; sig = 'viii'; break; case 'glGetInfoLog': func = function(id, maxLength, length, infoLog) { if (GL.programs[id]) { _glGetProgramInfoLog(id, maxLength, length, infoLog); @@ -1314,67 +1315,68 @@ var LibraryGL = { } else { Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id); } - }; break; + }; sig = 'viiii'; break; case 'glBindProgram': func = function(type, id) { assert(id == 0); - }; break; - case 'glDrawRangeElements': func = _glDrawRangeElements; break; - case 'glShaderSource': func = _glShaderSource; break; - case 'glCompileShader': func = _glCompileShader; break; - case 'glLinkProgram': func = _glLinkProgram; break; - case 'glGetUniformLocation': func = _glGetUniformLocation; break; - case 'glUniform1f': func = _glUniform1f; break; - case 'glUniform2f': func = _glUniform2f; break; - case 'glUniform3f': func = _glUniform3f; break; - case 'glUniform4f': func = _glUniform4f; break; - case 'glUniform1fv': func = _glUniform1fv; break; - case 'glUniform2fv': func = _glUniform2fv; break; - case 'glUniform3fv': func = _glUniform3fv; break; - case 'glUniform4fv': func = _glUniform4fv; break; - case 'glUniform1i': func = _glUniform1i; break; - case 'glUniform2i': func = _glUniform2i; break; - case 'glUniform3i': func = _glUniform3i; break; - case 'glUniform4i': func = _glUniform4i; break; - case 'glUniform1iv': func = _glUniform1iv; break; - case 'glUniform2iv': func = _glUniform2iv; break; - case 'glUniform3iv': func = _glUniform3iv; break; - case 'glUniform4iv': func = _glUniform4iv; break; - case 'glBindAttribLocation': func = _glBindAttribLocation; break; - case 'glGetActiveUniform': func = _glGetActiveUniform; break; - case 'glGenBuffers': func = _glGenBuffers; break; - case 'glBindBuffer': func = _glBindBuffer; break; - case 'glBufferData': func = _glBufferData; break; - case 'glBufferSubData': func = _glBufferSubData; break; - case 'glDeleteBuffers': func = _glDeleteBuffers; break; - case 'glActiveTexture': func = _glActiveTexture; break; - case 'glClientActiveTexture': func = _glClientActiveTexture; break; - case 'glGetProgramiv': func = _glGetProgramiv; break; - case 'glEnableVertexAttribArray': func = _glEnableVertexAttribArray; break; - case 'glDisableVertexAttribArray': func = _glDisableVertexAttribArray; break; - case 'glVertexAttribPointer': func = _glVertexAttribPointer; break; - case 'glBindRenderbuffer': func = _glBindRenderbuffer; break; - case 'glDeleteRenderbuffers': func = _glDeleteRenderbuffers; break; - case 'glGenRenderbuffers': func = _glGenRenderbuffers; break; - case 'glCompressedTexImage2D': func = _glCompressedTexImage2D; break; - case 'glCompressedTexSubImage2D': func = _glCompressedTexSubImage2D; break; - case 'glBindFramebuffer': func = _glBindFramebuffer; break; - case 'glGenFramebuffers': func = _glGenFramebuffers; break; - case 'glDeleteFramebuffers': func = _glDeleteFramebuffers; break; - case 'glFramebufferRenderbuffer': func = _glFramebufferRenderbuffer; break; - case 'glFramebufferTexture2D': func = _glFramebufferTexture2D; break; - case 'glGetFramebufferAttachmentParameteriv': func = _glGetFramebufferAttachmentParameteriv; break; - case 'glIsFramebuffer': func = _glIsFramebuffer; break; - case 'glCheckFramebufferStatus': func = _glCheckFramebufferStatus; break; - case 'glRenderbufferStorage': func = _glRenderbufferStorage; break; + }; sig = 'vii'; break; + case 'glDrawRangeElements': func = _glDrawRangeElements; sig = 'viiiiii'; break; + case 'glShaderSource': func = _glShaderSource; sig = 'viiii'; break; + case 'glCompileShader': func = _glCompileShader; sig = 'vi'; break; + case 'glLinkProgram': func = _glLinkProgram; sig = 'vi'; break; + case 'glGetUniformLocation': func = _glGetUniformLocation; sig = 'iii'; break; + case 'glUniform1f': func = _glUniform1f; sig = 'vid'; break; + case 'glUniform2f': func = _glUniform2f; sig = 'vidd'; break; + case 'glUniform3f': func = _glUniform3f; sig = 'viddd'; break; + case 'glUniform4f': func = _glUniform4f; sig = 'vidddd'; break; + case 'glUniform1fv': func = _glUniform1fv; sig = 'viii'; break; + case 'glUniform2fv': func = _glUniform2fv; sig = 'viii'; break; + case 'glUniform3fv': func = _glUniform3fv; sig = 'viii'; break; + case 'glUniform4fv': func = _glUniform4fv; sig = 'viii'; break; + case 'glUniform1i': func = _glUniform1i; sig = 'vii'; break; + case 'glUniform2i': func = _glUniform2i; sig = 'viii'; break; + case 'glUniform3i': func = _glUniform3i; sig = 'viiii'; break; + case 'glUniform4i': func = _glUniform4i; sig = 'viiii'; break; + case 'glUniform1iv': func = _glUniform1iv; sig = 'viii'; break; + case 'glUniform2iv': func = _glUniform2iv; sig = 'viii'; break; + case 'glUniform3iv': func = _glUniform3iv; sig = 'viii'; break; + case 'glUniform4iv': func = _glUniform4iv; sig = 'viii'; break; + case 'glBindAttribLocation': func = _glBindAttribLocation; sig = 'viii'; break; + case 'glGetActiveUniform': func = _glGetActiveUniform; sig = 'viiiiiii'; break; + case 'glGenBuffers': func = _glGenBuffers; sig = 'iii'; break; + case 'glBindBuffer': func = _glBindBuffer; sig = 'vii'; break; + case 'glBufferData': func = _glBufferData; sig = 'viiii'; break; + case 'glBufferSubData': func = _glBufferSubData; sig = 'viiii'; break; + case 'glDeleteBuffers': func = _glDeleteBuffers; sig = 'vii'; break; + case 'glActiveTexture': func = _glActiveTexture; sig = 'vi'; break; + case 'glClientActiveTexture': func = _glClientActiveTexture; sig = 'vi'; break; + case 'glGetProgramiv': func = _glGetProgramiv; sig = 'viii'; break; + case 'glEnableVertexAttribArray': func = _glEnableVertexAttribArray; sig = 'vi'; break; + case 'glDisableVertexAttribArray': func = _glDisableVertexAttribArray; sig = 'vi'; break; + case 'glVertexAttribPointer': func = _glVertexAttribPointer; sig = 'viiiiii'; break; + case 'glBindRenderbuffer': func = _glBindRenderbuffer; sig = 'vii'; break; + case 'glDeleteRenderbuffers': func = _glDeleteRenderbuffers; sig = 'vii'; break; + case 'glGenRenderbuffers': func = _glGenRenderbuffers; sig = 'vii'; break; + case 'glCompressedTexImage2D': func = _glCompressedTexImage2D; sig = 'viiiiiiii'; break; + case 'glCompressedTexSubImage2D': func = _glCompressedTexSubImage2D; sig = 'viiiiiiiii'; break; + case 'glBindFramebuffer': func = _glBindFramebuffer; sig = 'vii'; break; + case 'glGenFramebuffers': func = _glGenFramebuffers; sig = 'vii'; break; + case 'glDeleteFramebuffers': func = _glDeleteFramebuffers; sig = 'vii'; break; + case 'glFramebufferRenderbuffer': func = _glFramebufferRenderbuffer; sig = 'viiii'; break; + case 'glFramebufferTexture2D': func = _glFramebufferTexture2D; sig = 'viiiii'; break; + case 'glGetFramebufferAttachmentParameteriv': func = _glGetFramebufferAttachmentParameteriv; sig = 'viiii'; break; + case 'glIsFramebuffer': func = _glIsFramebuffer; sig = 'ii'; break; + case 'glCheckFramebufferStatus': func = _glCheckFramebufferStatus; sig = 'ii'; break; + case 'glRenderbufferStorage': func = _glRenderbufferStorage; sig = 'viiii'; break; default: { Module.printErr('WARNING: getProcAddress failed for ' + name); func = function() { Module.printErr('WARNING: empty replacement for ' + name + ' called, no-op'); return 0; }; + sig = 'v'; } } - return Runtime.addFunction(func); + return Runtime.addFunction(func, sig); } }, @@ -2079,8 +2081,9 @@ var LibraryGL = { glVertex3fv: function(p) { _glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}}); }, + glVertex2fv__deps: ['glVertex3f'], glVertex2fv: function(p) { - _glVertex2f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}); + _glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, 0); }, glTexCoord2i: function(u, v) { @@ -2093,9 +2096,9 @@ var LibraryGL = { }, glTexCoord2f: 'glTexCoord2i', - glTexCoord2fv__deps: ['glTexCoord2f'], + glTexCoord2fv__deps: ['glTexCoord2i'], glTexCoord2fv: function(v) { - _glTexCoord2f({{{ makeGetValue('v', '0', 'float') }}}, {{{ makeGetValue('v', '4', 'float') }}}); + _glTexCoord2i({{{ makeGetValue('v', '0', 'float') }}}, {{{ makeGetValue('v', '4', 'float') }}}); }, glColor4f: function(r, g, b, a) { diff --git a/src/library_glut.js b/src/library_glut.js index 6069b484..2e662698 100644 --- a/src/library_glut.js +++ b/src/library_glut.js @@ -54,11 +54,11 @@ var LibraryGLUT = { if (GLUT.buttons == 0 && event.target == Module["canvas"] && GLUT.passiveMotionFunc) { event.preventDefault(); GLUT.saveModifiers(event); - FUNCTION_TABLE[GLUT.passiveMotionFunc](GLUT.lastX, GLUT.lastY); + Runtime.dynCall('vii', GLUT.passiveMotionFunc, [GLUT.lastX, GLUT.lastY]); } else if (GLUT.buttons != 0 && GLUT.motionFunc) { event.preventDefault(); GLUT.saveModifiers(event); - FUNCTION_TABLE[GLUT.motionFunc](GLUT.lastX, GLUT.lastY); + Runtime.dynCall('vii', GLUT.motionFunc, [GLUT.lastX, GLUT.lastY]); } }, @@ -159,7 +159,7 @@ var LibraryGLUT = { if( GLUT.specialFunc ) { event.preventDefault(); GLUT.saveModifiers(event); - FUNCTION_TABLE[GLUT.specialFunc](key, GLUT.lastX, GLUT.lastY); + Runtime.dynCall('viii', GLUT.specialFunc, [key, GLUT.lastX, GLUT.lastY]); } } else @@ -168,7 +168,7 @@ var LibraryGLUT = { if( key !== null && GLUT.keyboardFunc ) { event.preventDefault(); GLUT.saveModifiers(event); - FUNCTION_TABLE[GLUT.keyboardFunc](key, GLUT.lastX, GLUT.lastY); + Runtime.dynCall('viii', GLUT.keyboardFunc, [key, GLUT.lastX, GLUT.lastY]); } } } @@ -181,7 +181,7 @@ var LibraryGLUT = { if(GLUT.specialUpFunc) { event.preventDefault (); GLUT.saveModifiers(event); - FUNCTION_TABLE[GLUT.specialUpFunc](key, GLUT.lastX, GLUT.lastY); + Runtime.dynCall('viii', GLUT.specialUpFunc, [key, GLUT.lastX, GLUT.lastY]); } } else @@ -190,7 +190,7 @@ var LibraryGLUT = { if( key !== null && GLUT.keyboardUpFunc ) { event.preventDefault (); GLUT.saveModifiers(event); - FUNCTION_TABLE[GLUT.keyboardUpFunc](key, GLUT.lastX, GLUT.lastY); + Runtime.dynCall('viii', GLUT.keyboardUpFunc, [key, GLUT.lastX, GLUT.lastY]); } } } @@ -206,7 +206,7 @@ var LibraryGLUT = { } catch (e) {} event.preventDefault(); GLUT.saveModifiers(event); - FUNCTION_TABLE[GLUT.mouseFunc](event['button'], 0/*GLUT_DOWN*/, GLUT.lastX, GLUT.lastY); + Runtime.dynCall('viiii', GLUT.mouseFunc, [event['button'], 0/*GLUT_DOWN*/, GLUT.lastX, GLUT.lastY]); } }, @@ -217,7 +217,7 @@ var LibraryGLUT = { if(GLUT.mouseFunc) { event.preventDefault(); GLUT.saveModifiers(event); - FUNCTION_TABLE[GLUT.mouseFunc](event['button'], 1/*GLUT_UP*/, GLUT.lastX, GLUT.lastY); + Runtime.dynCall('viiii', GLUT.mouseFunc, [event['button'], 1/*GLUT_UP*/, GLUT.lastX, GLUT.lastY]); } }, @@ -241,7 +241,7 @@ var LibraryGLUT = { /* Can't call _glutReshapeWindow as that requests cancelling fullscreen. */ if (GLUT.reshapeFunc) { // console.log("GLUT.reshapeFunc (from FS): " + width + ", " + height); - FUNCTION_TABLE[GLUT.reshapeFunc](width, height); + Runtime.dynCall('vii', GLUT.reshapeFunc, [width, height]); } _glutPostRedisplay(); }, @@ -326,7 +326,7 @@ var LibraryGLUT = { glutIdleFunc: function(func) { var callback = function() { if (GLUT.idleFunc) { - FUNCTION_TABLE[GLUT.idleFunc](); + Runtime.dynCall('v', GLUT.idleFunc); window.setTimeout(callback, 0); } } @@ -336,7 +336,7 @@ var LibraryGLUT = { }, glutTimerFunc: function(msec, func, value) { - window.setTimeout(function() { FUNCTION_TABLE[func](value); }, msec); + window.setTimeout(function() { Runtime.dynCall('vi', func, [value]); }, msec); }, glutDisplayFunc: function(func) { @@ -388,7 +388,7 @@ var LibraryGLUT = { Browser.setCanvasSize(width, height); if (GLUT.reshapeFunc) { // console.log("GLUT.reshapeFunc: " + width + ", " + height); - FUNCTION_TABLE[GLUT.reshapeFunc](width, height); + Runtime.dynCall('vii', GLUT.reshapeFunc, [width, height]); } _glutPostRedisplay(); }, @@ -417,7 +417,9 @@ var LibraryGLUT = { glutPostRedisplay: function() { if (GLUT.displayFunc) { - Browser.requestAnimationFrame(FUNCTION_TABLE[GLUT.displayFunc]); + Browser.requestAnimationFrame(function() { + Runtime.dynCall('v', GLUT.displayFunc); + }); } }, diff --git a/src/library_sdl.js b/src/library_sdl.js index 1cd99534..b2ca338b 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -707,7 +707,9 @@ var LibrarySDL = { SDL_Quit: function() { for (var i = 0; i < SDL.numChannels; ++i) { - SDL.channels[i].audio.pause(); + if (SDL.channels[i].audio) { + SDL.channels[i].audio.pause(); + } } if (SDL.music.audio) { SDL.music.audio.pause(); @@ -1116,7 +1118,7 @@ var LibrarySDL = { SDL.audio.bufferSize = totalSamples*2; // hardcoded 16-bit audio SDL.audio.buffer = _malloc(SDL.audio.bufferSize); SDL.audio.caller = function() { - FUNCTION_TABLE[SDL.audio.callback](SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize); + Runtime.dynCall('viii', SDL.audio.callback, [SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize]); SDL.audio.pushAudio(SDL.audio.buffer, SDL.audio.bufferSize); }; // Mozilla Audio API. TODO: Other audio APIs @@ -1160,6 +1162,8 @@ var LibrarySDL = { SDL_CreateMutex: function() { return 0 }, SDL_LockMutex: function() {}, SDL_UnlockMutex: function() {}, + SDL_mutexP: function() { return 0 }, + SDL_mutexV: function() { return 0 }, SDL_DestroyMutex: function() {}, SDL_CreateCond: function() { return 0 }, @@ -1278,7 +1282,7 @@ var LibrarySDL = { channelInfo.audio = audio = audio.cloneNode(true); if (SDL.channelFinished) { audio['onended'] = function() { // TODO: cache these - Runtime.getFuncWrapper(SDL.channelFinished)(channel); + Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel); } } // Either play the element, or load the dynamic data into it @@ -1349,7 +1353,7 @@ var LibrarySDL = { info.audio = null; } if (SDL.channelFinished) { - Runtime.getFuncWrapper(SDL.channelFinished)(channel); + Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel); } return 0; }, @@ -1408,7 +1412,7 @@ var LibrarySDL = { audio.pause(); SDL.music.audio = null; if (SDL.hookMusicFinished) { - FUNCTION_TABLE[SDL.hookMusicFinished](); + Runtime.dynCall('v', SDL.hookMusicFinished); } return 0; }, @@ -1439,6 +1443,10 @@ var LibrarySDL = { return id; }, + TTF_CloseFont: function(font) { + SDL.fonts[font] = null; + }, + TTF_RenderText_Solid: function(font, text, color) { // XXX the font and color are ignored text = Pointer_stringify(text) || ' '; // if given an empty string, still return a valid surface @@ -1557,7 +1565,7 @@ var LibrarySDL = { SDL_AddTimer: function(interval, callback, param) { return window.setTimeout(function() { - FUNCTION_TABLE[callback](interval, param); + Runtime.dynCall('ii', callback, [interval, param]); }, interval); }, SDL_RemoveTimer: function(id) { diff --git a/src/long.js b/src/long.js index fc67733f..c3b0e605 100644 --- a/src/long.js +++ b/src/long.js @@ -1530,27 +1530,26 @@ var i64Math = (function() { // Emscripten wrapper // Emscripten wrapper var Wrapper = { - result: [0, 0], // return result stored here add: function(xl, xh, yl, yh) { var x = new goog.math.Long(xl, xh); var y = new goog.math.Long(yl, yh); var ret = x.add(y); - Wrapper.result[0] = ret.low_; - Wrapper.result[1] = ret.high_; + HEAP32[tempDoublePtr>>2] = ret.low_; + HEAP32[tempDoublePtr+4>>2] = ret.high_; }, subtract: function(xl, xh, yl, yh) { var x = new goog.math.Long(xl, xh); var y = new goog.math.Long(yl, yh); var ret = x.subtract(y); - Wrapper.result[0] = ret.low_; - Wrapper.result[1] = ret.high_; + HEAP32[tempDoublePtr>>2] = ret.low_; + HEAP32[tempDoublePtr+4>>2] = ret.high_; }, multiply: function(xl, xh, yl, yh) { var x = new goog.math.Long(xl, xh); var y = new goog.math.Long(yl, yh); var ret = x.multiply(y); - Wrapper.result[0] = ret.low_; - Wrapper.result[1] = ret.high_; + HEAP32[tempDoublePtr>>2] = ret.low_; + HEAP32[tempDoublePtr+4>>2] = ret.high_; }, ensureTemps: function() { if (Wrapper.ensuredTemps) return; @@ -1579,8 +1578,8 @@ var i64Math = (function() { // Emscripten wrapper var x = new goog.math.Long(xl, xh); var y = new goog.math.Long(yl, yh); var ret = x.div(y); - Wrapper.result[0] = ret.low_; - Wrapper.result[1] = ret.high_; + HEAP32[tempDoublePtr>>2] = ret.low_; + HEAP32[tempDoublePtr+4>>2] = ret.high_; } else { // slow precise bignum division var x = Wrapper.lh2bignum(xl >>> 0, xh >>> 0); @@ -1590,8 +1589,8 @@ var i64Math = (function() { // Emscripten wrapper var l = new BigInteger(); var h = new BigInteger(); z.divRemTo(Wrapper.two32, h, l); - Wrapper.result[0] = parseInt(l.toString()) | 0; - Wrapper.result[1] = parseInt(h.toString()) | 0; + HEAP32[tempDoublePtr>>2] = parseInt(l.toString()) | 0; + HEAP32[tempDoublePtr+4>>2] = parseInt(h.toString()) | 0; } }, modulo: function(xl, xh, yl, yh, unsigned) { @@ -1600,8 +1599,8 @@ var i64Math = (function() { // Emscripten wrapper var x = new goog.math.Long(xl, xh); var y = new goog.math.Long(yl, yh); var ret = x.modulo(y); - Wrapper.result[0] = ret.low_; - Wrapper.result[1] = ret.high_; + HEAP32[tempDoublePtr>>2] = ret.low_; + HEAP32[tempDoublePtr+4>>2] = ret.high_; } else { // slow precise bignum division var x = Wrapper.lh2bignum(xl >>> 0, xh >>> 0); @@ -1611,8 +1610,8 @@ var i64Math = (function() { // Emscripten wrapper var l = new BigInteger(); var h = new BigInteger(); z.divRemTo(Wrapper.two32, h, l); - Wrapper.result[0] = parseInt(l.toString()) | 0; - Wrapper.result[1] = parseInt(h.toString()) | 0; + HEAP32[tempDoublePtr>>2] = parseInt(l.toString()) | 0; + HEAP32[tempDoublePtr+4>>2] = parseInt(h.toString()) | 0; } }, stringify: function(l, h, unsigned) { @@ -1650,8 +1649,8 @@ var i64Math = (function() { // Emscripten wrapper error = true; } var ret = goog.math.Long.fromString(bignum.toString()); // min-max checks should have clamped this to a range goog.math.Long can handle well - Wrapper.result[0] = ret.low_; - Wrapper.result[1] = ret.high_; + HEAP32[tempDoublePtr>>2] = ret.low_; + HEAP32[tempDoublePtr+4>>2] = ret.high_; if (error) throw 'range error'; } }; diff --git a/src/modules.js b/src/modules.js index 64638be1..b5a30866 100644 --- a/src/modules.js +++ b/src/modules.js @@ -216,17 +216,29 @@ var Types = { }; var Functions = { - // All functions that will be implemented in this file + // All functions that will be implemented in this file. Maps id to signature implementedFunctions: {}, + libraryFunctions: {}, // functions added from the library + unimplementedFunctions: {}, // library etc. functions that we need to index, maps id to signature indexedFunctions: {}, nextIndex: 2, // Start at a non-0 (even, see below) value blockAddresses: {}, // maps functions to a map of block labels to label ids + getSignature: function(returnType, argTypes) { + var sig = returnType == 'void' ? 'v' : (isIntImplemented(returnType) ? 'i' : 'f'); + for (var i = 0; i < argTypes.length; i++) { + var type = argTypes[i]; + if (!type) break; // varargs + sig += isIntImplemented(type) ? (getBits(type) == 64 ? 'ii' : 'i') : 'f'; // legalized i64s will be i32s + } + return sig; + }, + // Mark a function as needing indexing. Python will coordinate them all getIndex: function(ident) { - if (phase != 'post') { + if (phase != 'post' && singlePhase) { this.indexedFunctions[ident] = 0; // tell python we need this indexized return '{{{ FI_' + ident + ' }}}'; // something python will replace later } else { @@ -240,27 +252,60 @@ var Functions = { } }, + getTable: function(sig) { + return ASM_JS ? 'FUNCTION_TABLE_' + sig : 'FUNCTION_TABLE'; + }, + // Generate code for function indexing generateIndexing: function() { - var vals = zeros(this.nextIndex); + var total = this.nextIndex; + if (ASM_JS) total = ceilPowerOfTwo(total); // must be power of 2 for mask + function emptyTable(sig) { + return zeros(total); + } + var tables = {}; + if (ASM_JS) { + ['v', 'vi', 'ii', 'iii'].forEach(function(sig) { // add some default signatures that are used in the library + tables[sig] = emptyTable(sig); // TODO: make them compact + }); + } for (var ident in this.indexedFunctions) { - vals[this.indexedFunctions[ident]] = ident; + var sig = ASM_JS ? Functions.implementedFunctions[ident] || Functions.unimplementedFunctions[ident] : 'x'; + assert(sig, ident); + if (!tables[sig]) tables[sig] = emptyTable(sig); // TODO: make them compact + tables[sig][this.indexedFunctions[ident]] = ident; } - // Resolve multi-level aliases all the way down - for (var i = 0; i < vals.length; i++) { - while (1) { - var varData = Variables.globals[vals[i]]; - if (!(varData && varData.resolvedAlias)) break; - vals[i] = vals[+varData.resolvedAlias || eval(varData.resolvedAlias)]; // might need to eval to turn (6) into 6 + var generated = false; + for (var t in tables) { + generated = true; + var table = tables[t]; + for (var i = 0; i < table.length; i++) { + // Resolve multi-level aliases all the way down + while (1) { + var varData = Variables.globals[table[i]]; + if (!(varData && varData.resolvedAlias)) break; + table[i] = table[+varData.resolvedAlias || eval(varData.resolvedAlias)]; // might need to eval to turn (6) into 6 + } + // Resolve library aliases + if (table[i]) { + var libName = LibraryManager.getRootIdent(table[i].substr(1)); + if (libName && typeof libName == 'string') { + table[i] = (libName.indexOf('.') < 0 ? '_' : '') + libName; + } + } + } + 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'; } } - var indices = vals.toString().replace('"', ''); - if (BUILD_AS_SHARED_LIB) { - // Shared libraries reuse the parent's function table. - return 'FUNCTION_TABLE.push.apply(FUNCTION_TABLE, [' + indices + ']);'; - } else { - return 'FUNCTION_TABLE = [' + indices + ']; Module["FUNCTION_TABLE"] = FUNCTION_TABLE;'; + if (!generated && !ASM_JS) { + tables['x'] = 'var FUNCTION_TABLE = [0, 0];\n'; // default empty table } + Functions.tables = tables; } }; @@ -311,16 +356,23 @@ var PassManager = { print('\n//FORWARDED_DATA:' + JSON.stringify({ Types: Types, Variables: Variables, - Functions: Functions + Functions: Functions, + EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS // needed for asm.js global constructors (ctors) })); } else if (phase == 'funcs') { print('\n//FORWARDED_DATA:' + JSON.stringify({ Types: { preciseI64MathUsed: Types.preciseI64MathUsed }, Functions: { blockAddresses: Functions.blockAddresses, - indexedFunctions: Functions.indexedFunctions + indexedFunctions: Functions.indexedFunctions, + implementedFunctions: ASM_JS ? Functions.implementedFunctions : [], + unimplementedFunctions: Functions.unimplementedFunctions, } })); + } else if (phase == 'post') { + print('\n//FORWARDED_DATA:' + JSON.stringify({ + Functions: { tables: Functions.tables } + })); } }, load: function(json) { diff --git a/src/parseTools.js b/src/parseTools.js index b2093da3..6cb3e776 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -128,16 +128,48 @@ function isStructType(type) { return type[0] == '%'; } +function isStructuralType(type) { + return /^{ ?[^}]* ?}$/.test(type); // { i32, i8 } etc. - anonymous struct types +} + +function getStructuralTypeParts(type) { // split { i32, i8 } etc. into parts + return type.replace(/[ {}]/g, '').split(','); +} + +function getStructureTypeParts(type) { + if (isStructuralType(type)) { + return type.replace(/[ {}]/g, '').split(','); + } else { + var typeData = Types.types[type]; + assert(typeData, type); + return typeData.fields; + } +} + +function getStructuralTypePartBits(part) { + return Math.ceil((getBits(part) || 32)/32)*32; // simple 32-bit alignment. || 32 is for pointers +} + function isIntImplemented(type) { return type[0] == 'i' || isPointerType(type); } -// Note: works for iX types, not pointers (even though they are implemented as ints) +// Note: works for iX types and structure types, not pointers (even though they are implemented as ints) function getBits(type) { - if (!type || type[0] != 'i') return 0; - var left = type.substr(1); - if (!isNumber(left)) return 0; - return parseInt(left); + if (!type) return 0; + if (type[0] == 'i') { + var left = type.substr(1); + if (!isNumber(left)) return 0; + return parseInt(left); + } + if (isStructuralType(type)) { + return sum(getStructuralTypeParts(type).map(getStructuralTypePartBits)); + } + if (isStructType(type)) { + var typeData = Types.types[type]; + return typeData.flatSize*8; + } + return 0; } function isIllegalType(type) { @@ -163,11 +195,13 @@ function isFunctionDef(token, out) { var subtext = segment[0].text; fail = fail || segment.length > 1 || !(isType(subtext) || subtext == '...'); }); - if (out) out.numArgs = segments.length; + if (out) { + out.segments = segments; + out.numArgs = segments.length; + } return !fail; } - function isPossiblyFunctionType(type) { // A quick but unreliable way to see if something is a function type. Yes is just 'maybe', no is definite. var len = type.length; @@ -189,6 +223,7 @@ function isFunctionType(type, out) { if (pointingLevels(type) !== 1) return false; var text = removeAllPointing(parts.slice(1).join(' ')); if (!text) return false; + if (out) out.returnType = parts[0]; return isType(parts[0]) && isFunctionDef({ text: text, item: tokenize(text.substr(1, text.length-2), true) }, out); } @@ -310,7 +345,9 @@ function parseParamTokens(params) { if (segment.length == 1) { if (segment[0].text == '...') { ret.push({ - intertype: 'varargs' + intertype: 'varargs', + type: 'i8*', + ident: 'varrp' // the conventional name we have for this }); } else { // Clang sometimes has a parameter with just a type, @@ -360,25 +397,34 @@ var UNINDEXABLE_GLOBALS = set( '_llvm_global_ctors' // special-cased ); -function noticePtr(ptr) { - if (!NAMED_GLOBALS && !LibraryManager.loaded) UNINDEXABLE_GLOBALS[ptr] = 1; // we cannot index globals referred to in the library, since they are used there by name -} - function isIndexableGlobal(ident) { if (!(ident in Variables.globals)) return false; - if (ident in UNINDEXABLE_GLOBALS) return false; + if (ident in UNINDEXABLE_GLOBALS) { + Variables.globals[ident].unIndexable = true; + return false; + } var data = Variables.globals[ident]; - return !data.alias && !data.external; + // in asm.js, externals are just globals + return !data.alias && (ASM_JS || !data.external); } function makeGlobalDef(ident) { if (!NAMED_GLOBALS && isIndexableGlobal(ident)) return ''; - return 'var ' + ident + ';'; // TODO: add option for namespacing or offsetting to allow reducing the number of globals + return 'var ' + ident + ';'; } function makeGlobalUse(ident) { - if (!NAMED_GLOBALS && isIndexableGlobal(ident)) return '(' + getFastValue('GLOBAL_BASE', '+', Variables.indexedGlobals[ident]) + ')'; - return ident; // TODO: add option for namespacing or offsetting to allow reducing the number of globals + if (!NAMED_GLOBALS && isIndexableGlobal(ident)) { + var index = Variables.indexedGlobals[ident]; + if (index === undefined) { + // we are accessing this before we index globals, likely from the library. mark as unindexable + UNINDEXABLE_GLOBALS[ident] = 1; + return ident; + } + // We know and assert on TOTAL_STACK being equal to GLOBAL_BASE + return (TOTAL_STACK + index).toString(); + } + return ident; } function sortGlobals(globals) { @@ -447,7 +493,7 @@ function parseLLVMSegment(segment) { return { intertype: 'value', ident: toNiceIdent(segment[1].text), - type: segment[0].text + type: type }; } } @@ -922,6 +968,12 @@ function checkSafeHeap() { return SAFE_HEAP === 1 || checkSpecificSafeHeap(); } +if (ASM_JS) { + var hexMemoryMask = '0x' + (TOTAL_MEMORY-1).toString(16); + var decMemoryMask = (TOTAL_MEMORY-1).toString(); + var memoryMask = hexMemoryMask.length <= decMemoryMask.length ? hexMemoryMask : decMemoryMask; +} + function getHeapOffset(offset, type) { if (USE_TYPED_ARRAYS !== 2) { return offset; @@ -930,17 +982,64 @@ function getHeapOffset(offset, type) { type = 'i32'; // XXX we emulate 64-bit values as 32 } var shifts = Math.log(Runtime.getNativeTypeSize(type))/Math.LN2; + offset = '(' + offset + ')'; + if (ASM_JS && phase == 'funcs') offset = '(' + offset + '&' + memoryMask + ')'; if (shifts != 0) { - return '((' + offset + ')>>' + (shifts) + ')'; + return '(' + offset + '>>' + shifts + ')'; } else { - return '(' + offset + ')'; + return offset; } } } +function makeVarDef(js) { + if (!ASM_JS) js = 'var ' + js; + return js; +} + +function asmEnsureFloat(value, type) { // ensures that a float type has either 5.5 (clearly a float) or +5 (float due to asm coercion) + if (!ASM_JS) return value; + if (type in Runtime.FLOAT_TYPES && isNumber(value) && value.toString().indexOf('.') < 0) { + return '(+(' + value + '))'; + } else { + return value; + } +} + +function asmInitializer(type, impl) { + if (type in Runtime.FLOAT_TYPES) { + return '+0'; + } else { + return '0'; + } +} + +function asmCoercion(value, type) { + if (!ASM_JS) return value; + if (type == 'void') { + return value; + } else if (type in Runtime.FLOAT_TYPES) { + return '(+(' + value + '))'; + } else { + return '((' + value + ')|0)'; + } +} + +function asmMultiplyI32(a, b) { + // special-case: there is no integer multiply in asm, because there is no true integer + // multiply in JS. While we wait for Math.imul, do double multiply + if (USE_MATH_IMUL) { + return 'Math.imul(' + a + ',' + b + ')'; + } + return '(~~(+' + a + ' * +' + b + '))'; +} + +function makeGetTempDouble(i) { // TODO: Support other than i32 + return makeGetValue('tempDoublePtr', Runtime.getNativeTypeSize('i32')*i, 'i32'); +} + // See makeSetValue function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSafe) { - noticePtr(ptr); if (UNALIGNED_MEMORY) align = 1; if (isStructType(type)) { var typeData = Types.types[type]; @@ -952,9 +1051,9 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa } if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double') { - return '(tempDoubleI32[0]=' + makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align) + ',' + - 'tempDoubleI32[1]=' + makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, unsigned, ignore, align) + ',' + - 'tempDoubleF64[0])'; + return '(HEAP32[tempDoublePtr>>2]=' + makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align) + ',' + + 'HEAP32[tempDoublePtr+4>>2]=' + makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, unsigned, ignore, align) + ',' + + 'HEAPF64[tempDoublePtr>>3])'; } if (USE_TYPED_ARRAYS == 2 && align) { @@ -977,9 +1076,9 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa } } else { if (type == 'float') { - ret += 'copyTempFloat(' + getFastValue(ptr, '+', pos) + '),tempDoubleF32[0]'; + ret += 'copyTempFloat(' + getFastValue(ptr, '+', pos) + '),HEAPF32[tempDoublePtr>>2]'; } else { - ret += 'copyTempDouble(' + getFastValue(ptr, '+', pos) + '),tempDoubleF64[0]'; + ret += 'copyTempDouble(' + getFastValue(ptr, '+', pos) + '),HEAP64[tempDoublePtr>>3]'; } } ret += ')'; @@ -993,14 +1092,24 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa if (type[0] === '#') type = type.substr(1); return 'SAFE_HEAP_LOAD(' + offset + ', ' + type + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')'; } else { - return makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type) + ']'; + var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type) + ']'; + if (ASM_JS && phase == 'funcs') { + ret = asmCoercion(ret, type); + } + return ret; } } function indexizeFunctions(value, type) { assert((type && type !== '?') || (typeof value === 'string' && value.substr(0, 6) === 'CHECK_'), 'No type given for function indexizing'); assert(value !== type, 'Type set to value'); - if (type && isFunctionType(type) && value[0] === '_') { // checking for _ differentiates from $ (local vars) + var out = {}; + if (type && isFunctionType(type, out) && value[0] === '_') { // checking for _ differentiates from $ (local vars) + // add signature to library functions that we now know need indexing + if (!(value in Functions.implementedFunctions) && !(value in Functions.unimplementedFunctions)) { + Functions.unimplementedFunctions[value] = Functions.getSignature(out.returnType, out.segments ? out.segments.map(function(segment) { return segment[0].text }) : []); + } + if (BUILD_AS_SHARED_LIB) { return '(FUNCTION_TABLE_OFFSET + ' + Functions.getIndex(value) + ')'; } else { @@ -1021,7 +1130,6 @@ function indexizeFunctions(value, type) { //! which means we should write to all slabs, ignore type differences if any on reads, etc. //! @param noNeedFirst Whether to ignore the offset in the pointer itself. function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) { - noticePtr(ptr); if (UNALIGNED_MEMORY && !forcedAlign) align = 1; sep = sep || ';'; if (isStructType(type)) { @@ -1039,9 +1147,9 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, } if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double') { - return '(tempDoubleF64[0]=' + value + ',' + - makeSetValue(ptr, pos, 'tempDoubleI32[0]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' + - makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'tempDoubleI32[1]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ')'; + return '(HEAPF64[tempDoublePtr>>3]=' + value + ',' + + makeSetValue(ptr, pos, 'HEAP32[tempDoublePtr>>2]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' + + makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'HEAP32[tempDoublePtr+4>>2]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ')'; } else if (USE_TYPED_ARRAYS == 2 && type == 'i64') { return '(tempI64 = [' + splitI64(value) + '],' + makeSetValue(ptr, pos, 'tempI64[0]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' + @@ -1093,7 +1201,6 @@ var SEEK_OPTIMAL_ALIGN_MIN = 20; var UNROLL_LOOP_MAX = 8; function makeSetValues(ptr, pos, value, type, num, align) { - noticePtr(ptr); function unroll(type, num, jump, value$) { jump = jump || 1; value$ = value$ || value; @@ -1125,7 +1232,7 @@ function makeSetValues(ptr, pos, value, type, num, align) { [4, 2, 1].forEach(function(possibleAlign) { if (num == 0) return; if (align >= possibleAlign) { - if (num <= UNROLL_LOOP_MAX*possibleAlign) { + if (num <= UNROLL_LOOP_MAX*possibleAlign || ASM_JS) { // XXX test asm performance ret.push(unroll('i' + (possibleAlign*8), Math.floor(num/possibleAlign), possibleAlign, values[possibleAlign])); } else { ret.push('for (var $$dest = ' + getFastValue(ptr, '+', pos) + (possibleAlign > 1 ? '>>' + log2(possibleAlign) : '') + ', ' + @@ -1143,8 +1250,6 @@ function makeSetValues(ptr, pos, value, type, num, align) { var TYPED_ARRAY_SET_MIN = Infinity; // .set() as memcpy seems to just slow us down function makeCopyValues(dest, src, num, type, modifier, align, sep) { - noticePtr(dest); - noticePtr(src); sep = sep || ';'; function unroll(type, num, jump) { jump = jump || 1; @@ -1171,7 +1276,7 @@ function makeCopyValues(dest, src, num, type, modifier, align, sep) { } else { // USE_TYPED_ARRAYS == 2 // If we don't know how to handle this at compile-time, or handling it is best done in a large amount of code, call memset if (!isNumber(num) || (align < 4 && parseInt(num) >= SEEK_OPTIMAL_ALIGN_MIN)) { - return '_memcpy(' + dest + ', ' + src + ', ' + num + ', ' + align + ')'; + return '_memcpy(' + dest + ', ' + src + ', ' + num + ')'; } num = parseInt(num); var ret = []; @@ -1179,7 +1284,7 @@ function makeCopyValues(dest, src, num, type, modifier, align, sep) { if (num == 0) return; if (align >= possibleAlign) { // If we can unroll the loop, do so. Also do so if we must unroll it (we do not create real loops when inlined) - if (num <= UNROLL_LOOP_MAX*possibleAlign || sep == ',') { + if (num <= UNROLL_LOOP_MAX*possibleAlign || sep == ',' || ASM_JS) { // XXX test asm performance ret.push(unroll('i' + (possibleAlign*8), Math.floor(num/possibleAlign), possibleAlign)); } else { assert(sep == ';'); @@ -1245,6 +1350,9 @@ function getFastValue(a, op, b, type) { return '(' + a + '<<' + shifts + ')'; } } + if (ASM_JS && !(type in Runtime.FLOAT_TYPES)) { + return asmMultiplyI32(a, b); // unoptimized multiply, do it using asm.js's special multiply operation + } } else { if (a == '0') { return '0'; @@ -1509,8 +1617,35 @@ function handleOverflow(text, bits) { } } -function makeLLVMStruct(values) { // TODO: Use this everywhere - return '{ ' + values.map(function(value, i) { return 'f' + i + ': ' + value }).join(', ') + ' }' +function makeLLVMStruct(values) { + if (USE_TYPED_ARRAYS == 2) { + return 'DEPRECATED' + (new Error().stack) + 'XXX'; + } else { + return '{ ' + values.map(function(value, i) { return 'f' + i + ': ' + value }).join(', ') + ' }' + } +} + +function makeStructuralReturn(values) { + if (USE_TYPED_ARRAYS == 2) { + var i = 0; + return 'return (' + values.slice(1).map(function(value) { + return ASM_JS ? 'asm.setTempRet' + (i++) + '(' + value + ')' + : 'tempRet' + (i++) + ' = ' + value; + }).concat([values[0]]).join(',') + ')'; + } else { + var i = 0; + return 'return { ' + values.map(function(value) { + return 'f' + (i++) + ': ' + value; + }).join(', ') + ' }'; + } +} + +function makeStructuralAccess(ident, i) { + if (USE_TYPED_ARRAYS == 2) { + return ident + '$' + i; + } else { + return ident + '.f' + i; + } } // From parseLLVMSegment @@ -1537,6 +1672,7 @@ function finalizeLLVMParameter(param, noIndexizeFunctions) { ret = parseI64Constant(ret); } ret = parseNumerical(ret, param.type); + ret = asmEnsureFloat(ret, param.type); } else if (param.intertype == 'structvalue') { ret = makeLLVMStruct(param.params.map(function(value) { return finalizeLLVMParameter(value, noIndexizeFunctions) })); } else if (param.intertype === 'blockaddress') { @@ -1619,30 +1755,36 @@ function makeSignOp(value, type, op, force, ignore) { function makeRounding(value, bits, signed, floatConversion) { // TODO: handle roundings of i64s assert(bits); - // C rounds to 0 (-5.5 to -5, +5.5 to 5), while JS has no direct way to do that. - if (bits <= 32 && signed) return '((' + value + ')&-1)'; // This is fast and even correct, for all cases. Note that it is the same - // as |0, but &-1 hints to the js optimizer that this is a rounding correction - // Do Math.floor, which is reasonably fast, if we either don't care, or if we can be sure - // the value is non-negative - if (!correctRoundings() || (!signed && !floatConversion)) return 'Math.floor(' + value + ')'; - // We are left with >32 bits signed, or a float conversion. Check and correct inline - // Note that if converting a float, we may have the wrong sign at this point! But, we have - // been rounded properly regardless, and we will be sign-corrected later when actually used, if - // necessary. - return makeInlineCalculation('VALUE >= 0 ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR'); -/* refactored version - needs perf testing TODO - if (bits <= 32) { - if (signed) { - return '((' + value + ')&-1)'; // &-1 (instead of |0) hints to the js optimizer that this is a rounding correction - } else { - return '((' + value + ')>>>0)'; + if (!ASM_JS) { + // C rounds to 0 (-5.5 to -5, +5.5 to 5), while JS has no direct way to do that. + if (bits <= 32 && signed) return '((' + value + ')&-1)'; // This is fast and even correct, for all cases. Note that it is the same + // as |0, but &-1 hints to the js optimizer that this is a rounding correction + // Do Math.floor, which is reasonably fast, if we either don't care, or if we can be sure + // the value is non-negative + if (!correctRoundings() || (!signed && !floatConversion)) return 'Math.floor(' + value + ')'; + // We are left with >32 bits signed, or a float conversion. Check and correct inline + // Note that if converting a float, we may have the wrong sign at this point! But, we have + // been rounded properly regardless, and we will be sign-corrected later when actually used, if + // necessary. + return makeInlineCalculation('VALUE >= 0 ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR'); + } else { + // asm.js mode, cleaner refactoring of this function as well. TODO: use in non-asm case, most of this + if (floatConversion && bits <= 32) { + return '(~~(' + value + '))'; // explicit float-to-int conversion + } + + if (bits <= 32) { + if (signed) { + return '((' + value + ')&-1)'; // &-1 (instead of |0) hints to the js optimizer that this is a rounding correction + } else { + return '((' + value + ')>>>0)'; + } } + // Math.floor is reasonably fast if we don't care about corrections (and even correct if unsigned) + if (!correctRoundings() || !signed) return 'Math.floor(' + value + ')'; + // We are left with >32 bits + return makeInlineCalculation('VALUE >= 0 ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR'); } - // Math.floor is reasonably fast if we don't care about corrections (and even correct if unsigned) - if (!correctRoundings() || !signed) return 'Math.floor(' + value + ')'; - // We are left with >32 bits - return makeInlineCalculation('VALUE >= 0 ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR'); -*/ } // fptoui and fptosi are not in these, because we need to be careful about what we do there. We can't @@ -1716,8 +1858,8 @@ function processMathop(item) { } function i64PreciseOp(type, lastArg) { Types.preciseI64MathUsed = true; - return finish(['(i64Math.' + type + '(' + low1 + ',' + high1 + ',' + low2 + ',' + high2 + - (lastArg ? ',' + lastArg : '') + '),i64Math.result[0])', 'i64Math.result[1]']); + return finish(['(i64Math' + (ASM_JS ? '_' : '.') + type + '(' + low1 + ',' + high1 + ',' + low2 + ',' + high2 + + (lastArg ? ',' + lastArg : '') + '),' + makeGetValue('tempDoublePtr', 0, 'i32') + ')', makeGetValue('tempDoublePtr', Runtime.getNativeTypeSize('i32'), 'i32')]); } switch (op) { // basic integer ops @@ -1734,7 +1876,8 @@ function processMathop(item) { case 'ashr': case 'lshr': { if (!isNumber(idents[1])) { - return 'Runtime.bitshift64(' + idents[0] + '[0], ' + idents[0] + '[1],"' + op + '",' + stripCorrections(idents[1]) + '[0]|0)'; + return '(Runtime' + (ASM_JS ? '_' : '.') + 'bitshift64(' + idents[0] + '[0], ' + idents[0] + '[1],"' + op + '",' + stripCorrections(idents[1]) + '[0]|0),' + + '[' + makeGetTempDouble(0) + ',' + makeGetTempDouble(1) + '])'; } bits = parseInt(idents[1]); var ander = Math.pow(2, bits)-1; @@ -1851,15 +1994,15 @@ function processMathop(item) { var outType = item.type; if (inType in Runtime.INT_TYPES && outType in Runtime.FLOAT_TYPES) { if (legalizedI64s) { - return '(tempDoubleI32[0]=' + idents[0] + '$0, tempDoubleI32[1]=' + idents[0] + '$1, tempDoubleF64[0])'; + return '(HEAP32[tempDoublePtr>>2]=' + idents[0] + '$0, HEAP32[tempDoublePtr+4>>2]=' + idents[0] + '$1, HEAPF64[tempDoublePtr>>3])'; } else { - return makeInlineCalculation('tempDoubleI32[0]=VALUE[0],tempDoubleI32[1]=VALUE[1],tempDoubleF64[0]', idents[0], 'tempI64'); + return makeInlineCalculation('HEAP32[tempDoublePtr>>2]=VALUE[0],HEAP32[tempDoublePtr+4>>2]=VALUE[1],HEAPF64[tempDoublePtr>>>3]', idents[0], 'tempI64'); } } else if (inType in Runtime.FLOAT_TYPES && outType in Runtime.INT_TYPES) { if (legalizedI64s) { - return 'tempDoubleF64[0]=' + idents[0] + '; ' + finish(['tempDoubleI32[0]','tempDoubleI32[1]']); + return 'HEAPF64[tempDoublePtr>>3]=' + idents[0] + '; ' + finish(['HEAP32[tempDoublePtr>>2]','HEAP32[tempDoublePtr+4>>2]']); } else { - return '(tempDoubleF64[0]=' + idents[0] + ',[tempDoubleI32[0],tempDoubleI32[1]])'; + return '(HEAPF64[tempDoublePtr>>3]=' + idents[0] + ',[HEAP32[tempDoublePtr>>2],HEAP32[tempDoublePtr+4>>2]])'; } } else { throw 'Invalid USE_TYPED_ARRAYS == 2 bitcast: ' + dump(item) + ' : ' + item.params[0].type; @@ -1877,7 +2020,7 @@ function processMathop(item) { case 'mul': { if (bits == 32 && PRECISE_I32_MUL) { Types.preciseI64MathUsed = true; - return '(i64Math.multiply(' + idents[0] + ',0,' + idents[1] + ',0),i64Math.result[0])'; + return '(i64Math' + (ASM_JS ? '_' : '.') + 'multiply(' + idents[0] + ',0,' + idents[1] + ',0),' + makeGetValue('tempDoublePtr', 0, 'i32') + ')'; } else { return handleOverflow(getFastValue(idents[0], '*', idents[1], item.type), bits); } @@ -1927,7 +2070,7 @@ function processMathop(item) { case 'fdiv': return getFastValue(idents[0], '/', idents[1], item.type); case 'fmul': return getFastValue(idents[0], '*', idents[1], item.type); case 'frem': return getFastValue(idents[0], '%', idents[1], item.type); - case 'uitofp': case 'sitofp': return idents[0]; + case 'uitofp': case 'sitofp': return asmCoercion(idents[0], 'double'); case 'fptoui': case 'fptosi': return makeRounding(idents[0], bitsLeft, op === 'fptosi', true); // TODO: We sometimes generate false instead of 0, etc., in the *cmps. It seemed slightly faster before, but worth rechecking @@ -1970,7 +2113,7 @@ function processMathop(item) { // then unsigning that i32... which would give something huge. case 'zext': case 'fpext': case 'sext': return idents[0]; case 'fptrunc': return idents[0]; - case 'select': return idents[0] + ' ? ' + idents[1] + ' : ' + idents[2]; + case 'select': return idents[0] + ' ? ' + asmEnsureFloat(idents[1], item.type) + ' : ' + asmEnsureFloat(idents[2], item.type); case 'ptrtoint': case 'inttoptr': { var ret = ''; if (QUANTUM_SIZE == 1) { @@ -1998,9 +2141,9 @@ function processMathop(item) { (inType in Runtime.FLOAT_TYPES && outType in Runtime.INT_TYPES)) { assert(USE_TYPED_ARRAYS == 2, 'Can only bitcast ints <-> floats with typed arrays mode 2'); if (inType in Runtime.INT_TYPES) { - return '(tempDoubleI32[0] = ' + idents[0] + ',tempDoubleF32[0])'; + return '(HEAP32[tempDoublePtr>>2] = ' + idents[0] + ',HEAPF32[tempDoublePtr>>2])'; } else { - return '(tempDoubleF32[0] = ' + idents[0] + ',tempDoubleI32[0])'; + return '(HEAPF32[tempDoublePtr>>2] = ' + idents[0] + ',HEAP32[tempDoublePtr>>2])'; } } return idents[0]; diff --git a/src/postamble.js b/src/postamble.js index 144b5af9..5f541733 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -18,12 +18,16 @@ Module.callMain = function callMain(args) { argv = allocate(argv, 'i32', ALLOC_STATIC); #if CATCH_EXIT_CODE + var initialStackTop = STACKTOP; try { - return _main(argc, argv, 0); + return Module['_main'](argc, argv, 0); } catch(e) { if (e.name == "ExitStatus") return e.status; throw e; } + finally { + STACKTOP = initialStackTop; + } #else - return _main(argc, argv, 0); + return Module['_main'](argc, argv, 0); #endif } diff --git a/src/preamble.js b/src/preamble.js index 0917f977..cb01994f 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -275,7 +275,7 @@ Module['printProfiling'] = printProfiling; // Runtime essentials //======================================== -var __THREW__ = false; // Used in checking for thrown exceptions. +var __THREW__ = 0; // Used in checking for thrown exceptions. var setjmpId = 1; // Used in setjmp/longjmp var setjmpLabels = {}; @@ -287,6 +287,7 @@ var undef = 0; var tempValue, tempInt, tempBigInt, tempInt2, tempBigInt2, tempPair, tempBigIntI, tempBigIntR, tempBigIntS, tempBigIntP, tempBigIntD; #if USE_TYPED_ARRAYS == 2 var tempI64, tempI64b; +var tempRet0, tempRet1, tempRet2, tempRet3, tempRet4, tempRet5, tempRet6, tempRet7, tempRet8, tempRet9; #endif function abort(text) { @@ -307,18 +308,10 @@ var globalScope = this; // defined with extern "C"). // // Note: LLVM optimizations can inline and remove functions, after which you will not be -// able to call them. Adding +// able to call them. Closure can also do so. To avoid that, add your function to +// the exports using something like // -// __attribute__((used)) -// -// to the function definition will prevent that. -// -// Note: Closure optimizations will minify function names, making -// functions no longer callable. If you run closure (on by default -// in -O2 and above), you should export the functions you will call -// by calling emcc with something like -// -// -s EXPORTED_FUNCTIONS='["_func1","_func2"]' +// -s EXPORTED_FUNCTIONS='["_main", "_myfunc"]' // // @param ident The name of the C function (note that C++ functions will be name-mangled - use extern "C") // @param returnType The return type of the function, one of the JS types 'number', 'string' or 'array' (use 'number' for any C pointer, and @@ -577,9 +570,6 @@ Module['Array_stringify'] = Array_stringify; // Memory management -var FUNCTION_TABLE; // XXX: In theory the indexes here can be equal to pointers to stacked or malloced memory. Such comparisons should - // be false, but can turn out true. We should probably set the top bit to prevent such issues. - var PAGE_SIZE = 4096; function alignMemoryPage(x) { return ((x+4095)>>12)<<12; @@ -601,7 +591,7 @@ var STATICTOP; #if USE_TYPED_ARRAYS function enlargeMemory() { #if ALLOW_MEMORY_GROWTH == 0 - abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ( ' + TOTAL_MEMORY + '), (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.'); + abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value, (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.'); #else // TOTAL_MEMORY is the current size of the actual array, and STATICTOP is the new top. #if ASSERTIONS @@ -699,47 +689,47 @@ Module['HEAPF64'] = HEAPF64; #endif STACK_ROOT = STACKTOP = Runtime.alignMemory(1); -STACK_MAX = STACK_ROOT + TOTAL_STACK; +STACK_MAX = TOTAL_STACK; // we lose a little stack here, but TOTAL_STACK is nice and round so use that as the max #if USE_TYPED_ARRAYS == 2 -var tempDoublePtr = Runtime.alignMemory(STACK_MAX, 8); -var tempDoubleI8 = HEAP8.subarray(tempDoublePtr); -var tempDoubleI32 = HEAP32.subarray(tempDoublePtr >> 2); -var tempDoubleF32 = HEAPF32.subarray(tempDoublePtr >> 2); -var tempDoubleF64 = HEAPF64.subarray(tempDoublePtr >> 3); +var tempDoublePtr = Runtime.alignMemory(allocate(12, 'i8', ALLOC_STACK), 8); +assert(tempDoublePtr % 8 == 0); function copyTempFloat(ptr) { // functions, because inlining this code is increases code size too much - tempDoubleI8[0] = HEAP8[ptr]; - tempDoubleI8[1] = HEAP8[ptr+1]; - tempDoubleI8[2] = HEAP8[ptr+2]; - tempDoubleI8[3] = HEAP8[ptr+3]; + HEAP8[tempDoublePtr] = HEAP8[ptr]; + HEAP8[tempDoublePtr+1] = HEAP8[ptr+1]; + HEAP8[tempDoublePtr+2] = HEAP8[ptr+2]; + HEAP8[tempDoublePtr+3] = HEAP8[ptr+3]; } function copyTempDouble(ptr) { - tempDoubleI8[0] = HEAP8[ptr]; - tempDoubleI8[1] = HEAP8[ptr+1]; - tempDoubleI8[2] = HEAP8[ptr+2]; - tempDoubleI8[3] = HEAP8[ptr+3]; - tempDoubleI8[4] = HEAP8[ptr+4]; - tempDoubleI8[5] = HEAP8[ptr+5]; - tempDoubleI8[6] = HEAP8[ptr+6]; - tempDoubleI8[7] = HEAP8[ptr+7]; + HEAP8[tempDoublePtr] = HEAP8[ptr]; + HEAP8[tempDoublePtr+1] = HEAP8[ptr+1]; + HEAP8[tempDoublePtr+2] = HEAP8[ptr+2]; + HEAP8[tempDoublePtr+3] = HEAP8[ptr+3]; + HEAP8[tempDoublePtr+4] = HEAP8[ptr+4]; + HEAP8[tempDoublePtr+5] = HEAP8[ptr+5]; + HEAP8[tempDoublePtr+6] = HEAP8[ptr+6]; + HEAP8[tempDoublePtr+7] = HEAP8[ptr+7]; } -STACK_MAX = tempDoublePtr + 8; #endif -STATICTOP = alignMemoryPage(STACK_MAX); - +STATICTOP = STACK_MAX; assert(STATICTOP < TOTAL_MEMORY); // Stack must fit in TOTAL_MEMORY; allocations from here on may enlarge TOTAL_MEMORY -var nullString = allocate(intArrayFromString('(null)'), 'i8', ALLOC_STATIC); +var nullString = allocate(intArrayFromString('(null)'), 'i8', ALLOC_STACK); function callRuntimeCallbacks(callbacks) { while(callbacks.length > 0) { var callback = callbacks.shift(); var func = callback.func; if (typeof func === 'number') { - func = FUNCTION_TABLE[func]; + if (callback.arg === undefined) { + Runtime.dynCall('v', func); + } else { + Runtime.dynCall('vi', func, [callback.arg]); + } + } else { + func(callback.arg === undefined ? null : callback.arg); } - func(callback.arg === undefined ? null : callback.arg); } } @@ -881,7 +871,8 @@ function removeRunDependency(id) { clearInterval(runDependencyWatcher); runDependencyWatcher = null; } - if (!calledRun) run(); + // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) + if (!calledRun && shouldRunNow) run(); } } Module['removeRunDependency'] = removeRunDependency; diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp index 933fda96..ae8577b1 100644 --- a/src/relooper/Relooper.cpp +++ b/src/relooper/Relooper.cpp @@ -58,6 +58,8 @@ void PutIndented(const char *String) { *OutputBuffer = 0; } +static int AsmJS = 0; + // Indenter #if EMSCRIPTEN @@ -271,7 +273,11 @@ void MultipleShape::Render(bool InLoop) { RenderLoopPrefix(); bool First = true; for (BlockShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) { - PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->first->Id); + if (AsmJS) { + PrintIndented("%sif ((label|0) == %d) {\n", First ? "" : "else ", iter->first->Id); + } else { + PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->first->Id); + } First = false; Indenter::Indent(); iter->second->Render(InLoop); @@ -977,6 +983,10 @@ void Relooper::MakeOutputBuffer(int Size) { OutputBufferSize = Size; } +void Relooper::SetAsmJSMode(int On) { + AsmJS = On; +} + #if DEBUG // Debugging @@ -1018,6 +1028,10 @@ void rl_make_output_buffer(int size) { Relooper::SetOutputBuffer((char*)malloc(size), size); } +void rl_set_asm_js_mode(int on) { + Relooper::SetAsmJSMode(on); +} + void *rl_new_block(const char *text) { Block *ret = new Block(text); #if DEBUG diff --git a/src/relooper/Relooper.h b/src/relooper/Relooper.h index 08ac8e40..eac03738 100644 --- a/src/relooper/Relooper.h +++ b/src/relooper/Relooper.h @@ -200,6 +200,9 @@ struct Relooper { // Creates an output buffer. Must call this or SetOutputBuffer. static void MakeOutputBuffer(int Size); + + // Sets asm.js mode on or off (default is off) + static void SetAsmJSMode(int On); }; typedef std::set<Block*> BlockSet; @@ -231,6 +234,7 @@ extern "C" { RELOOPERDLL_API void rl_set_output_buffer(char *buffer, int size); RELOOPERDLL_API void rl_make_output_buffer(int size); +RELOOPERDLL_API void rl_set_asm_js_mode(int on); RELOOPERDLL_API void *rl_new_block(const char *text); RELOOPERDLL_API void rl_delete_block(void *block); RELOOPERDLL_API void rl_block_add_branch_to(void *from, void *to, const char *condition, const char *code); diff --git a/src/relooper/emscripten/Makefile b/src/relooper/emscripten/Makefile deleted file mode 100644 index 277dd538..00000000 --- a/src/relooper/emscripten/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -EMSCRIPTEN = ~/Dev/emscripten -EMCC = $(EMSCRIPTEN)/emcc -BINDINGS_GENERATOR = $(EMSCRIPTEN)/tools/bindings_generator.py -NATIVIZER = $(EMSCRIPTEN)/tools/nativize_llvm.py - -all: relooper.js - -relooper.js: - $(EMCC) -O2 ../Relooper.cpp -I.. --post-js glue.js -o relooper.raw.js -s TOTAL_MEMORY=52428800 -s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE='["memcpy", "memset", "malloc", "free", "puts"]' - echo "// Relooper, (C) 2012 Alon Zakai, MIT license, https://github.com/kripken/Relooper" > relooper.js - echo "var Relooper = (function() {" >> relooper.js - cat relooper.raw.js >> relooper.js - echo " return Module.Relooper;" >> relooper.js - echo "})();" >> relooper.js - -clean: - rm relooper.js - diff --git a/src/relooper/emscripten/glue.js b/src/relooper/emscripten/glue.js index 15998acf..36922185 100644 --- a/src/relooper/emscripten/glue.js +++ b/src/relooper/emscripten/glue.js @@ -49,6 +49,9 @@ RelooperGlue['setDebug'] = function(debug) { _rl_set_debugging(+!!debug); }; + RelooperGlue['setAsmJSMode'] = function(on) { + _rl_set_asm_js_mode(on); + }; Module['Relooper'] = RelooperGlue; 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/runtime.js b/src/runtime.js index dd14e779..9d5e5e1f 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -13,7 +13,7 @@ var RuntimeGenerator = { if (init) { ret += sep + '_memset(' + type + 'TOP, 0, ' + size + ')'; } - ret += sep + type + 'TOP += ' + size; + ret += sep + type + 'TOP = (' + type + 'TOP + ' + size + ')|0'; if ({{{ QUANTUM_SIZE }}} > 1 && !ignoreAlign) { ret += sep + RuntimeGenerator.alignMemory(type + 'TOP', {{{ QUANTUM_SIZE }}}); } @@ -23,27 +23,27 @@ var RuntimeGenerator = { // An allocation that lives as long as the current function call stackAlloc: function(size, sep) { sep = sep || ';'; - if (USE_TYPED_ARRAYS === 2) 'STACKTOP += STACKTOP % ' + ({{{ QUANTUM_SIZE }}} - (isNumber(size) ? Math.min(size, {{{ QUANTUM_SIZE }}}) : {{{ QUANTUM_SIZE }}})) + 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)); if (ASSERTIONS) { - ret += sep + 'assert(STACKTOP < STACK_ROOT + STACK_MAX, "Ran out of stack")'; + ret += sep + 'assert(STACKTOP|0 < STACK_MAX|0)'; } return ret; }, stackEnter: function(initial, force) { if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return ''; - var ret = 'var __stackBase__ = STACKTOP'; - if (initial > 0) ret += '; STACKTOP += ' + initial; + var ret = 'var __stackBase__ = ' + (ASM_JS ? '0; __stackBase__ = ' : '') + 'STACKTOP'; + if (initial > 0) ret += '; STACKTOP = (STACKTOP + ' + initial + ')|0'; if (USE_TYPED_ARRAYS == 2) { assert(initial % QUANTUM_SIZE == 0); if (ASSERTIONS) { - ret += '; assert(STACKTOP % {{{ QUANTUM_SIZE }}} == 0, "Stack is unaligned")'; + ret += '; assert(STACKTOP|0 % {{{ QUANTUM_SIZE }}} == 0)'; } } if (ASSERTIONS) { - ret += '; assert(STACKTOP < STACK_MAX, "Ran out of stack")'; + ret += '; assert(STACKTOP < STACK_MAX)'; } if (INIT_STACK) { ret += '; _memset(__stackBase__, 0, ' + initial + ')'; @@ -124,36 +124,50 @@ var Runtime = { // Mirrors processMathop's treatment of constants (which we optimize directly) bitshift64: function(low, high, op, bits) { + var ret; var ander = Math.pow(2, bits)-1; if (bits < 32) { switch (op) { case 'shl': - return [low << bits, (high << bits) | ((low&(ander << (32 - bits))) >>> (32 - bits))]; + ret = [low << bits, (high << bits) | ((low&(ander << (32 - bits))) >>> (32 - bits))]; + break; case 'ashr': - return [(((low >>> bits ) | ((high&ander) << (32 - bits))) >> 0) >>> 0, (high >> bits) >>> 0]; + ret = [(((low >>> bits ) | ((high&ander) << (32 - bits))) >> 0) >>> 0, (high >> bits) >>> 0]; + break; case 'lshr': - return [((low >>> bits) | ((high&ander) << (32 - bits))) >>> 0, high >>> bits]; + ret = [((low >>> bits) | ((high&ander) << (32 - bits))) >>> 0, high >>> bits]; + break; } } else if (bits == 32) { switch (op) { case 'shl': - return [0, low]; + ret = [0, low]; + break; case 'ashr': - return [high, (high|0) < 0 ? ander : 0]; + ret = [high, (high|0) < 0 ? ander : 0]; + break; case 'lshr': - return [high, 0]; + ret = [high, 0]; + break; } } else { // bits > 32 switch (op) { case 'shl': - return [0, low << (bits - 32)]; + ret = [0, low << (bits - 32)]; + break; case 'ashr': - return [(high >> (bits - 32)) >>> 0, (high|0) < 0 ? ander : 0]; + ret = [(high >> (bits - 32)) >>> 0, (high|0) < 0 ? ander : 0]; + break; case 'lshr': - return [high >>> (bits - 32) , 0]; + ret = [high >>> (bits - 32) , 0]; + break; } } - abort('unknown bitshift64 op: ' + [value, op, bits]); +#if ASSERTIONS + assert(ret); +#endif + HEAP32[tempDoublePtr>>2] = ret[0]; // cannot use utility functions since we are in runtime itself + HEAP32[tempDoublePtr+4>>2] = ret[1]; }, // Imprecise bitops utilities @@ -311,10 +325,35 @@ var Runtime = { return ret; }, - addFunction: function(func) { - var ret = FUNCTION_TABLE.length; - FUNCTION_TABLE.push(func); - FUNCTION_TABLE.push(0); + dynCall: function(sig, ptr, args) { + if (args && args.length) { +#if ASSERTIONS + assert(args.length == sig.length-1); +#endif +#if ASM_JS + args.splice(0, 0, ptr); + return Module['dynCall_' + sig].apply(null, args); +#else + return FUNCTION_TABLE[ptr].apply(null, args); +#endif + } else { +#if ASSERTIONS + assert(sig.length == 1); +#endif +#if ASM_JS + return Module['dynCall_' + sig].call(null, ptr); +#else + return FUNCTION_TABLE[ptr](); +#endif + } + }, + + addFunction: function(func, sig) { + assert(sig); + var table = FUNCTION_TABLE; // TODO: support asm + var ret = table.length; + table.push(func); + table.push(0); return ret; }, @@ -328,10 +367,11 @@ var Runtime = { funcWrappers: {}, - getFuncWrapper: function(func) { + getFuncWrapper: function(func, sig) { + assert(sig); if (!Runtime.funcWrappers[func]) { Runtime.funcWrappers[func] = function() { - FUNCTION_TABLE[func].apply(null, arguments); + Runtime.dynCall(sig, func, arguments); }; } return Runtime.funcWrappers[func]; diff --git a/src/settings.js b/src/settings.js index c4d1df48..8ae287f9 100644 --- a/src/settings.js +++ b/src/settings.js @@ -43,7 +43,7 @@ var TOTAL_STACK = 5*1024*1024; // The total stack size. There is no way to enlar // value must be large enough for the program's requirements. If // assertions are on, we will assert on not exceeding this, otherwise, // it will fail silently. -var TOTAL_MEMORY = 10*1024*1024; // The total amount of memory to use. Using more memory than this will +var TOTAL_MEMORY = 16777216; // The total amount of memory to use. Using more memory than this will // cause us to expand the heap, which can be costly with typed arrays: // we need to copy the old heap into a new one in that case. var FAST_MEMORY = 2*1024*1024; // The amount of memory to initialize to 0. This ensures it will be @@ -118,7 +118,9 @@ var INLINING_LIMIT = 50; // A limit on inlining. If 0, we will inline normally i 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 - // terminated with an error message. + // terminated with an error message, that is, the exception thrown + // by exit() is not handled in any way (in particular, the stack + // position will not be reset). // Generated code debugging options var SAFE_HEAP = 0; // Check each write to the heap, for example, this will give a clear @@ -138,7 +140,7 @@ var LABEL_FUNCTION_FILTERS = []; // Filters for function label debug. // labels of functions that is equaled to // one of the filters are printed out // When the array is empty, the filter is disabled. -var EXCEPTION_DEBUG = 1; // Print out exceptions in emscriptened code +var EXCEPTION_DEBUG = 0; // Print out exceptions in emscriptened code var LIBRARY_DEBUG = 0; // Print out when we enter a library call (library*.js). You can also unset // Runtime.debug at runtime for logging to cease, and can set it when you @@ -200,7 +202,7 @@ var PGO = 0; // Profile-guided optimization. // All CORRECT_* options default to 1 with PGO builds. // See https://github.com/kripken/emscripten/wiki/Optimizing-Code for more info -var NAMED_GLOBALS = 1; // If 1, we use global variables for globals. Otherwise +var NAMED_GLOBALS = 0; // If 1, we use global variables for globals. Otherwise // they are referred to by a base plus an offset (called an indexed global), // saving global variables but adding runtime overhead. @@ -220,6 +222,12 @@ var DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = ['memcpy', 'memset', 'malloc', 'free', '$ // add it here (and in EXPORTED FUNCTIONS with prefix // "_", for closure). +var LIBRARY_DEPS_TO_AUTOEXPORT = ['memcpy']; // This list is also used to determine + // auto-exporting of library dependencies (i.e., functions that + // might be dependencies of JS library functions, that if + // so we must export so that if they are implemented in C + // they will be accessible, in ASM_JS mode). + var IGNORED_FUNCTIONS = []; // Functions that we should not generate, neither a stub nor a complete function. // This is useful if your project code includes a function, and you want to replace // that in the compiled code with your own handwritten JS. (Of course even without @@ -301,6 +309,11 @@ var HEADLESS = 0; // If 1, will include shim code that tries to 'fake' a browser // very partial - it is hard to fake a whole browser! - so // keep your expectations low for this to work. +var ASM_JS = 0; // If 1, generate code in asm.js format. XXX This is highly experimental, + // and will not work on most codebases yet. It is NOT recommended that you + // try this yet. +var USE_MATH_IMUL = 0; // If 1, use Math.imul when useful + var NECESSARY_BLOCKADDRS = []; // List of (function, block) for all block addresses that are taken. // Compiler debugging options @@ -1186,6 +1199,8 @@ var C_DEFINES = {'SI_MESGQ': '5', '_SC_TTY_NAME_MAX': '41', 'AF_INET': '1', 'AF_INET6': '6', - 'FIONREAD': '1' + 'FIONREAD': '1', + 'SOCK_STREAM': '200', + 'IPPROTO_TCP': 1 }; diff --git a/src/utility.js b/src/utility.js index 84b50ce9..63582ae8 100644 --- a/src/utility.js +++ b/src/utility.js @@ -321,6 +321,12 @@ function isPowerOfTwo(x) { return x > 0 && ((x & (x-1)) == 0); } +function ceilPowerOfTwo(x) { + var ret = 1; + while (ret < x) ret <<= 1; + return ret; +} + function Benchmarker() { var starts = {}, times = {}, counts = {}; this.start = function(id) { |