aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/eliminator/asm-eliminator-test-output.js7
-rw-r--r--tools/eliminator/asm-eliminator-test.js10
-rw-r--r--tools/eliminator/eliminator-test-output.js3
-rw-r--r--tools/eliminator/eliminator-test.js10
-rw-r--r--tools/file_packager.py23
-rw-r--r--tools/js-optimizer.js146
-rw-r--r--tools/response_file.py6
-rw-r--r--tools/shared.py45
8 files changed, 185 insertions, 65 deletions
diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js
index dda82047..434fbaf9 100644
--- a/tools/eliminator/asm-eliminator-test-output.js
+++ b/tools/eliminator/asm-eliminator-test-output.js
@@ -291,4 +291,11 @@ function watIf() {
if ($cmp38) {} else {}
}
}
+function select2($foundBase_0_off0) {
+ $foundBase_0_off0 = $foundBase_0_off0 | 0;
+ var $call24 = 0;
+ $call24 = MUST_RUN() | 0;
+ STACKTOP = sp;
+ return ($foundBase_0_off0 ? 0 : $call24) | 0;
+}
diff --git a/tools/eliminator/asm-eliminator-test.js b/tools/eliminator/asm-eliminator-test.js
index 6f426150..7ec277d5 100644
--- a/tools/eliminator/asm-eliminator-test.js
+++ b/tools/eliminator/asm-eliminator-test.js
@@ -362,5 +362,13 @@ function watIf() {
}
}
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7", "looop8", "multiloop", "multiloop2", "tempDouble2", "watIf"]
+function select2($foundBase_0_off0) {
+ $foundBase_0_off0 = $foundBase_0_off0 | 0;
+ var $call24 = 0, $retval_0 = 0;
+ $call24 = MUST_RUN() | 0;
+ $retval_0 = $foundBase_0_off0 ? 0 : $call24;
+ STACKTOP = sp;
+ return $retval_0 | 0;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7", "looop8", "multiloop", "multiloop2", "tempDouble2", "watIf", "select2"]
diff --git a/tools/eliminator/eliminator-test-output.js b/tools/eliminator/eliminator-test-output.js
index 1a6506ed..0171e99b 100644
--- a/tools/eliminator/eliminator-test-output.js
+++ b/tools/eliminator/eliminator-test-output.js
@@ -6119,4 +6119,7 @@ function intoCond() {
HEAP32[$115 >> 2] = $NumWords;
}
}
+function math(a, b, c, d) {
+ print(Math_imul(d) + (Math_fround(c) + (a + Math_abs(b))));
+}
diff --git a/tools/eliminator/eliminator-test.js b/tools/eliminator/eliminator-test.js
index ffad69ea..ef17b388 100644
--- a/tools/eliminator/eliminator-test.js
+++ b/tools/eliminator/eliminator-test.js
@@ -8852,5 +8852,13 @@ function intoCond() {
HEAP32[$504 >> 2] = $503;
}
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "c", "f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1", "_inflate", "_malloc", "_mallocNoU", "asm", "phi", "intoCond"]
+function math(a, b, c, d) {
+ var x, y, z, w;
+ x = a;
+ y = Math_abs(b);
+ z = Math_fround(c);
+ w = Math_imul(d);
+ print(x + y + z + w);
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "c", "f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1", "_inflate", "_malloc", "_mallocNoU", "asm", "phi", "intoCond", "math"]
diff --git a/tools/file_packager.py b/tools/file_packager.py
index 1d0ec447..3ba5b23f 100644
--- a/tools/file_packager.py
+++ b/tools/file_packager.py
@@ -11,7 +11,7 @@ data downloads.
Usage:
- file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--compress COMPRESSION_DATA] [--crunch[=X]] [--js-output=OUTPUT.js] [--no-force]
+ file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--compress COMPRESSION_DATA] [--crunch[=X]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--no-heap-copy]
--crunch=X Will compress dxt files to crn with quality level X. The crunch commandline tool must be present
and CRUNCH should be defined in ~/.emscripten that points to it. JS crunch decompressing code will
@@ -27,6 +27,10 @@ Usage:
--use-preload-cache Stores package in IndexedDB so that subsequent loads don't need to do XHR. Checks package version.
+ --no-heap-copy If specified, the preloaded filesystem is not copied inside the Emscripten HEAP, but kept in a separate typed array outside it.
+ The default, if this is not specified, is to embed the VFS inside the HEAP, so that mmap()ing files in it is a no-op.
+ Passing this flag optimizes for fread() usage, omitting it optimizes for mmap() usage.
+
Notes:
* The file packager generates unix-style file paths. So if you are on windows and a file is accessed at
@@ -43,7 +47,7 @@ from shared import Compression, execute, suffix, unsuffixed
from subprocess import Popen, PIPE, STDOUT
if len(sys.argv) == 1:
- print '''Usage: file_packager.py TARGET [--preload A...] [--embed B...] [--compress COMPRESSION_DATA] [--crunch[=X]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache]
+ print '''Usage: file_packager.py TARGET [--preload A...] [--embed B...] [--compress COMPRESSION_DATA] [--crunch[=X]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--no-heap-copy]
See the source for more details.'''
sys.exit(0)
@@ -70,7 +74,12 @@ crunch = 0
plugins = []
jsoutput = None
force = True
+# If set to True, IndexedDB (IDBFS in library_idbfs.js) is used to locally cache VFS XHR so that subsequent
+# page loads can read the data from the offline cache instead.
use_preload_cache = False
+# If set to True, the blob received from XHR is moved to the Emscripten HEAP, optimizing for mmap() performance.
+# If set to False, the XHR blob is kept intact, and fread()s etc. are performed directly to that data. This optimizes for minimal memory usage and fread() performance.
+no_heap_copy = True
for arg in sys.argv[1:]:
if arg == '--preload':
@@ -91,6 +100,8 @@ for arg in sys.argv[1:]:
force = False
elif arg == '--use-preload-cache':
use_preload_cache = True
+ elif arg == '--no-heap-copy':
+ no_heap_copy = False
elif arg.startswith('--js-output'):
jsoutput = arg.split('=')[1] if '=' in arg else None
elif arg.startswith('--crunch'):
@@ -414,12 +425,18 @@ for file_ in data_files:
if has_preloaded:
# Get the big archive and split it up
- use_data = '''
+ if no_heap_copy:
+ use_data = '''
// copy the entire loaded file into a spot in the heap. Files will refer to slices in that. They cannot be freed though.
var ptr = Module['_malloc'](byteArray.length);
Module['HEAPU8'].set(byteArray, ptr);
DataRequest.prototype.byteArray = Module['HEAPU8'].subarray(ptr, ptr+byteArray.length);
'''
+ else:
+ use_data = '''
+ // Reuse the bytearray from the XHR as the source for file reads.
+ DataRequest.prototype.byteArray = byteArray;
+'''
for file_ in data_files:
if file_['mode'] == 'preload':
use_data += ' DataRequest.prototype.requests["%s"].onload();\n' % (file_['dstpath'])
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 022bdf47..57ce0071 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -618,6 +618,7 @@ function simplifyExpressions(ast) {
if (asm) {
if (hasTempDoublePtr) {
+ var asmData = normalizeAsm(ast);
traverse(ast, function(node, type) {
if (type === 'assign') {
if (node[1] === true && node[2][0] === 'sub' && node[2][1][0] === 'name' && node[2][1][1] === 'HEAP32') {
@@ -642,7 +643,7 @@ function simplifyExpressions(ast) {
node[2][0] !== 'seq') { // avoid (x, y, z) which can be used for tempDoublePtr on doubles for alignment fixes
if (node[1][2][1][1] === 'HEAP32') {
node[1][3][1][1] = 'HEAPF32';
- return ['unary-prefix', '+', node[1][3]];
+ return makeAsmCoercion(node[1][3], detectAsmCoercion(node[2]));
} else {
node[1][3][1][1] = 'HEAP32';
return ['binary', '|', node[1][3], ['num', 0]];
@@ -686,7 +687,6 @@ function simplifyExpressions(ast) {
}
}
});
- var asmData = normalizeAsm(ast);
for (var v in bitcastVars) {
var info = bitcastVars[v];
// good variables define only one type, use only one type, have definitions and uses, and define as a different type than they use
@@ -1142,13 +1142,28 @@ function simplifyNotComps(ast) {
simplifyNotCompsPass = false;
}
-var NO_SIDE_EFFECTS = set('num', 'name');
+function callHasSideEffects(node) { // checks if the call itself (not the args) has side effects (or is not statically known)
+ return !(node[1][0] === 'name' && /^Math_/.test(node[1][1]));
+}
function hasSideEffects(node) { // this is 99% incomplete!
- if (node[0] in NO_SIDE_EFFECTS) return false;
- if (node[0] === 'unary-prefix') return hasSideEffects(node[2]);
- if (node[0] === 'binary') return hasSideEffects(node[2]) || hasSideEffects(node[3]);
- return true;
+ switch (node[0]) {
+ case 'num': case 'name': case 'string': return false;
+ case 'unary-prefix': return hasSideEffects(node[2]);
+ case 'binary': return hasSideEffects(node[2]) || hasSideEffects(node[3]);
+ case 'sub': return hasSideEffects(node[1]) || hasSideEffects(node[2]);
+ case 'call': {
+ if (callHasSideEffects(node)) return true;
+ // This is a statically known call, with no side effects. only args can side effect us
+ var args = node[2];
+ var num = args.length;
+ for (var i = 0; i < num; i++) {
+ if (hasSideEffects(args[i])) return true;
+ }
+ return false;
+ }
+ default: return true;
+ }
}
// Clear out empty ifs and blocks, and redundant blocks/stats and so forth
@@ -1515,21 +1530,33 @@ function unVarify(vars, ret) { // transform var x=1, y=2 etc. into (x=1, y=2), i
// annotations, plus explicit metadata) and denormalize (vice versa)
var ASM_INT = 0;
var ASM_DOUBLE = 1;
+var ASM_FLOAT = 2;
function detectAsmCoercion(node, asmInfo) {
// for params, +x vs x|0, for vars, 0.0 vs 0
if (node[0] === 'num' && node[1].toString().indexOf('.') >= 0) return ASM_DOUBLE;
if (node[0] === 'unary-prefix') return ASM_DOUBLE;
+ if (node[0] === 'call' && node[1][0] === 'name' && node[1][1] === 'Math_fround') return ASM_FLOAT;
if (asmInfo && node[0] == 'name') return getAsmType(node[1], asmInfo);
return ASM_INT;
}
function makeAsmCoercion(node, type) {
- return type === ASM_INT ? ['binary', '|', node, ['num', 0]] : ['unary-prefix', '+', node];
+ switch (type) {
+ case ASM_INT: return ['binary', '|', node, ['num', 0]];
+ case ASM_DOUBLE: return ['unary-prefix', '+', node];
+ case ASM_FLOAT: return ['call', ['name', 'Math_fround'], [node]];
+ default: throw 'wha? ' + JSON.stringify([node, type]) + new Error().stack;
+ }
}
function makeAsmVarDef(v, type) {
- return [v, type === ASM_INT ? ['num', 0] : ['unary-prefix', '+', ['num', 0]]];
+ switch (type) {
+ case ASM_INT: return [v, ['num', 0]];
+ case ASM_DOUBLE: return [v, ['unary-prefix', '+', ['num', 0]]];
+ case ASM_FLOAT: return [v, ['call', ['name', 'Math_fround'], [['num', 0]]]];
+ default: throw 'wha?';
+ }
}
function getAsmType(name, asmInfo) {
@@ -1568,7 +1595,8 @@ function normalizeAsm(func) {
var name = v[0];
var value = v[1];
if (!(name in data.vars)) {
- assert(value[0] === 'num' || (value[0] === 'unary-prefix' && value[2][0] === 'num')); // must be valid coercion no-op
+ assert(value[0] === 'num' || (value[0] === 'unary-prefix' && value[2][0] === 'num') // must be valid coercion no-op
+ || (value[0] === 'call' && value[1][0] === 'name' && value[1][1] === 'Math_fround'));
data.vars[name] = detectAsmCoercion(value);
v.length = 1; // make an un-assigning var
} else {
@@ -1917,7 +1945,7 @@ function registerize(ast) {
// we just use a fresh register to make sure we avoid this, but it could be
// optimized to check for safe registers (free, and not used in this loop level).
var varRegs = {}; // maps variables to the register they will use all their life
- var freeRegsClasses = asm ? [[], []] : []; // two classes for asm, one otherwise
+ var freeRegsClasses = asm ? [[], [], []] : []; // two classes for asm, one otherwise XXX - hardcoded length
var nextReg = 1;
var fullNames = {};
var loopRegs = {}; // for each loop nesting level, the list of bound variables
@@ -2031,6 +2059,33 @@ function registerize(ast) {
}
}
denormalizeAsm(fun, finalAsmData);
+ if (extraInfo && extraInfo.globals) {
+ // minify in asm var definitions, that denormalizeAsm just generated
+ function minify(value) {
+ if (value && value[0] === 'call' && value[1][0] === 'name') {
+ var name = value[1][1];
+ var minified = extraInfo.globals[name];
+ if (minified) {
+ value[1][1] = minified;
+ }
+ }
+ }
+ var stats = fun[3];
+ for (var i = 0; i < stats.length; i++) {
+ var line = stats[i];
+ if (i >= fun[2].length && line[0] !== 'var') break; // when we pass the arg and var coercions, break
+ if (line[0] === 'stat') {
+ assert(line[1][0] === 'assign');
+ minify(line[1][3]);
+ } else {
+ assert(line[0] === 'var');
+ var pairs = line[1];
+ for (var j = 0; j < pairs.length; j++) {
+ minify(pairs[j][1]);
+ }
+ }
+ }
+ }
}
});
}
@@ -2068,7 +2123,6 @@ function registerize(ast) {
// can happen in ALLOW_MEMORY_GROWTH mode
var ELIMINATION_SAFE_NODES = set('var', 'assign', 'call', 'if', 'toplevel', 'do', 'return', 'label', 'switch'); // do is checked carefully, however
-var NODES_WITHOUT_ELIMINATION_SIDE_EFFECTS = set('name', 'num', 'string', 'binary', 'sub', 'unary-prefix');
var IGNORABLE_ELIMINATOR_SCAN_NODES = set('num', 'toplevel', 'string', 'break', 'continue', 'dot'); // dot can only be STRING_TABLE.*
var ABORTING_ELIMINATOR_SCAN_NODES = set('new', 'object', 'function', 'defun', 'for', 'while', 'array', 'throw'); // we could handle some of these, TODO, but nontrivial (e.g. for while, the condition is hit multiple times after the body)
@@ -2160,7 +2214,7 @@ function eliminate(ast, memSafe) {
if (definitions[name] === 1 && uses[name] === 1) {
potentials[name] = 1;
} else if (uses[name] === 0 && (!definitions[name] || definitions[name] <= 1)) { // no uses, no def or 1 def (cannot operate on phis, and the llvm optimizer will remove unneeded phis anyhow) (no definition means it is a function parameter, or a local with just |var x;| but no defining assignment)
- var hasSideEffects = false;
+ var sideEffects = false;
var value = values[name];
if (value) {
// TODO: merge with other side effect code
@@ -2170,15 +2224,10 @@ function eliminate(ast, memSafe) {
if (!(value[0] === 'seq' && value[1][0] === 'assign' && value[1][2][0] === 'sub' && value[1][2][2][0] === 'binary' && value[1][2][2][1] === '>>' &&
value[1][2][2][2][0] === 'name' && value[1][2][2][2][1] === 'tempDoublePtr')) {
// If not that, then traverse and scan normally.
- traverse(value, function(node, type) {
- if (!(type in NODES_WITHOUT_ELIMINATION_SIDE_EFFECTS)) {
- hasSideEffects = true; // cannot remove this unused variable, constructing it has side effects
- return true;
- }
- });
+ sideEffects = hasSideEffects(value);
}
}
- if (!hasSideEffects) {
+ if (!sideEffects) {
varsToRemove[name] = !definitions[name] ? 2 : 1; // remove it normally
sideEffectFree[name] = true;
// Each time we remove a variable with 0 uses, if its value has no
@@ -2437,14 +2486,16 @@ function eliminate(ast, memSafe) {
for (var i = 0; i < args.length; i++) {
traverseInOrder(args[i]);
}
- // these two invalidations will also invalidate calls
- if (!globalsInvalidated) {
- invalidateGlobals();
- globalsInvalidated = true;
- }
- if (!memoryInvalidated) {
- invalidateMemory();
- memoryInvalidated = true;
+ if (callHasSideEffects(node)) {
+ // these two invalidations will also invalidate calls
+ if (!globalsInvalidated) {
+ invalidateGlobals();
+ globalsInvalidated = true;
+ }
+ if (!memoryInvalidated) {
+ invalidateMemory();
+ memoryInvalidated = true;
+ }
}
} else if (type === 'if') {
if (allowTracking) {
@@ -2485,6 +2536,10 @@ function eliminate(ast, memSafe) {
} else if (type === 'return') {
if (node[1]) traverseInOrder(node[1]);
} else if (type === 'conditional') {
+ if (!callsInvalidated) { // invalidate calls, since we cannot eliminate them into a branch of an LLVM select/JS conditional that does not execute
+ invalidateCalls();
+ callsInvalidated = true;
+ }
traverseInOrder(node[1]);
traverseInOrder(node[2]);
traverseInOrder(node[3]);
@@ -3169,7 +3224,7 @@ function outline(ast) {
var writes = {};
var namings = {};
- var hasReturn = false, hasReturnInt = false, hasReturnDouble = false, hasBreak = false, hasContinue = false;
+ var hasReturn = false, hasReturnType = {}, hasBreak = false, hasContinue = false;
var breaks = {}; // set of labels we break or continue
var continues = {}; // to (name -> id, just like labels)
var breakCapturers = 0;
@@ -3189,10 +3244,8 @@ function outline(ast) {
} else if (type == 'return') {
if (!node[1]) {
hasReturn = true;
- } else if (detectAsmCoercion(node[1]) == ASM_INT) {
- hasReturnInt = true;
} else {
- hasReturnDouble = true;
+ hasReturnType[detectAsmCoercion(node[1])] = true;
}
} else if (type == 'break') {
var label = node[1] || 0;
@@ -3222,7 +3275,6 @@ function outline(ast) {
continueCapturers--;
}
});
- assert(hasReturn + hasReturnInt + hasReturnDouble <= 1);
var reads = {};
for (var v in namings) {
@@ -3230,7 +3282,7 @@ function outline(ast) {
if (actualReads > 0) reads[v] = actualReads;
}
- return { writes: writes, reads: reads, hasReturn: hasReturn, hasReturnInt: hasReturnInt, hasReturnDouble: hasReturnDouble, hasBreak: hasBreak, hasContinue: hasContinue, breaks: breaks, continues: continues, labels: labels };
+ return { writes: writes, reads: reads, hasReturn: hasReturn, hasReturnType: hasReturnType, hasBreak: hasBreak, hasContinue: hasContinue, breaks: breaks, continues: continues, labels: labels };
}
function makeAssign(dst, src) {
@@ -3251,7 +3303,10 @@ function outline(ast) {
return ['switch', value, cases];
}
- var CONTROL_BREAK = 1, CONTROL_BREAK_LABEL = 2, CONTROL_CONTINUE = 3, CONTROL_CONTINUE_LABEL = 4, CONTROL_RETURN_VOID = 5, CONTROL_RETURN_INT = 6, CONTROL_RETURN_DOUBLE = 7;
+ var CONTROL_BREAK = 1, CONTROL_BREAK_LABEL = 2, CONTROL_CONTINUE = 3, CONTROL_CONTINUE_LABEL = 4, CONTROL_RETURN_VOID = 5, CONTROL_RETURN_INT = 6, CONTROL_RETURN_DOUBLE = 7, CONTROL_RETURN_FLOAT = 8;
+ function controlFromAsmType(asmType) {
+ return CONTROL_RETURN_INT + (asmType | 0); // assumes ASM_INT starts at 0, and order of these two is identical!
+ }
var sizeToOutline = null; // customized per function and as we make progress
function calculateThreshold(func, asmData) {
@@ -3310,7 +3365,7 @@ function outline(ast) {
});
// Generate new function
- if (codeInfo.hasReturn || codeInfo.hasReturnInt || codeInfo.hasReturnDouble || codeInfo.hasBreak || codeInfo.hasContinue) {
+ if (codeInfo.hasReturn || codeInfo.hasReturnType[ASM_INT] || codeInfo.hasReturnType[ASM_DOUBLE] || codeInfo.hasReturnType[ASM_FLOAT] || codeInfo.hasBreak || codeInfo.hasContinue) {
// we need to capture all control flow using a top-level labeled one-time loop in the outlined function
var breakCapturers = 0;
var continueCapturers = 0;
@@ -3335,7 +3390,7 @@ function outline(ast) {
ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', CONTROL_RETURN_VOID])]);
} else {
var type = detectAsmCoercion(node[1], asmData);
- ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', type == ASM_INT ? CONTROL_RETURN_INT : CONTROL_RETURN_DOUBLE])]);
+ ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', controlFromAsmType(type)])]);
ret.push(['stat', makeAssign(makeStackAccess(type, asmData.controlDataStackPos(outlineIndex)), node[1])]);
}
ret.push(['stat', ['break', 'OL']]);
@@ -3398,16 +3453,10 @@ function outline(ast) {
[['stat', ['return']]]
));
}
- if (codeInfo.hasReturnInt) {
+ for (var returnType in codeInfo.hasReturnType) {
reps.push(makeIf(
- makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_INT]),
- [['stat', ['return', makeAsmCoercion(['name', 'tempInt'], ASM_INT)]]]
- ));
- }
- if (codeInfo.hasReturnDouble) {
- reps.push(makeIf(
- makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_DOUBLE]),
- [['stat', ['return', makeAsmCoercion(['name', 'tempDouble'], ASM_DOUBLE)]]]
+ makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', controlFromAsmType(returnType)]),
+ [['stat', ['return', makeAsmCoercion(['name', 'tempInt'], returnType | 0)]]]
));
}
if (codeInfo.hasBreak) {
@@ -3486,8 +3535,9 @@ function outline(ast) {
var last = getStatements(func)[getStatements(func).length-1];
if (last[0] === 'stat') last = last[1];
if (last[0] !== 'return') {
- if (allCodeInfo.hasReturnInt || allCodeInfo.hasReturnDouble) {
- getStatements(func).push(['stat', ['return', makeAsmCoercion(['num', 0], allCodeInfo.hasReturnInt ? ASM_INT : ASM_DOUBLE)]]);
+ for (var returnType in codeInfo.hasReturnType) {
+ getStatements(func).push(['stat', ['return', makeAsmCoercion(['num', 0], returnType | 0)]]);
+ break;
}
}
outliningParents[newIdent] = func[1];
diff --git a/tools/response_file.py b/tools/response_file.py
index f19cf8af..7f916752 100644
--- a/tools/response_file.py
+++ b/tools/response_file.py
@@ -1,4 +1,5 @@
import tempfile, os, sys, shlex
+import shared
# Routes the given cmdline param list in args into a new response file and returns the filename to it.
# The returned filename has a suffix '.rsp'.
@@ -9,6 +10,11 @@ def create_response_file(args, directory):
args = map(lambda p: p.replace('\\', '\\\\').replace('"', '\\"'), args)
response_fd.write('"' + '" "'.join(args) + '"')
response_fd.close()
+
+ # Register the created .rsp file to be automatically cleaned up once this process finishes, so that
+ # caller does not have to remember to do it.
+ shared.configuration.get_temp_files().note(response_filename)
+
return response_filename
# Reads a response file, and returns the list of cmdline params found in the file.
diff --git a/tools/shared.py b/tools/shared.py
index d38aef4c..e2c6e89f 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -3,7 +3,7 @@ from subprocess import Popen, PIPE, STDOUT
from tempfile import mkstemp
from distutils.spawn import find_executable
import jsrun, cache, tempfiles
-from response_file import create_response_file
+import response_file
import logging, platform
def listify(x):
@@ -41,8 +41,8 @@ class WindowsPopen:
# emscripten.py supports reading args from a response file instead of cmdline.
# Use .rsp to avoid cmdline length limitations on Windows.
if len(args) >= 2 and args[1].endswith("emscripten.py"):
- self.response_filename = create_response_file(args[2:], TEMP_DIR)
- args = args[0:2] + ['@' + self.response_filename]
+ response_filename = response_file.create_response_file(args[2:], TEMP_DIR)
+ args = args[0:2] + ['@' + response_filename]
try:
# Call the process with fixed streams.
@@ -78,13 +78,6 @@ class WindowsPopen:
def kill(self):
return self.process.kill()
- def __del__(self):
- try:
- # Clean up the temporary response file that was used to spawn this process, so that we don't leave temp files around.
- tempfiles.try_delete(self.response_filename)
- except:
- pass # Mute all exceptions in dtor, particularly if we didn't use a response file, self.response_filename doesn't exist.
-
__rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
def path_from_root(*pathelems):
return os.path.join(__rootpath__, *pathelems)
@@ -314,7 +307,7 @@ def find_temp_directory():
# we re-check sanity when the settings are changed)
# We also re-check sanity and clear the cache when the version changes
-EMSCRIPTEN_VERSION = '1.7.2'
+EMSCRIPTEN_VERSION = '1.7.6'
def generate_sanity():
return EMSCRIPTEN_VERSION + '|' + get_llvm_target() + '|' + LLVM_ROOT
@@ -1214,7 +1207,13 @@ class Building:
# Run Emscripten
Settings.RELOOPER = Cache.get_path('relooper.js')
settings = Settings.serialize()
- compiler_output = jsrun.timeout_run(Popen([PYTHON, EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + settings + extra_args, stdout=PIPE), None, 'Compiling')
+ args = settings + extra_args
+ if WINDOWS:
+ args = ['@' + response_file.create_response_file(args, TEMP_DIR)]
+ cmdline = [PYTHON, EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + args
+ if jsrun.TRACK_PROCESS_SPAWNS:
+ logging.info('Executing emscripten.py compiler with cmdline "' + ' '.join(cmdline) + '"')
+ compiler_output = jsrun.timeout_run(Popen(cmdline, stdout=PIPE), None, 'Compiling')
#print compiler_output
# Detect compilation crashes and errors
@@ -1511,6 +1510,28 @@ class JS:
return ident.replace('%', '$').replace('@', '_')
@staticmethod
+ def make_initializer(sig, settings=None):
+ settings = settings or Settings
+ if sig == 'i':
+ return '0'
+ elif sig == 'f' and settings.get('PRECISE_F32'):
+ return 'Math_fround(0)'
+ else:
+ return '+0'
+
+ @staticmethod
+ def make_coercion(value, sig, settings=None):
+ settings = settings or Settings
+ if sig == 'i':
+ return value + '|0'
+ elif sig == 'f' and settings.get('PRECISE_F32'):
+ return 'Math_fround(' + value + ')'
+ elif sig == 'd' or sig == 'f':
+ return '+' + value
+ else:
+ return value
+
+ @staticmethod
def make_extcall(sig, named=True):
args = ','.join(['a' + str(i) for i in range(1, len(sig))])
args = 'index' + (',' if args else '') + args