diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 33 | ||||
-rw-r--r-- | src/compiler.js | 2 | ||||
-rw-r--r-- | src/dlmalloc.c | 2 | ||||
-rw-r--r-- | src/experimental/noncallgraphprofiling.diff | 197 | ||||
-rw-r--r-- | src/include/emscripten.h | 16 | ||||
-rw-r--r-- | src/intertyper.js | 118 | ||||
-rw-r--r-- | src/jsifier.js | 90 | ||||
-rw-r--r-- | src/library.js | 611 | ||||
-rw-r--r-- | src/modules.js | 52 | ||||
-rw-r--r-- | src/parseTools.js | 90 | ||||
-rw-r--r-- | src/preamble.js | 108 | ||||
-rw-r--r-- | src/runtime.js | 11 | ||||
-rw-r--r-- | src/settings.js | 38 |
13 files changed, 946 insertions, 422 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index 824e7903..37285733 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -46,8 +46,10 @@ function analyzer(data) { // Functions & labels item.functions = []; var currLabelFinished; // Sometimes LLVM puts a branch in the middle of a label. We need to ignore all lines after that. + item.items.sort(function(a, b) { return a.lineNum - b.lineNum }); for (var i = 0; i < item.items.length; i++) { var subItem = item.items[i]; + assert(subItem.lineNum); if (subItem.intertype == 'function') { item.functions.push(subItem); subItem.endLineNum = null; @@ -58,7 +60,7 @@ function analyzer(data) { if (LLVM_STYLE == 'new' && item.items[i+1].intertype !== 'label') { item.items.splice(i+1, 0, { intertype: 'label', - ident: '_entry', + ident: toNiceIdent('%0'), lineNum: subItem.lineNum + '.5' }); } @@ -77,10 +79,10 @@ function analyzer(data) { currLabelFinished = true; } } else { - print('// WARNING: content after a branch in a label, line: ' + subItem.lineNum); + print('// WARNING: content after a branch in a label, line: ' + subItem.lineNum + '::' + dump(subItem)); } } else { - print("ERROR: what is this? " + JSON.stringify(subItem)); + throw "ERROR: what is this? " + JSON.stringify(subItem); } } delete item.items; @@ -93,6 +95,7 @@ function analyzer(data) { if (Types.types[type]) return; if (['internal', 'hidden', 'inbounds', 'void'].indexOf(type) != -1) return; if (Runtime.isNumberType(type)) return; + dprint('types', 'Adding type: ' + type); // 'blocks': [14 x %struct.X] etc. If this is a pointer, we need // to look at the underlying type - it was not defined explicitly @@ -107,6 +110,8 @@ function analyzer(data) { // 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) + Types.types[nonPointing] = { name_: nonPointing, fields: range(num).map(function() { return subType }), @@ -124,6 +129,21 @@ function analyzer(data) { return; } + // anonymous structure definition, for example |{ i32, i8*, void ()*, i32 }| + if (type[0] == '{' || type[0] == '<') { + type = nonPointing; + var packed = type[0] == '<'; + Types.types[type] = { + name_: type, + fields: splitTokenList(tokenize(type.substr(2 + packed, type.length - 4 - 2*packed)).tokens).map(function(segment) { + return segment[0].text; + }), + packed: packed, + lineNum: '?' + }; + return; + } + if (isPointerType(type)) return; if (['['].indexOf(type) != -1) return; Types.types[type] = { @@ -333,7 +353,7 @@ function analyzer(data) { // Decision time - var pointedType = removePointing(variable.type); + var pointedType = pointingLevels(variable.type) > 0 ? removePointing(variable.type) : null; if (variable.origin == 'getelementptr') { // Use our implementation that emulates pointers etc. // TODO Can we perhaps nativize some of these? However to do so, we need to discover their @@ -563,7 +583,7 @@ function analyzer(data) { func.labelsDict[label.ident] = label; func.labelIds[label.ident] = func.labelIdCounter++; }); - func.labelIds[toNiceIdent('%entry')] = -1; // entry is always -1 + func.labelIds[toNiceIdent('%0')] = -1; // entry is always -1 func.hasPhi = false; func.hasIndirectBr = false; @@ -575,8 +595,9 @@ function analyzer(data) { var remarkableLabelId = line.value.params[i].label; func.remarkableLabels.push(remarkableLabelId); var remarkableLabel = func.labelsDict[remarkableLabelId]; + assert(remarkableLabel); var lastLine = remarkableLabel.lines.slice(-1)[0]; - if (lastLine.value) { + if (lastLine.intertype === 'assign') { lastLine.value.currLabelId = remarkableLabelId; } else { lastLine.currLabelId = remarkableLabelId; diff --git a/src/compiler.js b/src/compiler.js index 9bf81ba4..1639d4e7 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -79,5 +79,7 @@ raw = null; // Do it +//dprint(JSON.stringify(C_DEFINES)); + JSify(analyzer(intertyper(lines))); diff --git a/src/dlmalloc.c b/src/dlmalloc.c index 672ca48d..aa37dc0d 100644 --- a/src/dlmalloc.c +++ b/src/dlmalloc.c @@ -2,7 +2,7 @@ #define __THROW #define __attribute_malloc__ #define __wur -#include "emscripten.h" + /* This is a version (aka dlmalloc) of malloc/free/realloc written by diff --git a/src/experimental/noncallgraphprofiling.diff b/src/experimental/noncallgraphprofiling.diff new file mode 100644 index 00000000..9bceeee9 --- /dev/null +++ b/src/experimental/noncallgraphprofiling.diff @@ -0,0 +1,197 @@ +diff --git a/src/jsifier.js b/src/jsifier.js +index da8c4db..2d606be 100644 +--- a/src/jsifier.js ++++ b/src/jsifier.js +@@ -432,12 +432,16 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { + func.JS = '\nfunction ' + func.ident + '(' + func.paramIdents.join(', ') + ') {\n'; + + if (PROFILE) { +- func.JS += ' if (PROFILING) { ' +- + 'var __parentProfilingNode__ = PROFILING_NODE; PROFILING_NODE = PROFILING_NODE.children["' + func.ident + '"]; ' +- + 'if (!PROFILING_NODE) __parentProfilingNode__.children["' + func.ident + '"] = PROFILING_NODE = { time: 0, children: {}, calls: 0 };' +- + 'PROFILING_NODE.calls++; ' +- + 'var __profilingStartTime__ = Date.now() ' +- + '}\n'; ++ if (PROFILE_CALLGRAPH) { ++ func.JS += ' if (PROFILING) { ' ++ + 'var __parentProfilingNode__ = PROFILING_NODE; PROFILING_NODE = PROFILING_NODE.children["' + func.ident + '"]; ' ++ + 'if (!PROFILING_NODE) __parentProfilingNode__.children["' + func.ident + '"] = PROFILING_NODE = { time: 0, children: {}, calls: 0 };' ++ + 'PROFILING_NODE.calls++; ' ++ + 'var __profilingStartTime__ = Date.now() ' ++ + '}\n'; ++ } else { ++ func.JS += ' PROFILING_DATA[' + Profiling.getIndex(func.ident) + '] -= Date.now();'; ++ } + } + + func.JS += ' ' + RuntimeGenerator.stackEnter(func.initialStack) + ';\n'; +@@ -733,10 +737,14 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { + makeFuncLineActor('return', function(item) { + var ret = RuntimeGenerator.stackExit(item.funcData.initialStack) + ';\n'; + if (PROFILE) { +- ret += 'if (PROFILING) { ' +- + 'PROFILING_NODE.time += Date.now() - __profilingStartTime__; ' +- + 'PROFILING_NODE = __parentProfilingNode__ ' +- + '}\n'; ++ if (PROFILE_CALLGRAPH) { ++ ret += 'if (PROFILING) { ' ++ + 'PROFILING_NODE.time += Date.now() - __profilingStartTime__; ' ++ + 'PROFILING_NODE = __parentProfilingNode__ ' ++ + '}\n'; ++ } else { ++ ret += 'PROFILING_DATA[' + Profiling.getIndex(item.funcData.ident) + '] += Date.now();' ++ } + } + if (LABEL_DEBUG) { + ret += "print(INDENT + 'Exiting: " + item.funcData.ident + "');\n" +@@ -945,6 +953,9 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { + print(shellParts[0]); + var preFile = BUILD_AS_SHARED_LIB ? 'preamble_sharedlib.js' : 'preamble.js'; + var pre = processMacros(preprocess(read(preFile).replace('{{RUNTIME}}', getRuntime()), CONSTANTS)); ++ if (PROFILE && !PROFILE_CALLGRAPH) { ++ pre = pre.replace('{{PROFILING_DATA}}', Profiling.generateIndexing()); ++ } + print(pre); + print('Runtime.QUANTUM_SIZE = ' + QUANTUM_SIZE); + if (RUNTIME_TYPE_INFO) { +diff --git a/src/modules.js b/src/modules.js +index 20f42f9..624c5ae 100644 +--- a/src/modules.js ++++ b/src/modules.js +@@ -226,13 +226,15 @@ var Functions = { + + indexedFunctions: [0, 0], // Start at a non-0 (even, see below) value + ++ skip: true, ++ + // Mark a function as needing indexing, and returns the index + getIndex: function(ident) { + var key = this.indexedFunctions.indexOf(ident); + if (key < 0) { + key = this.indexedFunctions.length; + this.indexedFunctions[key] = ident; +- this.indexedFunctions[key+1] = 0; // Need to have keys be even numbers, see |polymorph| test ++ if (this.skip) this.indexedFunctions[key+1] = 0; // Need to have keys be even numbers, see |polymorph| test + } + return key.toString(); + }, +@@ -281,3 +283,19 @@ var LibraryManager = { + } + }; + ++var Profiling = { // We use the same principle of function hashing as in Functions ++ currFunctions: [], ++ implementedFunctions: null, ++ indexedFunctions: [], ++ getIndex: Functions.getIndex, ++ ++ generateIndexing: function() { ++ var ret = 'var PROFILING_DATA = new Array(' + this.indexedFunctions.length + ');\n' ++ + 'for (var i = 0; i < ' + this.indexedFunctions.length + '; i++) {\n' ++ + ' PROFILING_DATA[i] = 0;\n' ++ + '}\n' ++ + 'var PROFILING_NAMES = ' + JSON.stringify(this.indexedFunctions) + ';\n'; ++ return ret; ++ } ++}; ++ +diff --git a/src/preamble.js b/src/preamble.js +index 1c1ec91..1bda448 100644 +--- a/src/preamble.js ++++ b/src/preamble.js +@@ -276,22 +276,26 @@ var START_TIME = Date.now(); + + #if PROFILE + var PROFILING = 0; ++ ++#if PROFILE_CALLGRAPH + var PROFILING_ROOT = { time: 0, children: {}, calls: 0 }; + var PROFILING_NODE; +- + function startProfiling() { + PROFILING_NODE = PROFILING_ROOT; + PROFILING = 1; + } + Module['startProfiling'] = startProfiling; +- + function stopProfiling() { + PROFILING = 0; + assert(PROFILING_NODE === PROFILING_ROOT, 'Must have popped all the profiling call stack'); + } + Module['stopProfiling'] = stopProfiling; ++#else ++{{PROFILING_DATA}} ++#endif + + function printProfiling() { ++#if PROFILE_CALLGRAPH + function dumpData(name_, node, indent) { + print(indent + ('________' + node.time).substr(-8) + ': ' + name_ + ' (' + node.calls + ')'); + var children = []; +@@ -303,9 +307,20 @@ function printProfiling() { + children.forEach(function(child) { dumpData(child.name_, child, indent + ' ') }); + } + dumpData('root', PROFILING_ROOT, ' '); ++#else ++ var items = []; ++ for (var i = 0; i < PROFILING_NAMES.length; i++) { ++ items.push({ name_: PROFILING_NAMES[i], time: PROFILING_DATA[i] }); ++ } ++ items.sort(function(x, y) { return y.time - x.time }); ++ items.forEach(function(item) { ++ print(('________' + item.time).substr(-8) + ': ' + item.name_); ++ }); ++#endif + } + Module['printProfiling'] = printProfiling; + ++#if PROFILE_CALLGRAPH + function printXULProfiling() { + function dumpData(name_, node, indent) { + var children = []; +@@ -357,6 +372,7 @@ function printXULProfiling() { + } + Module['printXULProfiling'] = printXULProfiling; + #endif ++#endif + + //======================================== + // Runtime essentials +diff --git a/src/settings.js b/src/settings.js +index ef8b399..749468b 100644 +--- a/src/settings.js ++++ b/src/settings.js +@@ -114,7 +114,8 @@ AUTO_OPTIMIZE = 0; // When run with the CHECK_* options, will not fail on errors + // checking enabled and which do not, that is, this is a way to automate the + // generation of line data for CORRECT_*_LINES options + +-PROFILE = 0; // Enables runtime profiling. See test_profiling for a usage example. ++PROFILE = 1; // Enables runtime profiling. As lightweight as possible. ++PROFILE_CALLGRAPH = 0; // Much heavier profiling, of entire callgraphs. + + EXPORTED_FUNCTIONS = ['_main']; // Functions that are explicitly exported, so they are guaranteed to + // be accessible outside of the generated code. +diff --git a/tests/runner.py b/tests/runner.py +index f7ae9b0..3caedad 100644 +--- a/tests/runner.py ++++ b/tests/runner.py +@@ -3525,16 +3525,16 @@ if 'benchmark' not in str(sys.argv): + def post(filename): + src = open(filename, 'a') + src.write(''' +- startProfiling(); ++ //startProfiling(); + run(); +- stopProfiling(); ++ //stopProfiling(); + printProfiling(); + print('*ok*'); + ''') + src.close() + + # Using build_ll_hook forces a recompile, which leads to DFE being done even without opts +- self.do_test(src, ': __Z6inner1i (5000)\n*ok*', post_build=post) ++ self.do_test(src, ': __Z6inner2i\n*ok*', post_build=post) + + ### Integration tests + diff --git a/src/include/emscripten.h b/src/include/emscripten.h deleted file mode 100644 index b06f3781..00000000 --- a/src/include/emscripten.h +++ /dev/null @@ -1,16 +0,0 @@ -/** - * This file contains a few useful things for compiling C/C++ code - * with Emscripten, an LLVM-to-JavaScript compiler. - * - * The code can be used permissively under the MIT license. - * - * http://emscripten.org - */ - -#undef __i386__ -#undef __x86_64__ - -// Interface to the underlying JS engine. This function will -// eval() the given script. -extern void emscripten_run_script(const char *script); - diff --git a/src/intertyper.js b/src/intertyper.js index 83a49645..74f38e3a 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -2,6 +2,9 @@ // to be processed by the later stages. var tokenizer; // TODO: Clean this up/out +function tokenize(text) { + return tokenizer.processItem({ lineText: text }, true); +} //! @param parseFunctions We parse functions only on later passes, since we do not //! want to parse all of them at once, and have all their @@ -48,8 +51,11 @@ function intertyper(data, parseFunctions, baseLineNum) { currFunctionLineNum = i + 1; } if (!inFunction || parseFunctions) { - if (inContinual || new RegExp(/^\ +to.*/g).test(line)) { - // to after invoke + if (inContinual || new RegExp(/^\ +to.*/g).test(line) + || new RegExp(/^\ +catch .*/g).test(line) + || new RegExp(/^\ +filter .*/g).test(line) + || new RegExp(/^\ +cleanup.*/g).test(line)) { + // to after invoke or landingpad second line ret.slice(-1)[0].lineText += line; if (new RegExp(/^\ +\]/g).test(line)) { // end of llvm switch inContinual = false; @@ -137,13 +143,15 @@ function intertyper(data, parseFunctions, baseLineNum) { // merge certain tokens if (lastToken && isType(lastToken.text) && isFunctionDef(token)) { lastToken.text += ' ' + text; - } else if (lastToken && text[text.length-1] == '}') { + } else if (lastToken && /^}\**$/.exec(text)) { // }, }*, etc. var openBrace = tokens.length-1; - while (tokens[openBrace].text != '{') openBrace --; + while (tokens[openBrace].text.substr(-1) != '{') openBrace --; token = combineTokens(tokens.slice(openBrace+1)); tokens.splice(openBrace, tokens.length-openBrace+1); tokens.push(token); token.type = '{'; + token.text = '{ ' + token.text + ' }'; + while (pointingLevels(text) > pointingLevels(token.text)) token.text += '*'; // TODO: optimize lastToken = token; } else { tokens.push(token); @@ -265,6 +273,8 @@ function intertyper(data, parseFunctions, baseLineNum) { return 'Unreachable'; if (tokensLength >= 3 && token0Text == 'indirectbr') return 'IndirectBr'; + if (tokensLength >= 2 && token0Text == 'resume') + return 'Resume'; } else if (item.indent === -1) { if (tokensLength >= 3 && (token0Text == 'load' || token1Text == 'load')) @@ -280,8 +290,12 @@ function intertyper(data, parseFunctions, baseLineNum) { return 'Alloca'; if (tokensLength >= 3 && token0Text == 'extractvalue') return 'ExtractValue'; + if (tokensLength >= 3 && token0Text == 'insertvalue') + return 'InsertValue'; if (tokensLength >= 3 && token0Text == 'phi') return 'Phi'; + if (tokensLength >= 3 && token0Text == 'landingpad') + return 'Landingpad'; } else if (item.indent === 0) { if ((tokensLength >= 1 && token0Text.substr(-1) == ':') || // LLVM 2.7 format, or llvm-gcc in 2.8 (tokensLength >= 3 && token1Text == '<label>')) @@ -320,6 +334,7 @@ function intertyper(data, parseFunctions, baseLineNum) { processItem: function(item) { function scanConst(value, type) { //dprint('inter-const: ' + item.lineNum + ' : ' + JSON.stringify(value) + ',' + type + '\n'); + Types.needAnalysis[type] = 0; if (Runtime.isNumberType(type) || pointingLevels(type) >= 1) { return { value: toNiceIdent(value.text), type: type }; } else if (value.text in set('zeroinitializer', 'undef')) { // undef doesn't really need initting, but why not @@ -334,16 +349,21 @@ function intertyper(data, parseFunctions, baseLineNum) { if (segment[1].text == 'null') { return { intertype: 'value', value: 0, type: 'i32' }; } else if (segment[1].text == 'zeroinitializer') { + Types.needAnalysis[segment[0].text] = 0; return { intertype: 'emptystruct', type: segment[0].text }; } else if (segment[1].text in PARSABLE_LLVM_FUNCTIONS) { return parseLLVMFunctionCall(segment); } else if (segment[1].type && segment[1].type == '{') { + Types.needAnalysis[segment[0].text] = 0; return { intertype: 'struct', type: segment[0].text, contents: handleSegments(segment[1].tokens) }; } else if (segment[1].type && segment[1].type == '<') { + Types.needAnalysis[segment[0].text] = 0; return { intertype: 'struct', type: segment[0].text, contents: handleSegments(segment[1].item.tokens[0].tokens) }; } else if (segment[1].type && segment[1].type == '[') { + Types.needAnalysis[segment[0].text] = 0; return { intertype: 'list', type: segment[0].text, contents: handleSegments(segment[1].item.tokens) }; } else if (segment.length == 2) { + Types.needAnalysis[segment[0].text] = 0; return { intertype: 'value', type: segment[0].text, value: toNiceIdent(segment[1].text) }; } else if (segment[1].text === 'c') { // string @@ -377,16 +397,20 @@ function intertyper(data, parseFunctions, baseLineNum) { } } + cleanOutTokens(LLVM.VISIBILITIES, item.tokens, 2); if (item.tokens[2].text == 'alias') { - cleanOutTokensSet(LLVM.LINKAGES, item.tokens, 3); - cleanOutTokensSet(LLVM.VISIBILITIES, item.tokens, 3); - return [{ + cleanOutTokens(LLVM.LINKAGES, item.tokens, 3); + cleanOutTokens(LLVM.VISIBILITIES, item.tokens, 3); + var last = getTokenIndexByText(item.tokens, ';'); + var ret = { intertype: 'alias', ident: toNiceIdent(item.tokens[0].text), - aliasee: toNiceIdent(item.tokens[4].text), - type: item.tokens[3].text, + value: parseLLVMSegment(item.tokens.slice(3, last)), lineNum: item.lineNum - }]; + }; + ret.type = ret.value.type; + Types.needAnalysis[ret.type] = 0; + return [ret]; } if (item.tokens[2].text == 'type') { var fields = []; @@ -397,7 +421,7 @@ function intertyper(data, parseFunctions, baseLineNum) { } else if (item.tokens[3].text != 'opaque') { if (item.tokens[3].type == '<') { packed = true; - item.tokens[3] = tokenizer.processItem({ lineText: '{ ' + item.tokens[3].item.tokens[0].text + ' }' }, true).tokens[0]; + item.tokens[3] = item.tokens[3].item.tokens[0]; } var subTokens = item.tokens[3].tokens; subTokens.push({text:','}); @@ -418,13 +442,13 @@ function intertyper(data, parseFunctions, baseLineNum) { } else { // variable var ident = item.tokens[0].text; - cleanOutTokensSet(LLVM.GLOBAL_MODIFIERS, item.tokens, 3); - cleanOutTokensSet(LLVM.GLOBAL_MODIFIERS, item.tokens, 2); + cleanOutTokens(LLVM.GLOBAL_MODIFIERS, item.tokens, [2, 3]); var external = false; if (item.tokens[2].text === 'external') { external = true; item.tokens.splice(2, 1); } + Types.needAnalysis[item.tokens[2].text] = 0; var ret = { intertype: 'globalVariable', ident: toNiceIdent(ident), @@ -515,7 +539,7 @@ function intertyper(data, parseFunctions, baseLineNum) { substrate.addActor('Load', { processItem: function(item) { item.intertype = 'load'; - if (item.tokens[0].text == 'volatile') item.tokens.shift(0); + cleanOutTokens(LLVM.ACCESS_OPTIONS, item.tokens, [0, 1]); item.pointerType = item.tokens[1].text; item.valueType = item.type = removePointing(item.pointerType); Types.needAnalysis[item.type] = 0; @@ -537,6 +561,20 @@ function intertyper(data, parseFunctions, baseLineNum) { this.forwardItem(item, 'Reintegrator'); } }); + // 'insertvalue' + substrate.addActor('InsertValue', { + processItem: function(item) { + var last = getTokenIndexByText(item.tokens, ';'); + item.intertype = 'insertvalue'; + item.type = item.tokens[1].text; // Of the origin aggregate, as well as the result + Types.needAnalysis[item.type] = 0; + item.ident = toNiceIdent(item.tokens[2].text); + var segments = splitTokenList(item.tokens.slice(4, last)); + item.value = parseLLVMSegment(segments[0]); + item.indexes = segments.slice(1); + this.forwardItem(item, 'Reintegrator'); + } + }); // 'bitcast' substrate.addActor('Bitcast', { processItem: function(item) { @@ -556,6 +594,7 @@ function intertyper(data, parseFunctions, baseLineNum) { processItem: function(item) { var first = 0; while (!isType(item.tokens[first].text)) first++; + Types.needAnalysis[item.tokens[first].text] = 0; var last = getTokenIndexByText(item.tokens, ';'); var segment = [ item.tokens[first], { text: 'getelementptr' }, null, { item: { tokens: item.tokens.slice(first, last) @@ -574,7 +613,6 @@ function intertyper(data, parseFunctions, baseLineNum) { if (['tail'].indexOf(item.tokens[0].text) != -1) { item.tokens.splice(0, 1); } - assertEq(item.tokens[0].text, type); while (item.tokens[1].text in LLVM.PARAM_ATTR || item.tokens[1].text in LLVM.CALLING_CONVENTIONS) { item.tokens.splice(1, 1); } @@ -607,26 +645,47 @@ function intertyper(data, parseFunctions, baseLineNum) { } item.ident = toNiceIdent(item.ident); if (type === 'invoke') { - cleanOutTokens(['alignstack', 'alwaysinline', 'inlinehint', 'naked', 'noimplicitfloat', 'noinline', 'alwaysinline attribute.', 'noredzone', 'noreturn', 'nounwind', 'optsize', 'readnone', 'readonly', 'ssp', 'sspreq'], item.tokens, 4); - item.toLabel = toNiceIdent(item.tokens[6].text); - item.unwindLabel = toNiceIdent(item.tokens[9].text); + var toIndex = findTokenText(item, 'to'); + item.toLabel = toNiceIdent(item.tokens[toIndex+2].text); + item.unwindLabel = toNiceIdent(item.tokens[toIndex+5].text); + assert(item.toLabel && item.unwindLabel); } if (item.indent == 2) { // standalone call - not in assign item.standalone = true; - return [item]; + return { forward: null, ret: [item], item: item }; } - this.forwardItem(item, 'Reintegrator'); - return null; + return { forward: item, ret: [], item: item }; } substrate.addActor('Call', { processItem: function(item) { - return makeCall.call(this, item, 'call'); + var result = makeCall.call(this, item, 'call'); + if (result.forward) this.forwardItem(result.forward, 'Reintegrator'); + return result.ret; } }); substrate.addActor('Invoke', { processItem: function(item) { - return makeCall.call(this, item, 'invoke'); + var result = makeCall.call(this, item, 'invoke'); + if (DISABLE_EXCEPTION_CATCHING) { + result.item.intertype = 'call'; + result.ret.push({ + intertype: 'branch', + label: result.item.toLabel, + lineNum: (result.forward ? item.parentLineNum : item.lineNum) + 0.5 + }); + } + if (result.forward) this.forwardItem(result.forward, 'Reintegrator'); + return result.ret; + } + }); + // 'landingpad' - just a stub implementation + substrate.addActor('Landingpad', { + processItem: function(item) { + item.intertype = 'landingpad'; + item.type = item.tokens[1].text; + Types.needAnalysis[item.type] = 0; + this.forwardItem(item, 'Reintegrator'); } }); // 'alloca' @@ -704,7 +763,7 @@ function intertyper(data, parseFunctions, baseLineNum) { // 'store' substrate.addActor('Store', { processItem: function(item) { - if (item.tokens[0].text == 'volatile') item.tokens.shift(0); + cleanOutTokens(LLVM.ACCESS_OPTIONS, item.tokens, [0, 1]); var segments = splitTokenList(item.tokens.slice(1)); var ret = { intertype: 'store', @@ -733,7 +792,7 @@ function intertyper(data, parseFunctions, baseLineNum) { var commaIndex = findTokenText(item, ','); return [{ intertype: 'branch', - condition: parseLLVMSegment(item.tokens.slice(1, commaIndex)), + value: parseLLVMSegment(item.tokens.slice(1, commaIndex)), labelTrue: toNiceIdent(item.tokens[commaIndex+2].text), labelFalse: toNiceIdent(item.tokens[commaIndex+5].text), lineNum: item.lineNum @@ -754,6 +813,15 @@ function intertyper(data, parseFunctions, baseLineNum) { }]; } }); + // 'resume' - partial implementation + substrate.addActor('Resume', { + processItem: function(item) { + return [{ + intertype: 'resume', + lineNum: item.lineNum + }]; + } + }); // 'switch' substrate.addActor('Switch', { processItem: function(item) { diff --git a/src/jsifier.js b/src/jsifier.js index 9a2a82c4..cd59d43c 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -26,6 +26,8 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { ident: '_' + ident }); }); + + Functions.implementedFunctions = set(data.unparsedFunctions.map(function(func) { return func.ident })); } // Does simple 'macro' substitution, using Django-like syntax, @@ -279,17 +281,18 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { item.intertype = 'GlobalVariableStub'; var ret = [item]; item.JS = 'var ' + item.ident + ';'; - // Set the actual value in a postset, since it may be a global variable. TODO: handle alias of alias (needs ordering) + // Set the actual value in a postset, since it may be a global variable. We also order by dependencies there + var value = finalizeLLVMParameter(item.value, true); // do *not* indexize functions here ret.push({ intertype: 'GlobalVariablePostSet', - JS: item.ident + ' = ' + item.aliasee + ';' + ident: item.ident, + dependencies: set([value]), + JS: item.ident + ' = ' + value + ';' }); return ret; } }); - var moduleFunctions = set(data.unparsedFunctions.map(function(func) { return func.ident })); - var addedLibraryItems = {}; // functionStub @@ -305,7 +308,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { if (ident in addedLibraryItems) return ''; // Don't replace implemented functions with library ones (which can happen when we add dependencies). // Note: We don't return the dependencies here. Be careful not to end up where this matters - if (('_' + ident) in moduleFunctions) return ''; + if (('_' + ident) in Functions.implementedFunctions) return ''; addedLibraryItems[ident] = true; var snippet = LibraryManager.library[ident]; @@ -338,8 +341,14 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { } 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('{', '{ print("[library call:' + ident + ']"); '); + } } var postsetId = ident + '__postset'; @@ -425,6 +434,15 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { func.JS = '\nfunction ' + func.ident + '(' + func.paramIdents.join(', ') + ') {\n'; + if (PROFILE) { + func.JS += ' if (PROFILING) { ' + + 'var __parentProfilingNode__ = PROFILING_NODE; PROFILING_NODE = PROFILING_NODE.children["' + func.ident + '"]; ' + + 'if (!PROFILING_NODE) __parentProfilingNode__.children["' + func.ident + '"] = PROFILING_NODE = { time: 0, children: {}, calls: 0 };' + + 'PROFILING_NODE.calls++; ' + + 'var __profilingStartTime__ = Date.now() ' + + '}\n'; + } + func.JS += ' ' + RuntimeGenerator.stackEnter(func.initialStack) + ';\n'; if (LABEL_DEBUG) func.JS += " print(INDENT + ' Entering: " + func.ident + "'); INDENT += ' ';\n"; @@ -540,7 +558,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { } function getVarImpl(funcData, ident) { - if (ident === 'null') return VAR_NATIVIZED; // like nativized, in that we have the actual value right here + if (ident === 'null' || isNumber(ident)) return VAR_NATIVIZED; // like nativized, in that we have the actual value right here var data = getVarData(funcData, ident); assert(data, 'What variable is this? |' + ident + '|'); return data.impl; @@ -674,10 +692,10 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { makeFuncLineActor('branch', function(item) { if (item.stolen) return ';'; // We will appear where we were stolen to - if (!item.condition) { + if (!item.value) { return makeBranch(item.label, item.currLabelId); } else { - var condition = finalizeLLVMParameter(item.condition); + var condition = finalizeLLVMParameter(item.value); var labelTrue = makeBranch(item.labelTrue, item.currLabelId); var labelFalse = makeBranch(item.labelFalse, item.currLabelId); if (labelTrue == ';' && labelFalse == ';') return ';'; @@ -707,9 +725,9 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { ret += ' ' + makeBranch(switchLabel.label, item.currLabelId || null) + '\n'; ret += '}\n'; }); - ret += 'else {\n'; + if (item.switchLabels.length > 0) ret += 'else {\n'; ret += makeBranch(item.defaultLabel, item.currLabelId) + '\n'; - ret += '}\n'; + if (item.switchLabels.length > 0) ret += '}\n'; if (item.value) { ret += ' ' + toNiceIdent(item.value); } @@ -717,6 +735,12 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { }); makeFuncLineActor('return', function(item) { var ret = RuntimeGenerator.stackExit(item.funcData.initialStack) + ';\n'; + if (PROFILE) { + ret += 'if (PROFILING) { ' + + 'PROFILING_NODE.time += Date.now() - __profilingStartTime__; ' + + 'PROFILING_NODE = __parentProfilingNode__ ' + + '}\n'; + } if (LABEL_DEBUG) { ret += "print(INDENT + 'Exiting: " + item.funcData.ident + "');\n" + "INDENT = INDENT.substr(0, INDENT.length-2);\n"; @@ -727,12 +751,14 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { } return ret + ';'; }); + makeFuncLineActor('resume', function(item) { + return (EXCEPTION_DEBUG ? 'print("Resuming exception");' : '') + 'throw [0,0];'; + }); makeFuncLineActor('invoke', function(item) { // Wrapping in a function lets us easily return values if we are // in an assignment var call_ = makeFunctionCall(item.ident, item.params, item.funcData); var branch = makeBranch(item.toLabel, item.currLabelId); - if (DISABLE_EXCEPTIONS) return call_ + '; ' + branch; var ret = '(function() { try { __THREW__ = false; return ' + call_ + ' ' + '} catch(e) { ' @@ -743,6 +769,10 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { + ' } else { ' + makeBranch(item.unwindLabel, item.currLabelId) + ' }'; return ret; }); + makeFuncLineActor('landingpad', function(item) { + // Just a stub + return '{ f0: 0, f1: 0 }'; + }); makeFuncLineActor('load', function(item) { var value = finalizeLLVMParameter(item.pointer); var impl = item.ident ? getVarImpl(item.funcData, item.ident) : VAR_EMULATED; @@ -759,6 +789,15 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { // and we emulate them using simple JS objects { f1: , f2: , } etc., for speed return item.ident + '.f' + item.indexes[0][0].text; }); + makeFuncLineActor('insertvalue', function(item) { + assert(item.indexes.length == 1); // TODO: see extractvalue + var ret = '(', ident; + if (item.ident === 'undef') { + item.ident = 'tempValue'; + ret += item.ident + ' = [' + makeEmptyStruct(item.type) + '], '; + } + return ret + item.ident + '.f' + item.indexes[0][0].text + ' = ' + finalizeLLVMParameter(item.value) + ', ' + item.ident + ')'; + }); makeFuncLineActor('indirectbr', function(item) { return makeBranch(finalizeLLVMParameter(item.pointer), item.currLabelId, true); }); @@ -841,6 +880,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { } makeFuncLineActor('getelementptr', function(item) { return finalizeLLVMFunctionCall(item) }); makeFuncLineActor('call', function(item) { + if (item.standalone && LibraryManager.isStubFunction(item.ident)) return ';'; return makeFunctionCall(item.ident, item.params, item.funcData) + (item.standalone ? ';' : ''); }); @@ -853,11 +893,34 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { var itemsDict = { type: [], GlobalVariableStub: [], functionStub: [], function: [], GlobalVariable: [], GlobalVariablePostSet: [] }; items.forEach(function(item) { item.lines = null; - var small = { intertype: item.intertype, JS: item.JS }; // Release memory + |