aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js22
-rw-r--r--src/compiler.js10
-rw-r--r--src/experimental/allow_loopvars_from_memsetcpy_inasm.diff97
-rw-r--r--src/experimental/simplifyGeneratedFunctionsDetection.diff336
-rw-r--r--src/headless.js5
-rw-r--r--src/intertyper.js23
-rw-r--r--src/jsifier.js72
-rw-r--r--src/library.js501
-rw-r--r--src/library_browser.js80
-rw-r--r--src/library_gl.js228
-rw-r--r--src/library_openal.js471
-rw-r--r--src/library_sdl.js105
-rw-r--r--src/long.js18
-rw-r--r--src/modules.js7
-rw-r--r--src/parseTools.js86
-rw-r--r--src/preamble.js34
-rw-r--r--src/relooper/Relooper.cpp72
-rw-r--r--src/relooper/fuzzer.py4
-rw-r--r--src/relooper/test.txt4
-rw-r--r--src/runtime.js66
-rw-r--r--src/settings.js29
-rw-r--r--src/shell.html9
-rw-r--r--src/utility.js9
23 files changed, 1730 insertions, 558 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index ecb5ea6b..92b7d8cf 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -18,7 +18,7 @@ function recomputeLines(func) {
// Handy sets
var BRANCH_INVOKE = set('branch', 'invoke');
-var LABEL_ENDERS = set('branch', 'return');
+var LABEL_ENDERS = set('branch', 'return', 'switch');
var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic');
var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam');
@@ -231,9 +231,10 @@ function analyzer(data, sidePass) {
}
if (isIllegalType(item.valueType) || isIllegalType(item.type)) {
isIllegal = true;
- }
- if ((item.intertype == 'load' || item.intertype == 'store') && isStructType(item.valueType)) {
+ } else if ((item.intertype == 'load' || item.intertype == 'store') && isStructType(item.valueType)) {
isIllegal = true; // storing an entire structure is illegal
+ } else if (item.intertype == 'mathop' && item.op == 'trunc' && isIllegalType(item.params[1].ident)) { // trunc stores target value in second ident
+ isIllegal = true;
}
});
if (!isIllegal) {
@@ -290,7 +291,7 @@ function analyzer(data, sidePass) {
var elements = getLegalParams([item.value], bits)[0];
var j = 0;
elements.forEach(function(element) {
- var tempVar = '$st$' + i + '$' + j;
+ var tempVar = '$st$' + (tempId++) + '$' + j;
toAdd.push({
intertype: 'getelementptr',
assignTo: tempVar,
@@ -401,7 +402,7 @@ function analyzer(data, sidePass) {
var j = 0;
var toAdd = [];
elements.forEach(function(element) {
- var tempVar = '$st$' + i + '$' + j;
+ var tempVar = '$ld$' + (tempId++) + '$' + j;
toAdd.push({
intertype: 'getelementptr',
assignTo: tempVar,
@@ -652,13 +653,14 @@ function analyzer(data, sidePass) {
if (!isNumber(shifts)) {
// We can't statically legalize this, do the operation at runtime TODO: optimize
assert(sourceBits == 64, 'TODO: handle nonconstant shifts on != 64 bits');
+ assert(PRECISE_I64_MATH, 'Must have precise i64 math for non-constant 64-bit shifts');
+ Types.preciseI64MathUsed = 1;
value.intertype = 'value';
- value.ident = 'Runtime' + (ASM_JS ? '_' : '.') + 'bitshift64(' +
+ value.ident = 'var ' + value.assignTo + '$0 = _bitshift64' + value.op[0].toUpperCase() + value.op.substr(1) + '(' +
asmCoercion(sourceElements[0].ident, 'i32') + ',' +
asmCoercion(sourceElements[1].ident, 'i32') + ',' +
- Runtime['BITSHIFT64_' + value.op.toUpperCase()] + ',' +
asmCoercion(value.params[1].ident + '$0', 'i32') + ');' +
- 'var ' + value.assignTo + '$0 = ' + makeGetTempDouble(0, 'i32') + ', ' + value.assignTo + '$1 = ' + makeGetTempDouble(1, 'i32') + ';';
+ 'var ' + value.assignTo + '$1 = tempRet0;';
value.assignTo = null;
i++;
continue;
@@ -952,6 +954,7 @@ function analyzer(data, sidePass) {
// Function parameters
func.params.forEach(function(param) {
if (param.intertype !== 'varargs') {
+ if (func.variables[param.ident]) warn('cannot have duplicate variable names: ' + param.ident); // toNiceIdent collisions?
func.variables[param.ident] = {
ident: param.ident,
type: param.type,
@@ -965,6 +968,7 @@ function analyzer(data, sidePass) {
// Normal variables
func.lines.forEach(function(item, i) {
if (item.assignTo) {
+ if (func.variables[item.assignTo]) warn('cannot have duplicate variable names: ' + item.assignTo); // toNiceIdent collisions?
var variable = func.variables[item.assignTo] = {
ident: item.assignTo,
type: item.type,
@@ -1380,7 +1384,7 @@ function analyzer(data, sidePass) {
var label = func.labels[i];
for (var j = 0; j < label.lines.length; j++) {
var line = label.lines[j];
- if (line.intertype == 'call' && line.ident == setjmp) {
+ if ((line.intertype == 'call' || line.intertype == 'invoke') && line.ident == setjmp) {
// Add a new label
var oldIdent = label.ident;
var newIdent = func.labelIdCounter++;
diff --git a/src/compiler.js b/src/compiler.js
index 3047daf1..bb72c7dd 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -141,8 +141,13 @@ if (phase == 'pre') {
if (settings_file) {
var settings = JSON.parse(read(settings_file));
- for (setting in settings) {
- eval(setting + ' = ' + JSON.stringify(settings[setting]));
+ for (key in settings) {
+ var value = settings[key];
+ if (value[0] == '@') {
+ // response file type thing, workaround for large inputs: value is @path-to-file
+ value = JSON.parse(read(value.substr(1)));
+ }
+ eval(key + ' = ' + JSON.stringify(value));
}
}
@@ -163,6 +168,7 @@ if (SAFE_HEAP >= 2) {
EXPORTED_FUNCTIONS = set(EXPORTED_FUNCTIONS);
EXPORTED_GLOBALS = set(EXPORTED_GLOBALS);
EXCEPTION_CATCHING_WHITELIST = set(EXCEPTION_CATCHING_WHITELIST);
+DEAD_FUNCTIONS = numberedSet(DEAD_FUNCTIONS);
RUNTIME_DEBUG = LIBRARY_DEBUG || GL_DEBUG;
diff --git a/src/experimental/allow_loopvars_from_memsetcpy_inasm.diff b/src/experimental/allow_loopvars_from_memsetcpy_inasm.diff
new file mode 100644
index 00000000..a2dde7da
--- /dev/null
+++ b/src/experimental/allow_loopvars_from_memsetcpy_inasm.diff
@@ -0,0 +1,97 @@
+commit a61ef3dbbaf7333ad67fca29c0aad5bcc99b653a
+Author: Alon Zakai <alonzakai@gmail.com>
+Date: Wed Mar 6 18:18:03 2013 -0800
+
+ handle new vars in asm code, such as the loop vars from memset/memcpy loops
+
+diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
+index f2dc516..65059e8 100644
+--- a/tools/js-optimizer.js
++++ b/tools/js-optimizer.js
+@@ -1321,7 +1321,7 @@ 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
++ if (!(value[0] == 'num' || (value[0] == 'unary-prefix' && value[2][0] == 'num'))) break outer; // must be valid coercion no-op
+ data.vars[name] = detectAsmCoercion(value);
+ v.length = 1; // make an un-assigning var
+ } else {
+@@ -1331,6 +1331,7 @@ function normalizeAsm(func) {
+ i++;
+ }
+ // finally, look for other var definitions and collect them
++ var extra = [];
+ while (i < stats.length) {
+ traverse(stats[i], function(node, type) {
+ if (type == 'var') {
+@@ -1340,6 +1341,7 @@ function normalizeAsm(func) {
+ var value = v[1];
+ if (!(name in data.vars)) {
+ data.vars[name] = detectAsmCoercion(value);
++ extra.push(['var', [[name]]]); // add a 'var' for normal JS
+ }
+ }
+ unVarify(node[1], node);
+@@ -1353,6 +1355,7 @@ function normalizeAsm(func) {
+ });
+ i++;
+ }
++ if (extra.length > 0) stats.splice.apply(stats, [0, 0].concat(extra));
+ //printErr('normalized \n\n' + astToSrc(func) + '\n\nwith: ' + JSON.stringify(data));
+ return data;
+ }
+diff --git a/tools/test-js-optimizer-asm-regs-output.js b/tools/test-js-optimizer-asm-regs-output.js
+index 99bccd2..f84b8d5 100644
+--- a/tools/test-js-optimizer-asm-regs-output.js
++++ b/tools/test-js-optimizer-asm-regs-output.js
+@@ -9,6 +9,18 @@ function asm(d1, i2) {
+ d4 = d1 * 5;
+ return d4;
+ }
++function asm2(d1, i2) {
++ d1 = +d1;
++ i2 = i2 | 0;
++ var i3 = 0, d4 = +0;
++ i3 = i2;
++ i2 = d1 + i3 | 0;
++ d1 = d(Math_max(10, Math_min(5, f())));
++ i3 = i2 + 2 | 0;
++ print(i3);
++ d4 = d1 * 5;
++ return d4;
++}
+ function _doit(i1, i2, i3) {
+ i1 = i1 | 0;
+ i2 = i2 | 0;
+diff --git a/tools/test-js-optimizer-asm-regs.js b/tools/test-js-optimizer-asm-regs.js
+index 0afced2..fbaa7c4 100644
+--- a/tools/test-js-optimizer-asm-regs.js
++++ b/tools/test-js-optimizer-asm-regs.js
+@@ -10,6 +10,19 @@ function asm(x, y) {
+ double2 = double1*5;
+ return double2;
+ }
++function asm2(x, y) {
++ x = +x;
++ y = y | 0;
++ var int1 = 0, int2 = 0; // do not mix the types!
++ var double1 = +0, double2 = +0;
++ var tempy = y;
++ int1 = (x+tempy)|0;
++ double1 = d(Math.max(10, Math_min(5, f())));
++ int2 = (int1+2)|0;
++ print(int2);
++ double2 = double1*5;
++ return double2;
++}
+ function _doit($x, $y$0, $y$1) {
+ $x = $x | 0;
+ $y$0 = $y$0 | 0;
+@@ -41,5 +54,5 @@ function retf() {
+ }
+ // missing final return, need it as a float
+ }
+-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "_doit", "rett", "ret2t", "retf"]
++// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "asm2", "_doit", "rett", "ret2t", "retf"]
+
diff --git a/src/experimental/simplifyGeneratedFunctionsDetection.diff b/src/experimental/simplifyGeneratedFunctionsDetection.diff
new file mode 100644
index 00000000..09e0ebcd
--- /dev/null
+++ b/src/experimental/simplifyGeneratedFunctionsDetection.diff
@@ -0,0 +1,336 @@
+diff --git a/emscripten.py b/emscripten.py
+index b698654..a7843c7 100755
+--- a/emscripten.py
++++ b/emscripten.py
+@@ -294,8 +294,6 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
+ outfile.write(blockaddrsize(indexize(pre)))
+ pre = None
+
+- #if DEBUG: outfile.write('// funcs\n')
+-
+ # forward
+ forwarded_data = json.dumps(forwarded_json)
+ forwarded_file = temp_files.get('.2.json').name
+@@ -483,9 +481,14 @@ Runtime.stackRestore = function(top) { asm.stackRestore(top) };
+ else:
+ function_tables_defs = '\n'.join([table for table in last_forwarded_json['Functions']['tables'].itervalues()])
+ outfile.write(function_tables_defs)
++
++ outfile.write('// EMSCRIPTEN_START_FUNCS\n')
++
+ outfile.write(blockaddrsize(indexize(funcs_js)))
+ funcs_js = None
+
++ outfile.write('// EMSCRIPTEN_END_FUNCS\n')
++
+ outfile.write(indexize(post))
+ if DEBUG: print >> sys.stderr, ' emscript: phase 3 took %s seconds' % (time.time() - t)
+
+diff --git a/src/jsifier.js b/src/jsifier.js
+index ff58ece..d6aa110 100644
+--- a/src/jsifier.js
++++ b/src/jsifier.js
+@@ -1587,12 +1587,6 @@ function JSify(data, functionsOnly, givenFunctions) {
+
+ var shellParts = read(shellFile).split('{{BODY}}');
+ print(shellParts[1]);
+- // Print out some useful metadata (for additional optimizations later, like the eliminator)
+- if (EMIT_GENERATED_FUNCTIONS) {
+- print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) {
+- return IGNORED_FUNCTIONS.indexOf(func.ident) < 0;
+- })) + '\n');
+- }
+
+ PassManager.serialize();
+
+diff --git a/src/settings.js b/src/settings.js
+index 1bfcf92..99c83f4 100644
+--- a/src/settings.js
++++ b/src/settings.js
+@@ -336,8 +336,6 @@ var EXPLICIT_ZEXT = 0; // If 1, generate an explicit conversion of zext i1 to i3
+
+ var NECESSARY_BLOCKADDRS = []; // List of (function, block) for all block addresses that are taken.
+
+-var EMIT_GENERATED_FUNCTIONS = 0; // whether to emit the list of generated functions, needed for external JS optimization passes
+-
+ // Compiler debugging options
+ var DEBUG_TAGS_SHOWING = [];
+ // Some useful items:
+diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
+index f2dc516..9fa038c 100644
+--- a/tools/js-optimizer.js
++++ b/tools/js-optimizer.js
+@@ -140,16 +140,6 @@ var UNDEFINED_NODE = ['unary-prefix', 'void', ['num', 0]];
+ var TRUE_NODE = ['unary-prefix', '!', ['num', 0]];
+ var FALSE_NODE = ['unary-prefix', '!', ['num', 1]];
+
+-var GENERATED_FUNCTIONS_MARKER = '// EMSCRIPTEN_GENERATED_FUNCTIONS:';
+-var generatedFunctions = null;
+-function setGeneratedFunctions(metadata) {
+- var start = metadata.indexOf(GENERATED_FUNCTIONS_MARKER);
+- generatedFunctions = set(eval(metadata.substr(start + GENERATED_FUNCTIONS_MARKER.length)));
+-}
+-function isGenerated(ident) {
+- return ident in generatedFunctions;
+-}
+-
+ function srcToAst(src) {
+ return uglify.parser.parse(src);
+ }
+@@ -212,21 +202,9 @@ function traverse(node, pre, post, stack) {
+ return result;
+ }
+
+-// Only walk through the generated functions
+-function traverseGenerated(ast, pre, post, stack) {
+- assert(generatedFunctions);
+- traverse(ast, function(node) {
+- if (node[0] == 'defun' && isGenerated(node[1])) {
+- traverse(node, pre, post, stack);
+- return null;
+- }
+- });
+-}
+-
+-function traverseGeneratedFunctions(ast, callback) {
+- assert(generatedFunctions);
++function traverseFunctions(ast, callback) {
+ traverse(ast, function(node) {
+- if (node[0] == 'defun' && isGenerated(node[1])) {
++ if (node[0] == 'defun') {
+ callback(node);
+ return null;
+ }
+@@ -418,7 +396,7 @@ function simplifyExpressionsPre(ast) {
+ var rerun = true;
+ while (rerun) {
+ rerun = false;
+- traverseGenerated(ast, function process(node, type, stack) {
++ traverse(ast, function process(node, type, stack) {
+ if (type == 'binary' && node[1] == '|') {
+ if (node[2][0] == 'num' && node[3][0] == 'num') {
+ return ['num', node[2][1] | node[3][1]];
+@@ -455,7 +433,7 @@ function simplifyExpressionsPre(ast) {
+ }
+
+ // &-related optimizations
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ if (type == 'binary' && node[1] == '&' && node[3][0] == 'num') {
+ if (node[2][0] == 'num') return ['num', node[2][1] & node[3][1]];
+ var input = node[2];
+@@ -489,7 +467,7 @@ function simplifyExpressionsPre(ast) {
+
+ if (asm) {
+ // optimize num >> num, in asm we need this here since we do not run optimizeShifts
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ if (type == 'binary' && node[1] == '>>' && node[2][0] == 'num' && node[3][0] == 'num') {
+ node[0] = 'num';
+ node[1] = node[2][1] >> node[3][1];
+@@ -505,7 +483,7 @@ function simplifyExpressionsPre(ast) {
+ var rerun = true;
+ while (rerun) {
+ rerun = false;
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ if (type == 'binary' && node[1] == '+') {
+ if (node[2][0] == 'num' && node[3][0] == 'num') {
+ rerun = true;
+@@ -528,7 +506,7 @@ function simplifyExpressionsPre(ast) {
+
+ // if (x == 0) can be if (!x), etc.
+ function simplifyZeroComp(ast) {
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ var binary;
+ if (type == 'if' && (binary = node[1])[0] == 'binary') {
+ if ((binary[1] == '!=' || binary[1] == '!==') && binary[3][0] == 'num' && binary[3][1] == 0) {
+@@ -554,7 +532,7 @@ function simplifyExpressionsPre(ast) {
+ // TODO: when shifting a variable, if there are other uses, keep an unshifted version too, to prevent slowdowns?
+ function optimizeShiftsInternal(ast, conservative) {
+ var MAX_SHIFTS = 3;
+- traverseGeneratedFunctions(ast, function(fun) {
++ traverseFunctions(ast, function(fun) {
+ var funMore = true;
+ var funFinished = {};
+ while (funMore) {
+@@ -999,7 +977,7 @@ function vacuum(ast) {
+ } break;
+ }
+ }
+- traverseGeneratedFunctions(ast, function(node) {
++ traverseFunctions(ast, function(node) {
+ vacuumInternal(node);
+ simplifyNotComps(node);
+ });
+@@ -1021,7 +999,7 @@ function getStatements(node) {
+ // if (condition) { label == x } else ..
+ // We can hoist the multiple block into the condition, thus removing code and one 'if' check
+ function hoistMultiples(ast) {
+- traverseGeneratedFunctions(ast, function(node) {
++ traverseFunctions(ast, function(node) {
+ traverse(node, function(node, type) {
+ var statements = getStatements(node);
+ if (!statements) return;
+@@ -1135,7 +1113,7 @@ function hoistMultiples(ast) {
+ // if (..) { .. break|continue } else { .. }
+ // to
+ // if (..) { .. break|continue } ..
+- traverseGenerated(ast, function(container, type) {
++ traverse(ast, function(container, type) {
+ var statements = getStatements(container);
+ if (!statements) return;
+ for (var i = 0; i < statements.length; i++) {
+@@ -1168,7 +1146,7 @@ function loopOptimizer(ast) {
+ function passTwo(ast) {
+ var neededDos = [];
+ // Find unneeded labels
+- traverseGenerated(ast, function(node, type, stack) {
++ traverse(ast, function(node, type, stack) {
+ if (type == 'label' && node[2][0] in LOOP) {
+ // this is a labelled loop. we don't know if it's needed yet. Mark its label for removal for now now.
+ stack.push(node);
+@@ -1212,7 +1190,7 @@ function loopOptimizer(ast) {
+ // We return whether another pass is necessary
+ var more = false;
+ // Remove unneeded labels
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ if (type == 'label' && node[1][0] == '+') {
+ more = true;
+ var ident = node[1].substr(1);
+@@ -1227,13 +1205,13 @@ function loopOptimizer(ast) {
+ });
+ // Remove unneeded one-time loops. We need such loops if (1) they have a label, or (2) they have a direct break so they are in neededDos.
+ // First, add all labeled loops of this nature to neededDos
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ if (type == 'label' && node[2][0] == 'do') {
+ neededDos.push(node[2]);
+ }
+ });
+ // Remove unneeded dos, we know who they are now
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ if (type == 'do' && neededDos.indexOf(node) < 0) {
+ assert(jsonCompare(node[1], ['num', 0]), 'Trying to remove a one-time do loop that is not one of our generated ones.;');
+ more = true;
+@@ -1407,7 +1385,7 @@ function denormalizeAsm(func, data) {
+ // we still need the eliminator? Closure? And in what order? Perhaps just
+ // closure simple?
+ function registerize(ast) {
+- traverseGeneratedFunctions(ast, function(fun) {
++ traverseFunctions(ast, function(fun) {
+ if (asm) var asmData = normalizeAsm(fun);
+ // Add parameters as a first (fake) var (with assignment), so they get taken into consideration
+ var params = {}; // note: params are special, they can never share a register between them (see later)
+@@ -1671,7 +1649,7 @@ var ABORTING_ELIMINATOR_SCAN_NODES = set('new', 'object', 'function', 'defun', '
+
+ function eliminate(ast, memSafe) {
+ // Find variables that have a single use, and if they can be eliminated, do so
+- traverseGeneratedFunctions(ast, function(func, type) {
++ traverseFunctions(ast, function(func, type) {
+ if (asm) var asmData = normalizeAsm(func);
+ //printErr('eliminate in ' + func[1]);
+
+@@ -2218,9 +2196,6 @@ var passes = {
+ var src = read(arguments_[0]);
+ var ast = srcToAst(src);
+ //printErr(JSON.stringify(ast)); throw 1;
+-var metadata = src.split('\n').filter(function(line) { return line.indexOf(GENERATED_FUNCTIONS_MARKER) >= 0 })[0];
+-//assert(metadata, 'Must have EMSCRIPTEN_GENERATED_FUNCTIONS metadata');
+-if (metadata) setGeneratedFunctions(metadata);
+
+ arguments_.slice(1).forEach(function(arg) {
+ passes[arg](ast);
+diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py
+index 2fd2211..a253afb 100644
+--- a/tools/js_optimizer.py
++++ b/tools/js_optimizer.py
+@@ -41,17 +41,17 @@ def run_on_js(filename, passes, js_engine, jcache):
+ if os.linesep != '\n':
+ js = js.replace(os.linesep, '\n') # we assume \n in the splitting code
+
+- # Find suffix
+- suffix_marker = '// EMSCRIPTEN_GENERATED_FUNCTIONS'
+- suffix_start = js.find(suffix_marker)
+- suffix = ''
+- if suffix_start >= 0:
+- suffix = js[suffix_start:js.find('\n', suffix_start)] + '\n'
+- # if there is metadata, we will run only on the generated functions. If there isn't, we will run on everything.
+- generated = set(eval(suffix[len(suffix_marker)+1:]))
+-
+- if not suffix and jcache:
+- # JCache cannot be used without metadata, since it might reorder stuff, and that's dangerous since only generated can be reordered
++ # Find markers
++ start_marker = '// EMSCRIPTEN_START_FUNCS\n'
++ end_marker = '// EMSCRIPTEN_END_FUNCS\n'
++ start = js.find(start_marker)
++ end = js.find(end_marker)
++ assert (start >= 0) == (end >= 0), 'must have both markers or neither'
++ have_markers = start >= 0
++ # if there are markers, we will run only on the generated functions. If there isn't, we will run on everything.
++
++ if not have_markers and jcache:
++ # JCache cannot be used without markers, since it might reorder stuff, and that's dangerous since only generated can be reordered
+ # This means jcache does not work after closure compiler runs, for example. But you won't get much benefit from jcache with closure
+ # anyhow (since closure is likely the longest part of the build).
+ if DEBUG: print >>sys.stderr, 'js optimizer: no metadata, so disabling jcache'
+@@ -59,27 +59,10 @@ def run_on_js(filename, passes, js_engine, jcache):
+
+ # If we process only generated code, find that and save the rest on the side
+ func_sig = re.compile('( *)function (_[\w$]+)\(')
+- if suffix:
+- pos = 0
+- gen_start = 0
+- gen_end = 0
+- while 1:
+- m = func_sig.search(js, pos)
+- if not m: break
+- pos = m.end()
+- indent = m.group(1)
+- ident = m.group(2)
+- if ident in generated:
+- if not gen_start:
+- gen_start = m.start()
+- assert gen_start
+- gen_end = js.find('\n%s}\n' % indent, m.end()) + (3 + len(indent))
+- assert gen_end > gen_start
+- pre = js[:gen_start]
+- post = js[gen_end:]
+- if 'last' in passes:
+- post = post.replace(suffix, '') # no need to write out the metadata - nothing after us needs it
+- js = js[gen_start:gen_end]
++ if have_markers:
++ pre = js[:start + len(start_marker)] # includes start marker
++ post = js[end:] # includes end marker
++ js = js[start + len(start_marker):end]
+ else:
+ pre = ''
+ post = ''
+@@ -95,7 +78,7 @@ def run_on_js(filename, passes, js_engine, jcache):
+ if m:
+ ident = m.group(2)
+ else:
+- if suffix: continue # ignore whitespace
++ if have_markers: continue # ignore whitespace
+ ident = 'anon_%d' % i
+ assert ident
+ funcs.append((ident, func))
+@@ -131,7 +114,6 @@ def run_on_js(filename, passes, js_engine, jcache):
+ temp_file = temp_files.get('.jsfunc_%d.js' % i).name
+ f = open(temp_file, 'w')
+ f.write(chunk)
+- f.write(suffix)
+ f.close()
+ return temp_file
+ filenames = [write_chunk(chunks[i], i) for i in range(len(chunks))]
+@@ -169,7 +151,6 @@ def run_on_js(filename, passes, js_engine, jcache):
+ f.write(cached); # TODO: preserve order
+ f.write('\n')
+ f.write(post);
+- # No need to write suffix: if there was one, it is inside post which exists when suffix is there
+ f.write('\n')
+ f.close()
+
diff --git a/src/headless.js b/src/headless.js
index 8e847d27..d81fb5a3 100644
--- a/src/headless.js
+++ b/src/headless.js
@@ -537,7 +537,7 @@ var document = {
case /* GL_MAX_FRAGMENT_UNIFORM_VECTORS */ 0x8DFD: return 4096;
case /* GL_MAX_VARYING_VECTORS */ 0x8DFC: return 32;
case /* GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS */ 0x8B4D: return 32;
- default: throw 'getParameter ' + pname;
+ default: console.log('getParameter ' + pname + '?'); return 0;
}
},
getSupportedExtensions: function() {
@@ -686,6 +686,7 @@ var document = {
document.callEventListeners('pointerlockchange');
});
},
+ exitPointerLock: function(){},
style: {},
eventListeners: {},
addEventListener: document.addEventListener,
@@ -748,6 +749,8 @@ var document = {
body: {
appendChild: function(){},
},
+ exitPointerLock: function(){},
+ cancelFullScreen: function(){},
};
var alert = function(x) {
print(x);
diff --git a/src/intertyper.js b/src/intertyper.js
index 2103ecfa..57e3011d 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -122,16 +122,19 @@ function intertyper(data, sidePass, baseLineNums) {
SKIP_STACK_IN_SMALL = 0;
}
- unparsedBundles.push({
- intertype: 'unparsedFunction',
- // 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
- });
+ var ident = toNiceIdent(func.ident);
+ if (!(ident in DEAD_FUNCTIONS)) {
+ unparsedBundles.push({
+ intertype: 'unparsedFunction',
+ // We need this early, to know basic function info - ident, params, varargs
+ ident: ident,
+ params: func.params,
+ returnType: func.returnType,
+ hasVarArgs: func.hasVarArgs,
+ lineNum: currFunctionLineNum,
+ lines: currFunctionLines
+ });
+ }
currFunctionLines = [];
}
}
diff --git a/src/jsifier.js b/src/jsifier.js
index ff58ece2..4263618a 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -397,6 +397,20 @@ function JSify(data, functionsOnly, givenFunctions) {
}
});
+ function processLibraryFunction(snippet, ident) {
+ snippet = snippet.toString();
+ assert(snippet.indexOf('XXX missing C define') == -1,
+ 'Trying to include a library function with missing C defines: ' + ident + ' | ' + snippet);
+
+ // name the function; overwrite if it's already named
+ snippet = snippet.replace(/function(?:\s+([^(]+))?\s*\(/, 'function _' + ident + '(');
+ if (LIBRARY_DEBUG) {
+ snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.printErr("[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.printErr(" [ return:" + Runtime.prettyPrint(ret)); return ret; \n}';
+ }
+ return snippet;
+ }
+
// functionStub
substrate.addActor('FunctionStub', {
processItem: function(item) {
@@ -434,16 +448,7 @@ function JSify(data, functionsOnly, givenFunctions) {
snippet = stringifyWithFunctions(snippet);
} else if (typeof snippet === 'function') {
isFunction = true;
- snippet = snippet.toString();
- assert(snippet.indexOf('XXX missing C define') == -1,
- 'Trying to include a library function with missing C defines: ' + ident + ' | ' + snippet);
-
- // name the function; overwrite if it's already named
- snippet = snippet.replace(/function(?:\s+([^(]+))?\s*\(/, 'function _' + ident + '(');
- if (LIBRARY_DEBUG) {
- snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.printErr("[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.printErr(" [ return:" + Runtime.prettyPrint(ret)); return ret; \n}';
- }
+ snippet = processLibraryFunction(snippet, ident);
if (ASM_JS) Functions.libraryFunctions[ident] = 1;
}
@@ -600,6 +605,10 @@ function JSify(data, functionsOnly, givenFunctions) {
func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n';
+ if (PGO) {
+ func.JS += ' PGOMonitor.called["' + func.ident + '"] = 1;\n';
+ }
+
if (ASM_JS) {
// spell out argument types
func.params.forEach(function(param) {
@@ -731,7 +740,7 @@ function JSify(data, functionsOnly, givenFunctions) {
return indent + ' case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n'
+ getLabelLines(label, indent + ' ');
}).join('\n') + '\n';
- if (ASSERTIONS) ret += indent + ' default: assert(0, "bad label: " + label);\n';
+ if (ASSERTIONS) ret += indent + ' default: assert(0' + (ASM_JS ? '' : ', "bad label: " + label') + ');\n';
ret += indent + '}\n';
if (func.setjmpTable) {
ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }';
@@ -1312,6 +1321,8 @@ function JSify(data, functionsOnly, givenFunctions) {
} else {
callIdent = ident;
}
+ if (callIdent == '0') return 'abort(-2)';
+
var args = [];
var argsTypes = [];
var varargs = [];
@@ -1414,6 +1425,12 @@ function JSify(data, functionsOnly, givenFunctions) {
returnType = getReturnType(type);
}
+ if (callIdent in DEAD_FUNCTIONS) {
+ var ret = 'abort(' + DEAD_FUNCTIONS[callIdent] + ')';
+ if (ASM_JS) ret = asmCoercion(ret, returnType);
+ return ret;
+ }
+
if (byPointer) {
var sig = Functions.getSignature(returnType, argsTypes, hasVarArgs);
if (ASM_JS) {
@@ -1443,7 +1460,7 @@ function JSify(data, functionsOnly, givenFunctions) {
makeFuncLineActor('unreachable', function(item) {
if (ASSERTIONS) {
- return 'throw "Reached an unreachable!"';
+ return ASM_JS ? 'abort()' : 'throw "Reached an unreachable!"';
} else {
return ';';
}
@@ -1552,16 +1569,25 @@ function JSify(data, functionsOnly, givenFunctions) {
// This is the main 'post' pass. Print out the generated code that we have here, together with the
// rest of the output that we started to print out earlier (see comment on the
// "Final shape that will be created").
- if (CORRUPTION_CHECK) {
- assert(!ASM_JS); // cannot monkeypatch asm!
- print(processMacros(read('corruptionCheck.js')));
- }
if (PRECISE_I64_MATH && Types.preciseI64MathUsed) {
+ if (!INCLUDE_FULL_LIBRARY) {
+ ['i64Add', 'bitshift64Shl', 'bitshift64Lshr', 'bitshift64Ashr'].forEach(function(func) {
+ print(processLibraryFunction(LibraryManager.library[func], func)); // must be first to be close to generated code
+ Functions.implementedFunctions['_' + func] = LibraryManager.library[func + '__sig'];
+ });
+ }
+ print('// EMSCRIPTEN_END_FUNCS\n');
print(read('long.js'));
} else {
+ print('// EMSCRIPTEN_END_FUNCS\n');
print('// Warning: printing of i64 values may be slightly rounded! No deep i64 math used, so precise i64 code not included');
print('var i64Math = null;');
}
+
+ if (CORRUPTION_CHECK) {
+ assert(!ASM_JS); // cannot monkeypatch asm!
+ print(processMacros(read('corruptionCheck.js')));
+ }
if (HEADLESS) {
print('if (!ENVIRONMENT_IS_WEB) {');
print(read('headless.js').replace("'%s'", "'http://emscripten.org'").replace("'?%s'", "''").replace('%s,', 'null,').replace('%d', '0'));
@@ -1587,11 +1613,17 @@ function JSify(data, functionsOnly, givenFunctions) {
var shellParts = read(shellFile).split('{{BODY}}');
print(shellParts[1]);
- // Print out some useful metadata (for additional optimizations later, like the eliminator)
- if (EMIT_GENERATED_FUNCTIONS) {
- print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) {
+ // Print out some useful metadata
+ if (EMIT_GENERATED_FUNCTIONS || PGO) {
+ var generatedFunctions = JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) {
return IGNORED_FUNCTIONS.indexOf(func.ident) < 0;
- })) +