aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemscripten.py2
-rw-r--r--src/compiler.js3
-rw-r--r--src/intertyper.js134
-rw-r--r--src/parseTools.js18
-rw-r--r--src/settings.js2
-rw-r--r--src/utility.js11
6 files changed, 156 insertions, 14 deletions
diff --git a/emscripten.py b/emscripten.py
index 1eb486ad..2d7b3daf 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -186,6 +186,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
if out and DEBUG: logging.debug(' loading pre from jcache')
if not out:
open(pre_file, 'w').write(pre_input)
+ #print >> sys.stderr, 'running', str([settings_file, pre_file, 'pre'] + libraries).replace("'/", "'") # see funcs
out = jsrun.run_js(compiler, compiler_engine, [settings_file, pre_file, 'pre'] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE,
cwd=path_from_root('src'))
assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?'
@@ -223,6 +224,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
funcs, chunk_size,
jcache.get_cachename('emscript_files') if jcache else None)
+ #sys.exit(1)
#chunks = [chunks[0]] # pick specific chunks for debugging/profiling
funcs = None
diff --git a/src/compiler.js b/src/compiler.js
index 94197390..e42f5e19 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -218,7 +218,7 @@ if (phase == 'funcs' && RELOOP) { // XXX handle !singlePhase
try {
load(RELOOPER);
} catch(e) {
- printErr('cannot find relooper at ' + RELOOPER + ', trying in current dir');
+ printErr('cannot load relooper at ' + RELOOPER + ' : ' + e + ', trying in current dir');
load('relooper.js');
}
assert(typeof Relooper != 'undefined');
@@ -275,6 +275,7 @@ function compile(raw) {
JSify(analyzed);
//dumpInterProf();
+ //printErr(phase + ' paths (fast, slow): ' + [fastPaths, slowPaths]);
phase = null;
diff --git a/src/intertyper.js b/src/intertyper.js
index 525096f5..07f2020c 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) {
@@ -984,16 +989,123 @@ 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') {
+ if (m = /^ (%[\w\d\._]+) = (getelementptr|load) ([%\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;
+ }
+ default: throw 'unexpected fast path type ' + intertype;
+ }
+ //else if (line.lineText.indexOf(' = load ') > 0) printErr('close: ' + JSON.stringify(line.lineText));
+ }
+ }
+ if (ret) {
+ if (COMPILER_ASSERTIONS) {
+ //printErr(['\n', JSON.stringify(ret), '\n', JSON.stringify(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 = 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';
- //interProf[type] = (interProf[type] || 0) + Date.now() - time;
+ /*
+ 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);
@@ -1002,10 +1114,12 @@ function intertyper(lines, sidePass, baseLineNums) {
return finalResults;
}
+// intertyper profiler
+
/*
var interProf = {};
function dumpInterProf() {
- printErr('\nintertyper/' + phase + ' : ' + JSON.stringify(keys(interProf).sort(function(x, y) { return interProf[y] - interProf[x] }).map(function(x) { return x + ':' + interProf[x] }), null, ' ') + '\n');
+ 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/parseTools.js b/src/parseTools.js
index c06d0a0d..470c246f 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -118,7 +118,14 @@ function isJSVar(ident) {
}
function isLocalVar(ident) {
- return ident[0] == '$';
+ return ident[0] === '$';
+}
+
+// Simple variables or numbers, or things already quoted, do not need to be quoted
+function needsQuoting(ident) {
+ if (/^[-+]?[$_]?[\w$_\d]*$/.test(ident)) return false; // number or variable
+ if (ident[0] === '(' && ident[ident.length-1] === ')' && ident.indexOf('(', 1) < 0) return false; // already fully quoted
+ return true;
}
function isStructPointerType(type) {
@@ -2000,7 +2007,7 @@ function makeSignOp(value, type, op, force, ignore) {
value = '1';
} else if (value === 'false') {
value = '0';
- } else if (!isJSVar(value)) value = '(' + value + ')';
+ } else if (needsQuoting(value)) value = '(' + value + ')';
if (bits === 32) {
if (op === 're') {
return '(' + value + '|0)';
@@ -2101,7 +2108,7 @@ function processMathop(item) {
if (item.params[i]) {
paramTypes[i] = item.params[i].type || type;
idents[i] = finalizeLLVMParameter(item.params[i]);
- if (!isNumber(idents[i]) && !isNiceIdent(idents[i])) {
+ if (needsQuoting(idents[i])) {
idents[i] = '(' + idents[i] + ')'; // we may have nested expressions. So enforce the order of operations we want
}
} else {
@@ -2540,3 +2547,8 @@ function makePrintChars(s, sep) {
return ret;
}
+function parseAlign(text) { // parse ", align \d+"
+ if (!text) return QUANTUM_SIZE;
+ return parseInt(text.substr(8));
+}
+
diff --git a/src/settings.js b/src/settings.js
index 0daafa35..0a4765fd 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -426,6 +426,8 @@ var JS_CHUNK_SIZE = 10240; // Used as a maximum size before breaking up expressi
var EXPORT_NAME = 'Module'; // Global variable to export the module as for environments without a standardized module
// loading system (e.g. the browser and SM shell).
+var COMPILER_ASSERTIONS = 0; // costly (slow) compile-time assertions
+
// Compiler debugging options
var DEBUG_TAGS_SHOWING = [];
// Some useful items:
diff --git a/src/utility.js b/src/utility.js
index 7d122cef..b793106c 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -334,6 +334,17 @@ function jsonCompare(x, y) {
return JSON.stringify(x) == JSON.stringify(y);
}
+function sortedJsonCompare(x, y) {
+ if (x === null || typeof x !== 'object') return x === y;
+ for (var i in x) {
+ if (!sortedJsonCompare(x[i], y[i])) return false;
+ }
+ for (var i in y) {
+ if (!sortedJsonCompare(x[i], y[i])) return false;
+ }
+ return true;
+}
+
function stringifyWithFunctions(obj) {
if (typeof obj === 'function') return obj.toString();
if (obj === null || typeof obj !== 'object') return JSON.stringify(obj);