diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 34 | ||||
-rw-r--r-- | src/compiler.js | 15 | ||||
-rw-r--r-- | src/compiler_phase.html | 33 | ||||
-rw-r--r-- | src/fastLong.js | 12 | ||||
-rw-r--r-- | src/intertyper.js | 168 | ||||
-rw-r--r-- | src/jsifier.js | 216 | ||||
-rw-r--r-- | src/library.js | 845 | ||||
-rw-r--r-- | src/library_fs.js | 83 | ||||
-rw-r--r-- | src/library_gl.js | 168 | ||||
-rw-r--r-- | src/library_idbfs.js | 216 | ||||
-rw-r--r-- | src/library_memfs.js | 23 | ||||
-rw-r--r-- | src/library_nodefs.js | 234 | ||||
-rw-r--r-- | src/library_sdl.js | 465 | ||||
-rw-r--r-- | src/library_sockfs.js | 12 | ||||
-rw-r--r-- | src/modules.js | 28 | ||||
-rw-r--r-- | src/parseTools.js | 314 | ||||
-rw-r--r-- | src/preamble.js | 31 | ||||
-rw-r--r-- | src/runtime.js | 19 | ||||
-rw-r--r-- | src/settings.js | 444 | ||||
-rw-r--r-- | src/struct_info.json | 1045 | ||||
-rw-r--r-- | src/utility.js | 11 |
21 files changed, 3029 insertions, 1387 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index 750f2a4c..17ad26ad 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -30,6 +30,8 @@ function analyzer(data, sidePass) { var item = { items: data }; var data = item; + var newTypes = {}; + // Gather // Single-liners ['globalVariable', 'functionStub', 'unparsedFunction', 'unparsedGlobals', 'unparsedTypes', 'alias'].forEach(function(intertype) { @@ -42,6 +44,7 @@ function analyzer(data, sidePass) { temp.splitOut.forEach(function(type) { //dprint('types', 'adding defined type: ' + type.name_); Types.types[type.name_] = type; + newTypes[type.name_] = 1; if (QUANTUM_SIZE === 1) { Types.fatTypes[type.name_] = copy(type); } @@ -780,13 +783,14 @@ function analyzer(data, sidePass) { assert(PRECISE_I64_MATH, 'Must have precise i64 math for non-constant 64-bit shifts'); Types.preciseI64MathUsed = 1; value.intertype = 'value'; - value.ident = 'var ' + value.assignTo + '$0 = ' + + value.ident = makeVarDef(value.assignTo) + '$0=' + asmCoercion('_bitshift64' + value.op[0].toUpperCase() + value.op.substr(1) + '(' + asmCoercion(sourceElements[0].ident, 'i32') + ',' + asmCoercion(sourceElements[1].ident, 'i32') + ',' + asmCoercion(value.params[1].ident + '$0', 'i32') + ')', 'i32' ) + ';' + - 'var ' + value.assignTo + '$1 = tempRet0;'; + makeVarDef(value.assignTo) + '$1=tempRet0;'; + value.vars = [[value.assignTo + '$0', 'i32'], [value.assignTo + '$1', 'i32']]; value.assignTo = null; i++; continue; @@ -897,7 +901,7 @@ function analyzer(data, sidePass) { }); } - function addTypeInternal(type, data) { + function addTypeInternal(type) { if (type.length == 1) return; if (Types.types[type]) return; if (['internal', 'hidden', 'inbounds', 'void'].indexOf(type) != -1) return; @@ -908,8 +912,9 @@ function analyzer(data, sidePass) { // to look at the underlying type - it was not defined explicitly // anywhere else. var nonPointing = removeAllPointing(type); + if (Types.types[nonPointing]) return; var check = /^\[(\d+)\ x\ (.*)\]$/.exec(nonPointing); - if (check && !Types.types[nonPointing]) { + if (check) { var num = parseInt(check[1]); num = Math.max(num, 1); // [0 x something] is used not for allocations and such of course, but // for indexing - for an |array of unknown length|, basically. So we @@ -917,7 +922,7 @@ function analyzer(data, sidePass) { // check that we never allocate with this (either as a child structure // in the analyzer, or in calcSize in alloca). var subType = check[2]; - addTypeInternal(subType, data); // needed for anonymous structure definitions (see below) + addTypeInternal(subType); // needed for anonymous structure definitions (see below) // Huge structural types are represented very inefficiently, both here and in generated JS. Best to avoid them - for example static char x[10*1024*1024]; is bad, while static char *x = malloc(10*1024*1024) is fine. if (num >= 10*1024*1024) warnOnce('warning: very large fixed-size structural type: ' + type + ' - can you reduce it? (compilation may be slow)'); @@ -926,6 +931,7 @@ function analyzer(data, sidePass) { fields: range(num).map(function() { return subType }), lineNum: '?' }; + newTypes[nonPointing] = 1; // Also add a |[0 x type]| type var zerod = '[0 x ' + subType + ']'; if (!Types.types[zerod]) { @@ -934,6 +940,7 @@ function analyzer(data, sidePass) { fields: [subType, subType], // Two, so we get the flatFactor right. We care about the flatFactor, not the size here lineNum: '?' }; + newTypes[zerod] = 1; } return; } @@ -964,6 +971,7 @@ function analyzer(data, sidePass) { packed: packed, lineNum: '?' }; + newTypes[type] = 1; return; } @@ -975,13 +983,14 @@ function analyzer(data, sidePass) { flatSize: 1, lineNum: '?' }; + newTypes[type] = 1; } - function addType(type, data) { - addTypeInternal(type, data); + function addType(type) { + addTypeInternal(type); if (QUANTUM_SIZE === 1) { Types.flipTypes(); - addTypeInternal(type, data); + addTypeInternal(type); Types.flipTypes(); } } @@ -992,7 +1001,7 @@ function analyzer(data, sidePass) { // which handles type definitions, and later. Doing so before the first side pass will result in // making bad guesses about types which are actually defined for (var type in Types.needAnalysis) { - if (type) addType(type, data); + if (type) addType(type); } Types.needAnalysis = {}; } @@ -1021,17 +1030,18 @@ function analyzer(data, sidePass) { var more = true; while (more) { more = false; - for (var typeName in types) { + for (var typeName in newTypes) { var type = types[typeName]; if (type.flatIndexes) continue; var ready = true; type.fields.forEach(function(field) { if (isStructType(field)) { if (!types[field]) { - addType(field, item); + addType(field); ready = false; } else { if (!types[field].flatIndexes) { + newTypes[field] = 1; ready = false; } } @@ -1058,6 +1068,8 @@ function analyzer(data, sidePass) { Runtime.QUANTUM_SIZE = trueQuantumSize; Types.flipTypes(); } + + newTypes = null; } // Variable analyzer diff --git a/src/compiler.js b/src/compiler.js index 4f16986c..90060837 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -206,6 +206,11 @@ if (phase == 'pre') { if (VERBOSE) printErr('VERBOSE is on, this generates a lot of output and can slow down compilation'); +// Load struct and define information. +var temp = JSON.parse(read(STRUCT_INFO)); +C_STRUCTS = temp.structs; +C_DEFINES = temp.defines; + // Load compiler code load('modules.js'); @@ -215,7 +220,12 @@ load('analyzer.js'); load('jsifier.js'); if (phase == 'funcs' && RELOOP) { // XXX handle !singlePhase RelooperModule = { TOTAL_MEMORY: ceilPowerOfTwo(2*RELOOPER_BUFFER_SIZE) }; - load(RELOOPER); + try { + load(RELOOPER); + } catch(e) { + printErr('cannot load relooper at ' + RELOOPER + ' : ' + e + ', trying in current dir'); + load('relooper.js'); + } assert(typeof Relooper != 'undefined'); } globalEval(processMacros(preprocess(read('runtime.js')))); @@ -269,6 +279,9 @@ function compile(raw) { intertyped = null; JSify(analyzed); + //dumpInterProf(); + //printErr(phase + ' paths (fast, slow): ' + [fastPaths, slowPaths]); + phase = null; if (DEBUG_MEMORY) { diff --git a/src/compiler_phase.html b/src/compiler_phase.html new file mode 100644 index 00000000..8ca631e8 --- /dev/null +++ b/src/compiler_phase.html @@ -0,0 +1,33 @@ +<html> +<body> +<h2>Run the emscripten compiler in a web page, just for laughs</h2> +Open the web console to see stderr output +<hr> +<pre id="output"></pre> +<script> + arguments = ['tmp/emscripten_temp/tmpbTF9CI.txt', 'tmp/emscripten_temp/tmpz8Yvie.pre.ll', 'pre']; // copy from emscripten.py output + + var outputElement = document.getElementById('output'); + print = function(x) { + outputElement.innerHTML += 'output hidden, profiling mode'; + print = function(){}; + //outputElement.innerHTML += x; + }; + + // For generated code + var Module = { + print: function(x) { + throw 'what?' + } + }; + + var startTime = Date.now(); +</script> +<script src="compiler.js"> +</script> +<script> + outputElement.innerHTML += '<br>total time: ' + (Date.now() - startTime); +</script> +</body> +</html> + diff --git a/src/fastLong.js b/src/fastLong.js index 4f6efd9f..2b70b2fb 100644 --- a/src/fastLong.js +++ b/src/fastLong.js @@ -5,12 +5,12 @@ function ___muldsi3($a, $b) { var $1 = 0, $2 = 0, $3 = 0, $6 = 0, $8 = 0, $11 = 0, $12 = 0; $1 = $a & 65535; $2 = $b & 65535; - $3 = Math.imul($2, $1) | 0; + $3 = Math_imul($2, $1) | 0; $6 = $a >>> 16; - $8 = ($3 >>> 16) + (Math.imul($2, $6) | 0) | 0; + $8 = ($3 >>> 16) + (Math_imul($2, $6) | 0) | 0; $11 = $b >>> 16; - $12 = Math.imul($11, $1) | 0; - return (tempRet0 = (($8 >>> 16) + (Math.imul($11, $6) | 0) | 0) + ((($8 & 65535) + $12 | 0) >>> 16) | 0, 0 | ($8 + $12 << 16 | $3 & 65535)) | 0; + $12 = Math_imul($11, $1) | 0; + return (tempRet0 = (($8 >>> 16) + (Math_imul($11, $6) | 0) | 0) + ((($8 & 65535) + $12 | 0) >>> 16) | 0, 0 | ($8 + $12 << 16 | $3 & 65535)) | 0; } function ___divdi3($a$0, $a$1, $b$0, $b$1) { $a$0 = $a$0 | 0; @@ -63,8 +63,8 @@ function ___muldi3($a$0, $a$1, $b$0, $b$1) { $y_sroa_0_0_extract_trunc = $b$0; $1$0 = ___muldsi3($x_sroa_0_0_extract_trunc, $y_sroa_0_0_extract_trunc) | 0; $1$1 = tempRet0; - $2 = Math.imul($a$1, $y_sroa_0_0_extract_trunc) | 0; - return (tempRet0 = ((Math.imul($b$1, $x_sroa_0_0_extract_trunc) | 0) + $2 | 0) + $1$1 | $1$1 & 0, 0 | $1$0 & -1) | 0; + $2 = Math_imul($a$1, $y_sroa_0_0_extract_trunc) | 0; + return (tempRet0 = ((Math_imul($b$1, $x_sroa_0_0_extract_trunc) | 0) + $2 | 0) + $1$1 | $1$1 & 0, 0 | $1$0 & -1) | 0; } function ___udivdi3($a$0, $a$1, $b$0, $b$1) { $a$0 = $a$0 | 0; diff --git a/src/intertyper.js b/src/intertyper.js index e43cc298..09bdaa33 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -3,6 +3,8 @@ // LLVM assembly => internal intermediate representation, which is ready // to be processed by the later stages. +var fastPaths = 0, slowPaths = 0; + // Line tokenizer function tokenizer(item, inner) { //assert(item.lineNum != 40000); @@ -373,6 +375,14 @@ function intertyper(lines, sidePass, baseLineNums) { // Line parsers to intermediate form // globals: type or variable + function noteGlobalVariable(ret) { + if (!NAMED_GLOBALS) { + Variables.globals[ret.ident].type = ret.type; + Variables.globals[ret.ident].external = ret.external; + } + Types.needAnalysis[ret.type] = 0; + } + function globalHandler(item) { function scanConst(value, type) { // Gets an array of constant items, separated by ',' tokens @@ -497,7 +507,6 @@ function intertyper(lines, sidePass, baseLineNums) { external = true; item.tokens.splice(2, 1); } - Types.needAnalysis[item.tokens[2].text] = 0; var ret = { intertype: 'globalVariable', ident: toNiceIdent(ident), @@ -507,11 +516,7 @@ function intertyper(lines, sidePass, baseLineNums) { named: named, lineNum: item.lineNum }; - if (!NAMED_GLOBALS) { - Variables.globals[ret.ident].type = ret.type; - Variables.globals[ret.ident].external = external; - } - Types.needAnalysis[ret.type] = 0; + noteGlobalVariable(ret); if (ident == '@llvm.global_ctors') { ret.ctors = []; if (item.tokens[3].item) { @@ -845,6 +850,7 @@ function intertyper(lines, sidePass, baseLineNums) { // TODO: also remove 2nd param? } else if (item.op in LLVM.COMPS) { item.type = 'i1'; + if (item.params[1].intertype === 'type') item.params[1].intertype = 'value'; // parsed as type, but comparisons have just values there } if (USE_TYPED_ARRAYS == 2) { // Some specific corrections, since 'i64' is special @@ -984,11 +990,150 @@ function intertyper(lines, sidePass, baseLineNums) { return ret; } + // Fast paths - quick parses of common patterns, avoid tokenizing entirely + + function tryFastPaths(line) { + var m, ret; + if (phase === 'pre') { + // string constant + if (0) { // works, but not worth it m = /([@\.\w\d_]+) = (private )?(unnamed_addr )?(constant )?(\[\d+ x i8\]) c"([^"]+)".*/.exec(line.lineText)) { + if (m[1] === '@llvm.global_ctors') return ret; + ret = { + intertype: 'globalVariable', + ident: toNiceIdent(m[1]), + type: m[5], + external: false, + private_: m[2] !== null, + named: m[3] === null, + lineNum: line.lineNum, + value: { + intertype: 'string', + text: m[6] + } + }; + noteGlobalVariable(ret); + } + } else if (phase === 'funcs') { + // TODO: (void)call, store + if (m = /^ (%[\w\d\._]+) = (getelementptr|load|icmp) ([%\w\d\._ ,\*\-@]+)$/.exec(line.lineText)) { + var assignTo = m[1]; + var intertype = m[2]; + var args = m[3]; + switch (intertype) { + case 'getelementptr': { + if (args[0] === 'i' && args.indexOf('inbounds ') === 0) { + args = args.substr(9); + } + var params = args.split(', ').map(function(param) { + var parts = param.split(' '); + assert(parts.length === 2); + Types.needAnalysis[parts[0]] = 0; + return { + intertype: 'value', + type: parts[0], + ident: toNiceIdent(parts[1]), + byVal: 0 + } + }); + ret = { + intertype: 'getelementptr', + lineNum: line.lineNum, + assignTo: toNiceIdent(assignTo), + ident: params[0].ident, + type: '*', + params: params + }; + break; + } + case 'load': { + if (m = /(^[%\w\d\._\-@\*]+) ([%\w\d\._\-@]+)(, align \d+)?$/.exec(args)) { + var ident = toNiceIdent(m[2]); + var type = m[1]; + assert(type[type.length-1] === '*', type); + var valueType = type.substr(0, type.length-1); + ret = { + intertype: 'load', + lineNum: line.lineNum, + assignTo: toNiceIdent(assignTo), + ident: ident, + type: valueType, + valueType: valueType, + pointerType: type, + pointer: { + intertype: 'value', + ident: ident, + type: type, + }, + align: parseAlign(m[3]) + }; + } + break; + } + case 'icmp': { + var parts = args.split(' '); + assert(parts.length === 4); + ret = { + intertype: 'mathop', + op: 'icmp', + variant: parts[0], + lineNum: line.lineNum, + assignTo: toNiceIdent(assignTo), + params: [{ + intertype: 'value', + ident: toNiceIdent(parts[2].substr(0, parts[2].length-1)), + type: parts[1] + }, { + intertype: 'value', + ident: toNiceIdent(parts[3]), + type: parts[1] + }], + type: 'i1', + }; + break; + } + default: throw 'unexpected fast path type ' + intertype; + } + } + //else if (line.lineText.indexOf(' = icmp ') > 0) printErr('close: ' + JSON.stringify(line.lineText)); + } + if (ret) { + if (COMPILER_ASSERTIONS) { + //printErr(['\n', dump(ret), '\n', dump(triager(tokenizer(line)))]); + var normal = triager(tokenizer(line)); + delete normal.tokens; + delete normal.indent; + assert(sortedJsonCompare(normal, ret), 'fast path: ' + dump(normal) + '\n vs \n' + dump(ret)); + } + } + return ret; + } + // Input lineSplitter().forEach(function(line) { + var item; + if (COMPILER_FASTPATHS) { + item = tryFastPaths(line); + if (item) { + finalResults.push(item); + fastPaths++; + return; + } + } + slowPaths++; + + //var time = Date.now(); + var t = tokenizer(line); - var item = triager(t); + item = triager(t); + + /* + var type = item ? item.intertype + (item.op ? ':' + item.op : ''): 'none'; + if (!interProf[type]) interProf[type] = { ms: 0, n: 0 }; + interProf[type].ms += Date.now() - time; + interProf[type].n++; + */ + if (!item) return; finalResults.push(item); if (item.tokens) item.tokens = null; // We do not need tokens, past the intertyper. Clean them up as soon as possible here. @@ -996,3 +1141,12 @@ function intertyper(lines, sidePass, baseLineNums) { return finalResults; } +// intertyper profiler + +/* +var interProf = {}; +function dumpInterProf() { + printErr('\nintertyper/' + phase + ' (ms | n): ' + JSON.stringify(keys(interProf).sort(function(x, y) { return interProf[y].ms - interProf[x].ms }).map(function(x) { return x + ' : ' + interProf[x].ms + ' | ' + interProf[x].n }), null, ' ') + '\n'); +} +*/ + diff --git a/src/jsifier.js b/src/jsifier.js index 96cb8d9a..0e5f8ef3 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -347,7 +347,7 @@ function JSify(data, functionsOnly, givenFunctions) { js += 'if (globalScope) { assert(!globalScope["' + item.ident + '"]); globalScope["' + item.ident + '"] = ' + item.ident + ' }'; } if (item.external && !NAMED_GLOBALS) { - js = 'var ' + item.ident + ' = ' + js; // force an explicit naming, even if unnamed globals, for asm forwarding + js = 'var ' + item.ident + '=' + js; // force an explicit naming, even if unnamed globals, for asm forwarding } itemsDict.GlobalVariableStub.push({ intertype: 'GlobalVariable', @@ -418,7 +418,7 @@ function JSify(data, functionsOnly, givenFunctions) { } // 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\.\w+/.exec(snippet))) { + if (ASM_JS && !redirectedIdent && (typeof target == 'function' || /Math_\w+/.exec(snippet))) { Functions.libraryFunctions[ident] = 1; } } else if (typeof snippet === 'object') { @@ -537,6 +537,7 @@ function JSify(data, functionsOnly, givenFunctions) { default: throw 'what is this line? ' + dump(line); } assert(line.JS); + //if (ASM_JS) assert(line.JS.indexOf('var ') < 0, dump(line)); if (line.assignTo) makeAssign(line); Framework.currItem = null; }); @@ -584,7 +585,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (DLOPEN_SUPPORT) Functions.getIndex(func.ident); - func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n'; + func.JS += 'function ' + func.ident + '(' + paramIdents.join(',') + '){\n'; if (PGO) { func.JS += INDENTATION + 'PGOMonitor.called["' + func.ident + '"] = 1;\n'; @@ -593,13 +594,23 @@ function JSify(data, functionsOnly, givenFunctions) { if (ASM_JS) { // spell out argument types func.params.forEach(function(param) { - func.JS += INDENTATION + param.ident + ' = ' + asmCoercion(param.ident, param.type) + ';\n'; + func.JS += INDENTATION + param.ident + '=' + deParen(asmCoercion(param.ident, param.type)) + ';\n'; }); + addVariable('label', 'i32', func); + + if (func.setjmpTable) { + addVariable('setjmpLabel', 'i32', func); + addVariable('setjmpTable', 'i32', func); + } + // spell out local variables - var vars = values(func.variables).filter(function(v) { return v.origin != 'funcparam' }); + var vars = values(func.variables).filter(function(v) { + return v.origin !== 'funcparam' && + (!isIllegalType(getImplementationType(v)) || v.ident.indexOf('$', 1) > 0); // not illegal, or a broken up illegal (we have illegal chunks explicitly anyhow) + }); if (vars.length > 0) { - var chunkSize = 8; + var chunkSize = 20; var chunks = []; var i = 0; while (i < vars.length) { @@ -608,22 +619,15 @@ function JSify(data, functionsOnly, givenFunctions) { } for (i = 0; i < chunks.length; i++) { func.JS += INDENTATION + 'var ' + chunks[i].map(function(v) { - var type = getImplementationType(v); - if (!isIllegalType(type) || v.ident.indexOf('$', 1) > 0) { // not illegal, or a broken up illegal - return v.ident + ' = ' + asmInitializer(type); //, func.variables[v.ident].impl); - } else { - return range(Math.ceil(getBits(type)/32)).map(function(i) { - return v.ident + '$' + i + '= 0'; - }).join(','); - } - }).join(', ') + ';\n'; + return v.ident + '=' + asmInitializer(getImplementationType(v)); //, func.variables[v.ident].impl); + }).join(',') + ';\n'; } } } - if (true) { // TODO: optimize away when not needed - if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */'; - func.JS += INDENTATION + 'var label = 0;\n'; + if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */'; + if (!ASM_JS) { + func.JS += INDENTATION + 'var label=0;\n'; } if (ASM_JS) { @@ -632,12 +636,12 @@ function JSify(data, functionsOnly, givenFunctions) { hasByVal = hasByVal || param.byVal; }); if (hasByVal) { - func.JS += INDENTATION + 'var tempParam = 0;\n'; + func.JS += INDENTATION + 'var tempParam=0;\n'; } } if (func.hasVarArgsCall) { - func.JS += INDENTATION + 'var tempVarArgs = 0;\n'; + func.JS += INDENTATION + 'var tempVarArgs=0;\n'; } // Prepare the stack, if we need one. If we have other stack allocations, force the stack to be set up. @@ -654,7 +658,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (param.byVal) { var type = removePointing(param.type); var typeInfo = Types.types[type]; - func.JS += INDENTATION + (ASM_JS ? '' : 'var ') + 'tempParam = ' + param.ident + '; ' + param.ident + ' = ' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' + + func.JS += INDENTATION + (ASM_JS ? '' : 'var ') + 'tempParam = ' + param.ident + '; ' + param.ident + '=' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' + makeCopyValues(param.ident, 'tempParam', typeInfo.flatSize, 'null', null, param.byVal) + ';\n'; } }); @@ -665,14 +669,14 @@ function JSify(data, functionsOnly, givenFunctions) { function walkBlock(block, indent) { if (!block) return ''; dprint('relooping', 'walking block: ' + block.type + ',' + block.entries + ' : ' + block.labels.length); - function getLabelLines(label, indent, relooping) { + function getLabelLines(label, relooping) { if (!label) return ''; var ret = ''; if ((LABEL_DEBUG >= 2) && functionNameFilterTest(func.ident)) { - ret += indent + "Module.print(INDENT + '" + func.ident + ":" + label.ident + "');\n"; + ret += INDENTATION + "Module.print(INDENT + '" + func.ident + ":" + label.ident + "');\n"; } if (EXECUTION_TIMEOUT > 0) { - ret += indent + 'if (Date.now() - START_TIME >= ' + (EXECUTION_TIMEOUT*1000) + ') throw "Timed out!" + (new Error().stack);\n'; + ret += INDENTATION + 'if (Date.now() - START_TIME >= ' + (EXECUTION_TIMEOUT*1000) + ') throw "Timed out!" + (new Error().stack);\n'; } if (PRINT_SPLIT_FILE_MARKER && Debugging.on && Debugging.getAssociatedSourceFile(label.lines[label.lines.length-1].lineNum)) { @@ -685,6 +689,7 @@ function JSify(data, functionsOnly, givenFunctions) { var i = 0; return ret + label.lines.map(function(line) { var JS = line.JS; + if (!relooping) JS = INDENTATION + JS; if (relooping && i == label.lines.length-1) { if (line.intertype == 'branch' || line.intertype == 'switch') { JS = ''; // just branching operations - done in the relooper, so nothing need be done here @@ -695,12 +700,10 @@ function JSify(data, functionsOnly, givenFunctions) { i++; // invoke instructions span two lines, and the debug info is located // on the second line, hence the +1 - return JS + (Debugging.on ? Debugging.getComment(line.lineNum + (line.intertype === 'invoke' ? 1 : 0)) : ''); - }) - .join('\n') - .split('\n') // some lines include line breaks - .map(function(line) { return indent + line }) - .join('\n'); + if (Debugging.on) JS += Debugging.getComment(line.lineNum + (line.intertype === 'invoke' ? 1 : 0)); + //assert(JS.indexOf('\n') < 0, JS); + return JS; + }).join('\n'); } var ret = ''; if (!RELOOP || func.forceEmulated) { // TODO: also if just 1 label? @@ -719,19 +722,19 @@ function JSify(data, functionsOnly, givenFunctions) { ret += 'dummy: 0'; ret += '};\n'; } else { - ret += 'var setjmpLabel = 0;\n'; - ret += 'var setjmpTable = ' + RuntimeGenerator.stackAlloc(4 * (MAX_SETJMPS + 1) * 2) + ';\n'; + ret += makeVarDef('setjmpLabel') + '=0;\n'; + ret += makeVarDef('setjmpTable') + '=' + RuntimeGenerator.stackAlloc(4 * (MAX_SETJMPS + 1) * 2) + ';\n'; ret += makeSetValue('setjmpTable', '0', '0', 'i32') + ';'; // initialize first entry to 0 } } - ret += indent + 'while(1) '; + ret += indent + 'while(1)'; if (func.setjmpTable && !ASM_JS) { ret += 'try { '; } - ret += 'switch(' + asmCoercion('label', 'i32') + ') {\n'; + ret += 'switch(' + asmCoercion('label', 'i32') + '){\n'; ret += block.labels.map(function(label) { - return indent + INDENTATION + 'case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n' - + getLabelLines(label, indent + INDENTATION + INDENTATION); + return INDENTATION + 'case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n' + + getLabelLines(label); }).join('\n') + '\n'; if (func.setjmpTable && ASM_JS) { // emit a label in which we write to the proper local variable, before jumping to the actual label @@ -748,8 +751,16 @@ function JSify(data, functionsOnly, givenFunctions) { if (func.setjmpTable && !ASM_JS) { ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }'; } + if (ASM_JS && func.returnType !== 'void') { + // Add a return + if (func.returnType in Runtime.FLOAT_TYPES) { + ret += ' return +0;\n'; + } else { + ret += ' return 0;\n'; + } + } } else { - ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent); + ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0]); } ret += '\n'; } else { @@ -764,7 +775,7 @@ function JSify(data, functionsOnly, givenFunctions) { // add blocks for (var i = 0; i < block.labels.length; i++) { var label = block.labels[i]; - var content = getLabelLines(label, '', true); + var content = getLabelLines(label, true); //printErr(func.ident + ' : ' + label.ident + ' : ' + content + '\n'); var last = label.lines[label.lines.length-1]; if (!last.signedIdent) { @@ -810,9 +821,17 @@ function JSify(data, functionsOnly, givenFunctions) { // Finalize function if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " INDENT = INDENT.substr(0, INDENT.length-2);\n"; // Ensure a return in a function with a type that returns, even if it lacks a return (e.g., if it aborts()) - if (RELOOP && func.lines.length > 0 && func.returnType != 'void') { - var returns = func.labels.filter(function(label) { return label.lines[label.lines.length-1].intertype == 'return' }).length; - if (returns == 0) func.JS += INDENTATION + 'return ' + asmCoercion('0', func.returnType); + if (RELOOP && ASM_JS && func.lines.length > 0 && func.returnType != 'void') { + var lastCurly = func.JS.lastIndexOf('}'); + var lastReturn = func.JS.lastIndexOf('return '); + if ((lastCurly < 0 && lastReturn < 0) || // no control flow, no return + (lastCurly >= 0 && lastReturn < lastCurly)) { // control flow, no return past last join + if (func.returnType in Runtime.FLOAT_TYPES) { + func.JS += ' return +0;\n'; + } else { + func.JS += ' return 0;\n'; + } + } } func.JS += '}\n'; @@ -898,6 +917,11 @@ function JSify(data, functionsOnly, givenFunctions) { // Function lines function valueHandler(item) { + if (item.vars) { + item.vars.forEach(function(v) { + addVariable(v[0], v[1]); + }); + } return item.ident; } function noopHandler(item) { @@ -959,7 +983,7 @@ function JSify(data, functionsOnly, givenFunctions) { var parts = label.split('|'); var trueLabel = parts[1] || ''; var oldLabel = parts[2] || ''; - var labelSetting = oldLabel ? 'label = ' + getLabelId(oldLabel) + ';' + + var labelSetting = oldLabel ? 'label=' + getLabelId(oldLabel) + ';' + (SHOW_LABELS ? ' /* to: ' + getOriginalLabelId(cleanLabel(oldLabel)) + ' */' : '') : ''; // TODO: optimize away the setting if (label[1] == 'R') { if (label[2] == 'N') { // BRNOL: break, no label setting @@ -980,7 +1004,7 @@ function JSify(data, functionsOnly, givenFunctions) { } } else { if (!labelIsVariable) label = getLabelId(label); - return pre + 'label = ' + label + ';' + (SHOW_LABELS ? ' /* to: ' + getOriginalLabelId(cleanLabel(label)) + ' */' : '') + ' break;'; + return pre + 'label=' + label + ';' + (SHOW_LABELS ? ' /* to: ' + getOriginalLabelId(cleanLabel(label)) + ' */' : '') + 'break;'; } } @@ -1002,14 +1026,14 @@ 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 (ASM_JS ? '' : '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 - var valueJSes = {}; + var map = {}; labelSets.forEach(function(labelSet) { deps[labelSet.ident] = {}; - valueJSes[labelSet.ident] = labelSet.valueJS; + map[labelSet.ident] = labelSet; }); labelSets.forEach(function(labelSet) { walkInterdata(labelSet.value, function mark(item) { @@ -1028,14 +1052,18 @@ function JSify(data, functionsOnly, givenFunctions) { } for (var i = 0; i < idents.length; i++) { if (keys(deps[idents[i]]).length == 0) { - post = 'var ' + idents[i] + ' = ' + valueJSes[idents[i]] + ';' + post; + post = idents[i] + '=' + map[idents[i]].valueJS + ';' + post; + if (!ASM_JS) post = 'var ' + post; + else addVariable(idents[i], map[idents[i]].value.type); remove(idents[i]); continue mainLoop; } } // If we got here, we have circular dependencies, and must break at least one. - pre += 'var ' + idents[0] + '$phi = ' + valueJSes[idents[0]] + ';'; - post += 'var ' + idents[0] + ' = ' + idents[0] + '$phi;'; + pre += makeVarDef(idents[0]) + '$phi=' + map[idents[0]].valueJS + ';'; + post += makeVarDef(idents[0]) + '=' + idents[0] + '$phi;'; + addVariable(idents[0] + '$phi', map[idents[0]].value.type); + addVariable(idents[0], map[idents[0]].value.type); remove(idents[0]); } return pre + post; @@ -1062,10 +1090,10 @@ function JSify(data, functionsOnly, givenFunctions) { var labelTrue = (item.labelTrueJS = getPhiSetsForLabel(phiSets, item.labelTrue)) + makeBranch(item.labelTrue, item.currLabelId); var labelFalse = (item.labelFalseJS = getPhiSetsForLabel(phiSets, item.labelFalse)) + makeBr |