diff options
-rwxr-xr-x | emscripten.py | 2 | ||||
-rw-r--r-- | src/compiler.js | 3 | ||||
-rw-r--r-- | src/intertyper.js | 134 | ||||
-rw-r--r-- | src/parseTools.js | 18 | ||||
-rw-r--r-- | src/settings.js | 2 | ||||
-rw-r--r-- | src/utility.js | 11 |
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); |