diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 31 | ||||
-rw-r--r-- | src/compiler.js | 2 | ||||
-rw-r--r-- | src/experimental/noncallgraphprofiling.diff | 197 | ||||
-rw-r--r-- | src/include/emscripten.h | 16 | ||||
-rw-r--r-- | src/intertyper.js | 116 | ||||
-rw-r--r-- | src/jsifier.js | 88 | ||||
-rw-r--r-- | src/library.js | 326 | ||||
-rw-r--r-- | src/modules.js | 52 | ||||
-rw-r--r-- | src/parseTools.js | 90 | ||||
-rw-r--r-- | src/preamble.js | 98 | ||||
-rw-r--r-- | src/runtime.js | 11 | ||||
-rw-r--r-- | src/settings.js | 42 |
12 files changed, 799 insertions, 270 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index 824e7903..9d542e2c 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; @@ -107,6 +109,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 +128,20 @@ function analyzer(data) { return; } + // anonymous structure definition, for example |{ i32, i8*, void ()*, i32 }| + if (type[0] == '{' || type[0] == '<') { + 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 +351,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 +581,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 +593,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/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..67feafc6 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 @@ -378,15 +398,18 @@ function intertyper(data, parseFunctions, baseLineNum) { } 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 +420,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 +441,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 +538,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 +560,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) { @@ -574,7 +611,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 +643,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 +761,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 +790,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 +811,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..536b185e 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"; @@ -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 + var small = { intertype: item.intertype, JS: item.JS, ident: item.ident, dependencies: item.dependencies }; // Release memory itemsDict[small.intertype].push(small); }); items = null; + var splitPostSets = splitter(itemsDict.GlobalVariablePostSet, function(x) { return x.ident && x.dependencies }); + itemsDict.GlobalVariablePostSet = splitPostSets.leftIn; + var orderedPostSets = splitPostSets.splitOut; + + var limit = orderedPostSets.length * orderedPostSets.length; + for (var i = 0; i < orderedPostSets.length; i++) { + for (var j = i+1; j < orderedPostSets.length; j++) { + if (orderedPostSets[j].ident in orderedPostSets[i].dependencies) { + var temp = orderedPostSets[i]; + orderedPostSets[i] = orderedPostSets[j]; + orderedPostSets[j] = temp; + i--; + limit--; + assert(limit > 0, 'Could not sort postsets!'); + break; + } + } + } + + itemsDict.GlobalVariablePostSet = itemsDict.GlobalVariablePostSet.concat(orderedPostSets); + + // + var generated = []; if (mainPass) { generated = generated.concat(itemsDict.type).concat(itemsDict.GlobalVariableStub).concat(itemsDict.functionStub); @@ -892,13 +955,12 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { print('Runtime.structMetadata = ' + JSON.stringify(Types.structMetadata)); } generated.forEach(function(item) { print(indentify(item.JS || '', 2)); }); - print(Functions.generateIndexing()); - var postFile = BUILD_AS_SHARED_LIB ? 'postamble_sharedlib.js' : 'postamble.js'; var postParts = processMacros(preprocess(read(postFile), CONSTANTS)).split('{{GLOBAL_VARS}}'); print(postParts[0]); itemsDict.GlobalVariable.forEach(function(item) { print(indentify(item.JS, 4)); }); itemsDict.GlobalVariablePostSet.forEach(function(item) { print(indentify(item.JS, 4)); }); + print(Functions.generateIndexing()); // done last, as it may rely on aliases set in postsets print(postParts[1]); print(shellParts[1]); return null; diff --git a/src/library.js b/src/library.js index a628e323..9b48a1a2 100644 --- a/src/library.js +++ b/src/library.js @@ -21,8 +21,9 @@ LibraryManager.library = { stdin: 0, stdout: 0, stderr: 0, + _impure_ptr: 0, - $FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr'], + $FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'], $FS__postset: 'FS.init();', $FS: { // The path to the current folder. @@ -392,6 +393,16 @@ LibraryManager.library = { _stdout = allocate([2], 'void*', ALLOC_STATIC); _stderr = allocate([3], 'void*', ALLOC_STATIC); + // Newlib initialization + FS.streams[_stdin] = FS.streams[1]; + FS.streams[_stdout] = FS.streams[2]; + FS.streams[_stderr] = FS.streams[3]; + __impure_ptr = allocate(5, "void*", ALLOC_STATIC); + var impure = getValue(__impure_ptr, "void*"); + setValue(impure + {{{ QUANTUM_SIZE }}}, _stdin, "void*"); + setValue(impure + {{{ QUANTUM_SIZE }}}*2, _stdout, "void*"); + setValue(impure + {{{ QUANTUM_SIZE }}}*3, _stderr, "void*"); + // Once initialized, permissions start having effect. FS.ignorePermissions = false; } @@ -802,9 +813,12 @@ LibraryManager.library = { _umask.cmask = newMask; return oldMask; }, + stat64: 'stat', + fstat64: 'fstat', __01fstat64_: 'fstat', __01stat64_: 'stat', __01lstat64_: 'lstat', + // TODO: Check if other aliases are needed. // ========================================================================== @@ -856,13 +870,13 @@ LibraryManager.library = { var mode = {{{ makeGetValue('varargs', 0, 'i32') }}}; // Simplify flags. - var accessMode = oflag & 0x3; // O_ACCMODE. - var isWrite = accessMode != 0x0; // O_RDONLY. - var isRead = accessMode != 0x1; // O_WRONLY. - var isCreate = Boolean(oflag & 0x40); // O_CREAT. - var isExistCheck = Boolean(oflag & 0x80); // O_EXCL. - var isTruncate = Boolean(oflag & 0x200); // O_TRUNC. - var isAppend = Boolean(oflag & 0x400); // O_APPEND. + var accessMode = oflag & {{{ cDefine('O_ACCMODE') }}}; + var isWrite = accessMode != {{{ cDefine('O_RDONLY') }}}; + var isRead = accessMode != {{{ cDefine('O_WRONLY') }}}; + var isCreate = Boolean(oflag & {{{ cDefine('O_CREAT') }}}); + var isExistCheck = Boolean(oflag & {{{ cDefine('O_EXCL') }}}); + var isTruncate = Boolean(oflag & {{{ cDefine('O_TRUNC') }}}); + var isAppend = Boolean(oflag & {{{ cDefine('O_APPEND') }}}); // Verify path. var origPath = path; @@ -956,7 +970,7 @@ LibraryManager.library = { creat: function(path, mode) { // int creat(const char *path, mode_t mode); // http://pubs.opengroup.org/onlinepubs/009695399/functions/creat.html - return _open(path, 0x241, allocate([mode, 0, 0, 0], 'i32', ALLOC_STACK)); // O_WRONLY | O_CREAT | O_TRUNC. + return _open(path, {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_TRUNC') }}}, allocate([mode, 0, 0, 0], 'i32', ALLOC_STACK)); }, fcntl__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__flock_struct_layout'], fcntl: function(fildes, cmd, varargs) { @@ -968,7 +982,7 @@ LibraryManager.library = { } var stream = FS.streams[fildes]; switch (cmd) { - case 0: // F_DUPFD. + case {{{ cDefine('F_DUPFD') }}}: var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; if (arg < 0) { ___setErrNo(ERRNO_CODES.EINVAL); @@ -981,34 +995,37 @@ LibraryManager.library = { if (arg in FS.streams) arg = FS.streams.length; FS.streams[arg] = newStream; return arg; - case 1: // F_GETFD. - case 2: // F_SETFD. + case {{{ cDefine('F_GETFD') }}}: + case {{{ cDefine('F_SETFD') }}}: return 0; // FD_CLOEXEC makes no sense for a single process. - case 3: // F_GETFL. + case {{{ cDefine('F_GETFL') }}}: var flags = 0; - if (stream.isRead && stream.isWrite) flags = 0x2; // O_RDWR. - else if (!stream.isRead && stream.isWrite) flags = 0x1; // O_WRONLY. - else if (stream.isRead && !stream.isWrite) flags = 0x0; // O_RDONLY. - if (stream.isAppend) flags |= 0x400; // O_APPEND. + if (stream.isRead && stream.isWrite) flags = {{{ cDefine('O_RDWR') }}}; + else if (!stream.isRead && stream.isWrite) flags = {{{ cDefine('O_WRONLY') }}}; + else if (stream.isRead && !stream.isWrite) flags = {{{ cDefine('O_RDONLY') }}}; + if (stream.isAppend) flags |= {{{ cDefine('O_APPEND') }}}; // Synchronization and blocking flags are irrelevant to us. return flags; - case 4: // F_SETFL. + case {{{ cDefine('F_SETFL') }}}: var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; - stream.isAppend = Boolean(arg | 0x400); // O_APPEND. + stream.isAppend = Boolean(arg | {{{ cDefine('O_APPEND') }}}); // Synchronization and blocking flags are irrelevant to us. return 0; - case 5: // F_GETLK. + case {{{ cDefine('F_GETLK') }}}: + case {{{ cDefine('F_GETLK64') }}}: var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; var offset = ___flock_struct_layout.l_type; // We're always unlocked. - {{{ makeSetValue('arg', 'offset', '2', 'i16') }}} // F_UNLCK. + {{{ makeSetValue('arg', 'offset', cDefine('F_UNLCK'), 'i16') }}} return 0; - case 6: // F_SETLK. - case 7: // F_SETLKW. + case {{{ cDefine('F_SETLK') }}}: + case {{{ cDefine('F_SETLKW') }}}: + case {{{ cDefine('F_SETLK64') }}}: + case {{{ cDefine('F_SETLKW64') }}}: // Pretend that the locking is successful. return 0; - case 8: // F_SETOWN. - case 9: // F_GETOWN. + case {{{ cDefine('F_SETOWN') }}}: + case {{{ cDefine('F_GETOWN') }}}: // These are for sockets. We don't have them implemented (yet?). ___setErrNo(ERRNO_CODES.EINVAL); return -1; @@ -1060,10 +1077,10 @@ LibraryManager.library = { var revents = 0; if (fd in FS.streams) { var stream = FS.streams[fd]; - if (events & 0x1) revents |= 0x1; // POLLIN. - if (events & 0x4) revents |= 0x4; // POLLOUT. + if (events & {{{ cDefine('POLLIN') }}}) revents |= {{{ cDefine('POLLIN') }}}; + if (events & {{{ cDefine('POLLOUT') }}}) revents |= {{{ cDefine('POLLOUT') }}}; } else { - if (events & 0x20) revents |= 0x20; // POLLNVAL. + if (events & {{{ cDefine('POLLNVAL') }}}) revents |= {{{ cDefine('POLLNVAL') }}}; } if (revents) nonzero++; {{{ makeSetValue('pollfd', 'offsets.revents', 'revents', 'i16') }}} @@ -1680,37 +1697,37 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/confstr.html var value; switch (name) { - case 0: // _CS_PATH. + case {{{ cDefine('_CS_PATH') }}}: value = ENV['PATH'] || '/'; break; - case 1: // _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS. + case {{{ cDefine('_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS') }}}: // Mimicing glibc. value = 'POSIX_V6_ILP32_OFF32\nPOSIX_V6_ILP32_OFFBIG'; break; - case 2: // _CS_GNU_LIBC_VERSION. + case {{{ cDefine('_CS_GNU_LIBC_VERSION') }}}: // This JS implementation was tested against this glibc version. value = 'glibc 2.14'; break; - case 3: // _CS_GNU_LIBPTHREAD_VERSION. - // We don't support pthread. + case {{{ cDefine('_CS_GNU_LIBPTHREAD_VERSION') }}}: + // We don't support pthreads. value = ''; break; - case 1118: // _CS_POSIX_V6_ILP32_OFF32_LIBS. - case 1122: // _CS_POSIX_V6_ILP32_OFFBIG_LIBS. - case 1124: // _CS_POSIX_V6_LP64_OFF64_CFLAGS. - case 1125: // _CS_POSIX_V6_LP64_OFF64_LDFLAGS. - case 1126: // _CS_POSIX_V6_LP64_OFF64_LIBS. - case 1128: // _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS. - case 1129: // _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS. - case 1130: // _CS_POSIX_V6_LPBIG_OFFBIG_LIBS. + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_LIBS') }}}: + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_LIBS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_CFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_LDFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_LIBS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_LIBS') }}}: value = ''; break; - case 1116: // _CS_POSIX_V6_ILP32_OFF32_CFLAGS. - case 1117: // _CS_POSIX_V6_ILP32_OFF32_LDFLAGS. - case 1121: // _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS. + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_CFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_LDFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS') }}}: value = '-m32'; break; - case 1120: // _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS. + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS') }}}: value = '-m32 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'; break; default: @@ -2101,6 +2118,9 @@ LibraryManager.library = { self.DATASIZE += alignMemoryPage(bytes); return ret; // Previous break location. }, + open64: 'open', + lseek64: 'lseek', + ftruncate64: 'ftruncate', __01open64_: 'open', __01lseek64_: 'lseek', __01truncate64_: 'truncate', @@ -2330,9 +2350,9 @@ LibraryManager.library = { var argText; var prefix = ''; if (next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0)) { - argText = currAbsArg.toString(10); + argText = reSign(currArg, 8 * argSize, 1).toString(10); } else if (next == 'u'.charCodeAt(0)) { - argText = unSign(currArg, 8 * argSize).toString(10); + argText = unSign(currArg, 8 * argSize, 1).toString(10); currArg = Math.abs(currArg); } else if (next == 'o'.charCodeAt(0)) { argText = (flagAlternative ? '0' : '') + currAbsArg.toString(8); @@ -2369,11 +2389,13 @@ LibraryManager.library = { } } - // Add sign. - if (currArg < 0) { - prefix = '-' + prefix; - } else if (flagAlwaysSigned) { - prefix = '+' + prefix; + // Add sign if needed + if (flagAlwaysSigned) { + if (currArg < 0) { + prefix = '-' + prefix; + } else { + prefix = '+' + prefix; + } } // Add padding. @@ -2699,26 +2721,26 @@ LibraryManager.library = { mode = Pointer_stringify(mode); if (mode[0] == 'r') { if (mode.indexOf('+') != -1) { - flags = 0x2; // O_RDWR + flags = {{{ cDefine('O_RDWR') }}}; } else { - flags = 0x0; // O_RDONLY + flags = {{{ cDefine('O_RDONLY') }}}; } } else if (mode[0] == 'w') { if (mode.indexOf('+') != -1) { - flags = 0x2; // O_RDWR + flags = {{{ cDefine('O_RDWR') }}}; } else { - flags = 0x1; // O_WRONLY + flags = {{{ cDefine('O_WRONLY') }}}; } - flags |= 0x40; // O_CREAT - flags |= 0x200; // O_TRUNC + flags |= {{{ cDefine('O_CREAT') }}}; + flags |= {{{ cDefine('O_TRUNC') }}}; } else if (mode[0] == 'a') { if (mode.indexOf('+') != -1) { - flags = 0x2; // O_RDWR + flags = {{{ cDefine('O_RDWR') }}}; } else { - flags = 0x1; // O_WRONLY + flags = {{{ cDefine('O_WRONLY') }}}; } - flags |= 0x40; // O_CREAT - flags |= 0x400; // O_APPEND + flags |= {{{ cDefine('O_CREAT') }}}; + flags |= {{{ cDefine('O_APPEND') }}}; } else { ___setErrNo(ERRNO_CODES.EINVAL); return 0; @@ -2815,6 +2837,7 @@ LibraryManager.library = { } }, fseeko: 'fseek', + fseeko64: 'fseek', fsetpos__deps: ['$FS', 'lseek', '__setErrNo', '$ERRNO_CODES'], fsetpos: function(stream, pos) { // int fsetpos(FILE *stream, const fpos_t *pos); @@ -2853,6 +2876,7 @@ LibraryManager.library = { } }, ftello: 'ftell', + ftello64: 'ftell', fwrite__deps: ['$FS', 'write'], fwrite: function(ptr, size, nitems, stream) { // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); @@ -3056,7 +3080,10 @@ LibraryManager.library = { // int fprintf(FILE *restrict stream, const char *restrict format, ...); // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html var result = __formatString(format, varargs); - return _fwrite(allocate(result, 'i8', ALLOC_STACK), 1, result.length, stream); + var stack = Runtime.stackSave(); + var ret = _fwrite(allocate(result, 'i8', ALLOC_STACK), 1, result.length, stream); + Runtime.stackRestore(stack); + return ret; }, printf__deps: ['fprintf'], printf: function(format, varargs) { @@ -3078,6 +3105,7 @@ LibraryManager.library = { vscanf: 'scanf', vfscanf: 'fscanf', vsscanf: 'sscanf', + fopen64: 'fopen', __01fopen64_: 'fopen', __01freopen64_: 'freopen', __01fseeko64_: 'fseek', @@ -3986,6 +4014,10 @@ LibraryManager.library = { throw 'Assertion failed: ' + Pointer_stringify(condition);//JSON.stringify(arguments)//condition; }, + __assert_func: function(filename, line, func, condition) { + throw 'Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [Pointer_stringify(filename), line, Pointer_stringify(func)]; + }, + __cxa_guard_acquire: function() { return 1; }, @@ -4104,14 +4136,14 @@ LibraryManager.library = { if (!self.LLVM_SAVEDSTACKS) { self.LLVM_SAVEDSTACKS = []; } - self.LLVM_SAVEDSTACKS.push(STACKTOP); + self.LLVM_SAVEDSTACKS.push(Runtime.stackSave()); return self.LLVM_SAVEDSTACKS.length-1; }, llvm_stackrestore: function(p) { var self = _llvm_stacksave; var ret = self.LLVM_SAVEDSTACKS[p]; self.LLVM_SAVEDSTACKS.splice(p, 1); - return ret; + Runtime.stackRestore(ret); }, __cxa_pure_virtual: function() { @@ -4131,6 +4163,13 @@ LibraryManager.library = { return ret; }, + llvm_expect_i32: function(x, y) { + return x == y; // TODO: inline this + }, + + llvm_lifetime_start: function() {}, + llvm_lifetime_end: function() {}, + // ========================================================================== // iostream.h // ========================================================================== @@ -4349,6 +4388,15 @@ LibraryManager.library = { }, nanf: 'nan', + __fpclassifyf: function(x) { + if (isNaN(x)) return {{{ cDefine('FP_NAN') }}}; + if (!isFinite(x)) return {{{ cDefine('FP_INFINITE') }}}; + if (x == 0) return {{{ cDefine('FP_ZERO') }}}; + // FP_SUBNORMAL..? + return {{{ cDefine('FP_NORMAL') }}}; + }, + __fpclassifyd: '__fpclassifyf', + // ========================================================================== // sys/utsname.h // ========================================================================== @@ -4536,8 +4584,8 @@ LibraryManager.library = { // ========================================================================== clock: function() { - if (_clock.start === undefined) _clock.start = new Date(); - return (Date.now() - _clock.start.getTime()) * 1000; + if (_clock.start === undefined) _clock.start = Date.now(); + return Math.floor((Date.now() - _clock.start) * ({{{ cDefine('CLOCKS_PER_SEC') }}}/1000)); }, time: function(ptr) { @@ -4615,8 +4663,8 @@ LibraryManager.library = { timegm__deps: ['mktime'], timegm: function(tmPtr) { _tzset(); - var offset = {{{ makeGetValue('_timezone', 0, 'i32') }}}; - var daylight = {{{ makeGetValue('_daylight', 0, 'i32') }}}; + var offset = {{{ makeGetValue('__timezone', 0, 'i32') }}}; + var daylight = {{{ makeGetValue('__daylight', 0, 'i32') }}}; daylight = (daylight == 1) ? 60 * 60 : 0; var ret = _mktime(tmPtr) + offset - daylight; return ret; @@ -4694,29 +4742,30 @@ LibraryManager.library = { }, // TODO: Initialize these to defaults on startup from system settings. - tzname: null, - daylight: null, - timezone: null, - tzset__deps: ['malloc', 'tzname', 'daylight', 'timezone'], + // Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm) + _tzname: null, + _daylight: null, + _timezone: null, + tzset__deps: ['_tzname', '_daylight', '_timezone'], tzset: function() { // TODO: Use (malleable) environment variables instead of system settings. - if (_tzname !== null) return; + if (__tzname) return; // glibc does not need the double __ - _timezone = _malloc(QUANTUM_SIZE); - {{{ makeSetValue('_timezone', '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}} + __timezone = _malloc(QUANTUM_SIZE); + {{{ makeSetValue('__timezone', '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}} - _daylight = _malloc(QUANTUM_SIZE); + __daylight = _malloc(QUANTUM_SIZE); var winter = new Date(2000, 0, 1); var summer = new Date(2000, 6, 1); - {{{ makeSetValue('_daylight', '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}} + {{{ makeSetValue('__daylight', '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}} var winterName = winter.toString().match(/\(([A-Z]+)\)/)[1]; var summerName = summer.toString().match(/\(([A-Z]+)\)/)[1]; var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL); var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL); - _tzname = _malloc(2 * QUANTUM_SIZE); - {{{ makeSetValue('_tzname', '0', 'winterNamePtr', 'i32') }}} - {{{ makeSetValue('_tzname', QUANTUM_SIZE, 'summerNamePtr', 'i32') }}} + __tzname = _malloc(2 * QUANTUM_SIZE); // glibc does not need the double __ + {{{ makeSetValue('__tzname', '0', 'winterNamePtr', 'i32') }}} + {{{ makeSetValue('__tzname', QUANTUM_SIZE, 'summerNamePtr', 'i32') }}} }, stime__deps: ['$ERRNO_CODES', '__setErrNo'], @@ -4921,163 +4970,163 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/nl_langinfo.html var result; switch (item) { - case 0xE: // CODESET + case {{{ cDefine('CODESET') }}}: result = 'ANSI_X3.4-1968'; break; - case 0x20028: // D_T_FMT + case {{{ cDefine('D_T_FMT') }}}: result = '%a %b %e %H:%M:%S %Y'; break; - case 0x20029: // D_FMT + case {{{ cDefine('D_FMT') }}}: result = '%m/%d/%y'; break; - case 0x2002A: // T_FMT + case {{{ cDefine('T_FMT') }}}: result = '%H:%M:%S'; break; - case 0x2002B: // T_FMT_AMPM + case {{{ cDefine('T_FMT_AMPM') }}}: result = '%I:%M:%S %p'; break; - case 0x20026: // AM_STR + case {{{ cDefine('AM_STR') }}}: result = 'AM'; break; - case 0x20027: // PM_STR + case {{{ cDefine('PM_STR') }}}: result = 'PM'; break; - case 0x20007: // DAY_1 + case {{{ cDefine('DAY_1') }}}: result = 'Sunday'; break; - case 0x20008: // DAY_2 + case {{{ cDefine('DAY_2') }}}: result = 'Monday'; break; - case 0x20009: // DAY_3 + case {{{ cDefine('DAY_3') }}}: result = 'Tuesday'; break; - case 0x2000A: // DAY_4 + case {{{ cDefine('DAY_4') }}}: result = 'Wednesday'; break; - case 0x2000B: // DAY_5 + case {{{ cDefine('DAY_5') }}}: result = 'Thursday'; break; - case 0x2000C: // DAY_6 + case {{{ cDefine('DAY_6') }}}: result = 'Friday'; break; - case 0x2000D: // DAY_7 + case {{{ cDefine('DAY_7') }}}: result = 'Saturday'; break; - case 0x20000: // ABDAY_1 + case {{{ cDefine('ABDAY_1') }}}: result = 'Sun'; break; - case 0x20001: // ABDAY_2 + case {{{ cDefine('ABDAY_2') }}}: result = 'Mon'; break; - case 0x20002: // ABDAY_3 + case {{{ cDefine('ABDAY_3') }}}: result = 'Tue'; break; - case 0x20003: // ABDAY_4 + case {{{ cDefine('ABDAY_4') }}}: result = 'Wed'; break; - case 0x20004: // ABDAY_5 + case {{{ cDefine('ABDAY_5') }}}: result = 'Thu'; break; - case 0x20005: // ABDAY_6 + case {{{ cDefine('ABDAY_6') }}}: result = 'Fri'; break; - case 0x20006: // ABDAY_7 + case {{{ cDefine('ABDAY_7') }}}: result = 'Sat'; break; - case 0x2001A: // MON_1 + case {{{ cDefine('MON_1') }}}: result = 'January'; break; - case 0x2001B: // MON_2 + case {{{ cDefine('MON_2') }}}: result = 'February'; break; - case 0x2001C: // MON_3 + case {{{ cDefine('MON_3') }}}: result = 'March'; break; - case 0x2001D: // MON_4 + case {{{ cDefine('MON_4') }}}: result = 'April'; break; - case 0x2001E: // MON_5 + case {{{ cDefine('MON_5') }}}: result = 'May'; break; - case 0x2001F: // MON_6 + case {{{ cDefine('MON_6') }}}: result = 'June'; break; - case 0x20020: // MON_7 + case {{{ cDefine('MON_7') }}}: result = 'July'; break; - case 0x20021: // MON_8 + case {{{ cDefine('MON_8') }}}: result = 'August'; break; - case 0x20022: // MON_9 + case {{{ cDefine('MON_9') }}}: result = 'September'; break; - case 0x20023: // MON_10 + case {{{ cDefine('MON_10') }}}: result = 'October'; break; - case 0x20024: // MON_11 + case {{{ cDefine('MON_11') }}}: result = 'November'; break; - case 0x20025: // MON_12 + case {{{ cDefine('MON_12') }}}: result = 'December'; break; - case 0x2000E: // ABMON_1 + case {{{ cDefine('ABMON_1') }}}: result = 'Jan'; break; - case 0x2000F: // ABMON_2 + case {{{ cDefine('ABMON_2') }}}: result = 'Feb'; break; - case 0x20010: // ABMON_3 + case {{{ cDefine('ABMON_3') }}}: result = 'Mar'; break; - case 0x20011: // ABMON_4 + case {{{ cDefine('ABMON_4') }}}: result = 'Apr'; break; - case 0x20012: // ABMON_5 + case {{{ cDefine('ABMON_5') }}}: result = 'May'; break; - case 0x20013: // ABMON_6 + case {{{ cDefine('ABMON_6') }}}: result = 'Jun'; break; - case 0x20014: // ABMON_7 + case {{{ cDefine('ABMON_7') }}}: result = 'Jul'; break; - case 0x20015: // ABMON_8 + case {{{ cDefine('ABMON_8') }}}: result = 'Aug'; break; - case 0x20016: // ABMON_9 + case {{{ cDefine('ABMON_9') }}}: result = 'Sep'; break; - case 0x20017: // ABMON_10 + case {{{ cDefine('ABMON_10') }}}: result = 'Oct'; break; - case 0x20018: // ABMON_11 + case {{{ cDefine('ABMON_11') }}}: result = 'Nov'; break; - case 0x20019: // ABMON_12 + case {{{ cDefine('ABMON_12') }}}: result = 'Dec'; break; - case 0x2002F: // ALT_DIGITS + case {{{ cDefine('ALT_DIGITS') }}}: result = ''; break; - case 0x10000: // RADIXCHAR + case {{{ cDefine('RADIXCHAR') }}}: result = '.'; break; - case 0x10001: // THOUSEP + case {{{ cDefine('THOUSEP') }}}: result = ''; break; - case 0x50000: // YESEXPR + case {{{ cDefine('YESEXPR') }}}: result = '^[yY]'; break; - case 0x50001: // NOEXPR + case {{{ cDefine('NOEXPR') }}}: result = '^[nN]'; break; - case 0x4000F: // CRNCYSTR + case {{{ cDefine('CRNCYSTR') }}}: result = '-'; break; - case 0x2002C: // ERA - case 0x2002E: // ERA_D_FMT - case 0x20030: // ERA_D_T_FMT - case 0x20031: // ERA_T_FMT + case {{{ cDefine('ERA') }}}: + case {{{ cDefine('ERA_D_FMT') }}}: + case {{{ cDefine('ERA_D_T_FMT') }}}: + case {{{ cDefine('ERA_T_FMT') }}}: default: result = ''; break; @@ -5269,6 +5318,8 @@ LibraryManager.library = { __errno_location: function() { return ___setErrNo.ret; }, + __errno: '__errno_location', + // ========================================================================== // sys/resource.h // ========================================================================== @@ -5294,6 +5345,9 @@ LibraryManager.library = { pthread_mutex_init: function() {}, pthread_mutex_destroy: function() {}, + pthread_mutexattr_init: function() {}, + pthread_mutexattr_settype: function() {}, + pthread_mutexattr_destroy: function() {}, pthread_mutex_lock: function() {}, pthread_mutex_unlock: function() {}, pthread_cond_broadcast: function() {}, diff --git a/src/modules.js b/src/modules.js index f613c20b..2341b575 100644 --- a/src/modules.js +++ b/src/modules.js @@ -8,7 +8,9 @@ var LLVM = { 'weak_odr', 'externally_visible', 'dllimport', 'dllexport', 'unnamed_addr'), VISIBILITIES: set('default', 'hidden', 'protected'), PARAM_ATTR: set('noalias', 'signext', 'zeroext', 'inreg', 'sret', 'nocapture', 'nest'), - CALLING_CONVENTIONS: set('ccc', 'fastcc', 'coldcc', 'cc10', 'x86_fastcallcc') + CALLING_CONVENTIONS: set('ccc', 'fastcc', 'coldcc', 'cc10', 'x86_fastcallcc', 'x86_stdcallcc'), + ACCESS_OPTIONS: set('volatile', 'atomic'), + INVOKE_MODIFIERS: set('alignstack', 'alwaysinline', 'inlinehint', 'naked', 'noimplicitfloat', 'noinline', 'alwaysinline attribute.', 'noredzone', 'noreturn', 'nounwind', 'optsize', 'readnone', 'readonly', 'ssp', 'sspreq') }; LLVM.GLOBAL_MODIFIERS = set(keys(LLVM.LINKAGES).concat(['constant', 'global', 'hidden'])); @@ -27,18 +29,19 @@ var Debugging = { var form1 = new RegExp(/^ .*, !dbg !(\d+) *$/); var form2 = new RegExp(/^ .*, !dbg !(\d+) *; .*$/); - var form3 = new RegExp(/^!(\d+) = metadata !{i32 (\d+), i32 \d+, metadata !(\d+), .*}$/); - var form3a = new RegExp(/^!(\d+) = metadata !{i32 \d+, metadata !\d+, i32 \d+, i32 \d+, metadata !(\d+), i32 \d+} ; \[ DW_TAG_lexical_block \]$/); - var form3ab = new RegExp(/^!(\d+) = metadata !{i32 \d+, i32 \d+, metadata !(\d+), .*$/); - var form3ac = new RegExp(/^!(\d+) = metadata !{i32 \d+, metadata !\d+, metadata !"[^"]+", metadata !(\d+)[^\[]* ; \[ DW_TAG_.*$/); - var form3b = new RegExp(/^!(\d+) = metadata !{i32 \d+, metadata !"([^"]+)", metadata !"([^"]+)", metadata !\d+} ; \[ DW_TAG_file_type \]$/); + var form3 = new RegExp(/^!(\d+) = metadata !{i32 (\d+), (?:i32 \d+|null), metadata !(\d+), .*}$/); + var form3a = new RegExp(/^!(\d+) = metadata !{i32 \d+, (?:i32 \d+|metadata !\d+), (?:i32 \d+|null), (?:i32 \d+|null), metadata !(\d+), (?:i32 \d+|null)}.*/); + var form3ab = new RegExp(/^!(\d+) = metadata !{i32 \d+, (?:i32 \d+|null), metadata !(\d+), .*$/); + var form3ac = new RegExp(/^!(\d+) = metadata !{i32 \d+, (?:metadata !\d+|null), metadata !"[^"]+", metadata !(\d+)[^\[]*.*$/); + var form3ad = new RegExp(/^!(\d+) = metadata !{i32 \d+, (?:i32 \d+|null), (?:i32 \d+|null), metadata !"[^"]*", metadata !"[^"]*", metadata !"[^"]*", metadata !(\d+),.*$/); + var form3b = new RegExp(/^!(\d+) = metadata !{i32 \d+, metadata !"([^"]+)", metadata !"([^"]+)", (metadata !\d+|null)}.*$/); var form3c = new RegExp(/^!(\d+) = metadata !{\w+\d* !?(\d+)[^\d].*$/); var form4 = new RegExp(/^!llvm.dbg.[\w\.]+ = .*$/); var form5 = new RegExp(/^!(\d+) = metadata !{.*$/); var form6 = new RegExp(/^ (tail )?call void \@llvm.dbg.\w+\(metadata .*$/); - var formStruct = /^!(\d+) = metadata !\{i32 \d+, metadata !\d+, metadata !"([^"]+)", metadata !\d+, i32 \d+, i64 \d+, [^,]*, [^,]*, [^,]*, [^,]*, metadata !(\d+), .*} ; \[ DW_TAG_(?:structure|class)_type \]$/; + var formStruct = /^!(\d+) = metadata !\{i32 \d+, (metadata !\d+|null), metadata !"([^"]+)", metadata !(\d+), (?:i32 \d+|null), i64 \d+, [^,]*, [^,]*, [^,]*, [^,]*, metadata !(\d+), .*}.*$/; var formStructMembers = /^!(\d+) = metadata !\{(.*)\}$/; - var formMember = /^!(\d+) = metadata !\{i32 \d+, metadata !\d+, metadata !"([^"]+)", metadata !\d+, i32 \d+, i64 \d+, i64 \d+, i64 \d+, .+?, metadata !(\d+)} ; \[ DW_TAG_member \]$/; + var formMember = /^!(\d+) = metadata !\{i32 \d+, metadata !\d+, metadata !"([^"]+)", metadata !\d+, (?:i32 \d+|null), i64 \d+, i64 \d+, i64 \d+, .+?, metadata !(\d+)}.*$/; var debugComment = new RegExp(/; +\[debug line = \d+:\d+\]/); @@ -54,10 +57,13 @@ var Debugging = { return line.replace(', !dbg !' + calc[1], ''); } calc = formStruct.exec(line); - if (calc && !(calc[2] in structToMemberMeta)) { - structMetaToStruct[calc[1]] = calc[2]; - structToMemberMeta[calc[2]] = calc[3]; - memberMetaToStruct[calc[3]] = calc[1]; + if (calc) { + metadataToParentMetadata[calc[1]] = calc[4]; + if (!(calc[3] in structToMemberMeta)) { + structMetaToStruct[calc[1]] = calc[3]; + structToMemberMeta[calc[3]] = calc[5]; + memberMetaToStruct[calc[5]] = calc[1]; + } skipLine = true; } calc = formStructMembers.exec(line); @@ -79,7 +85,7 @@ var Debugging = { metadataToParentMetadata[calc[1]] = calc[3]; return ';'; // return an empty line, to keep line numbers of subsequent lines the same } - calc = form3a.exec(line) || form3ab.exec(line) || form3ac.exec(line); + calc = form3a.exec(line) || form3ab.exec(line) || form3ac.exec(line) || form3ad.exec(line); if (calc) { metadataToParentMetadata[calc[1]] = calc[2]; return ';'; @@ -106,9 +112,9 @@ var Debugging = { for (var l in llvmLineToMetadata) { var m = llvmLineToMetadata[l]; this.llvmLineToSourceLine[l] = metadataToSourceLine[m]; - //dprint('starting to recurse metadata for: ' + m); + dprint('metadata', 'starting to recurse metadata for: ' + m); while (!metadataToFilename[m]) { - //dprint('recursing metadata, at: ' + m); + dprint('metadata', 'recursing metadata, at: ' + m); m = metadataToParentMetadata[m]; assert(m, 'Confused as to parent metadata for llvm #' + l + ', metadata !' + m); } @@ -215,6 +221,9 @@ var Functions = { // The list of function datas which are being processed in the jsifier, currently currFunctions: [], + // All functions that will be implemented in this file + implementedFunctions: null, + indexedFunctions: [0, 0], // Start at a non-0 (even, see below) value // Mark a function as needing indexing, and returns the index @@ -235,7 +244,7 @@ var Functions = { // Shared libraries reuse the parent's function table. return 'FUNCTION_TABLE = FUNCTION_TABLE.concat([' + indices + ']);'; } else { - return 'var FUNCTION_TABLE = [' + indices + '];'; + return 'FUNCTION_TABLE = [' + indices + ']; Module["FUNCTION_TABLE"] = FUNCTION_TABLE;'; } } }; @@ -263,6 +272,17 @@ var LibraryManager = { ret = LibraryManager.library[ret]; } return last; + }, + + isStubFunction: function(ident) { + var libCall = LibraryManager.library[ident.substr(1)]; + return typeof libCall === 'function' && libCall.toString().replace(/\s/g, '') === 'function(){}' + && !(ident in Functions.implementedFunctions); } }; +// Safe way to access a C define. We check that we don't add library functions with missing defines. +function cDefine(key) { + return key in C_DEFINES ? C_DEFINES[key] : ('0 /* XXX missing C define ' + key + ' */'); +} + diff --git a/src/parseTools.js b/src/parseTools.js index 955353b2..7d62b34d 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -6,7 +6,9 @@ function processMacros(text) { return text.replace(/{{{[^}]+}}}/g, function(str) { str = str.substr(3, str.length-6); - return eval(str).toString(); + var ret = eval(str); + if (ret !== undefined) ret = ret.toString(); + return ret; }); } @@ -53,6 +55,7 @@ function preprocess(text, constants) { function addPointing(type) { return type + '*' } function removePointing(type, num) { if (num === 0) return type; + assert(type.substr(type.length-(num ? num : 1)).replace(/\*/g, '') === ''); //, 'Error in removePointing with ' + [type, num, type.substr(type.length-(num ? num : 1))]); return type.substr(0, type.length-(num ? num : 1)); } @@ -114,6 +117,7 @@ function isStructPointerType(type) { function isStructType(type) { if (isPointerType(type)) return false; if (new RegExp(/^\[\d+\ x\ (.*)\]/g).test(type)) return true; // [15 x ?] blocks. Like structs + if (new RegExp(/<?{ [^}]* }>?/g).test(type)) return true; // { i32, i8 } etc. - anonymous struct types // See comment in isStructPointerType() return !Runtime.isNumberType(type) && type[0] == '%'; } @@ -148,7 +152,7 @@ function isFunctionType(type) { if (pointingLevels(type) !== 1) return false; var text = removeAllPointing(parts.slice(1).join(' ')); if (!text) return false; - return isType(parts[0]) && isFunctionDef({ text: text, item: tokenizer.processItem({ lineText: text.substr(1, text.length-2) }, true) }); + return isType(parts[0]) && isFunctionDef({ text: text, item: tokenize(text.substr(1, text.length-2), true) }); } function isType(type) { // TODO! @@ -465,15 +469,13 @@ function eatLLVMIdent(tokens) { return ret; } -function cleanOutTokens(filterOut, tokens, index) { - while (filterOut.indexOf(tokens[index].text) != -1) { - tokens.splice(index, 1); - } -} - -function cleanOutTokensSet(filterOut, tokens, index) { - while (tokens[index].text in filterOut) { - tokens.splice(index, 1); +function cleanOutTokens(filterOut, tokens, indexes) { + if (typeof indexes !== 'object') indexes = [indexes]; + for (var i = indexes.length-1; i >=0; i--) { + var index = indexes[i]; + while (tokens[index].text in filterOut) { + tokens.splice(index, 1); + } } } @@ -671,6 +673,7 @@ function indentify(text, indent) { function correctSpecificSign() { assert(!(CORRECT_SIGNS >= 2 && !Debugging.on), 'Need debugging for line-specific corrections'); if (!Framework.currItem) return false; + if (Framework.currItem.funcData.ident.indexOf('emscripten_autodebug') >= 0) return 1; // always correct in the autodebugger code! return (CORRECT_SIGNS === 2 && Debugging.getIdentifier() in CORRECT_SIGNS_LINES) || (CORRECT_SIGNS === 3 && !(Debugging.getIdentifier() in CORRECT_SIGNS_LINES)); } @@ -842,21 +845,15 @@ function makeCopyValues(dest, src, num, type, modifier) { }).join('; ') + '; ' + safety() ) + '\n' + '}'; } else { // USE_TYPED_ARRAYS == 2 -/* - return 'for (var $mcpi$ = 0; $mcpi$ < ' + num + '; $mcpi$++) {\n' + - ' HEAP8[' + dest + '+$mcpi$] = HEAP8[' + src + '+$mcpi$]; ' + safety() + ';\n' + - '}'; -*/ return '' + 'var $src$, $dest$, $stop$, $stop4$, $fast$;\n' + '$src$ = ' + src + ';\n' + '$dest$ = ' + dest + ';\n' + '$stop$ = $src$ + ' + num + ';\n' + - '$fast$ = ($dest$%4) === ($src$%4);\n' + - 'while ($src$%4 !== 0 && $src$ < $stop$) {\n' + - ' ' + safety('$dest$', '$src$') + '; HEAP8[$dest$++] = HEAP8[$src$++];\n' + - '}\n' + - 'if ($fast$) {\n' + + 'if (($dest$%4) == ($src$%4) && ' + num + ' > 8) {\n' + + ' while ($src$%4 !== 0 && $src$ < $stop$) {\n' + + ' ' + safety('$dest$', '$src$') + '; HEAP8[$dest$++] = HEAP8[$src$++];\n' + + ' }\n' + ' $src$ >>= 2;\n' + ' $dest$ >>= 2;\n' + ' $stop4$ = $stop$ >> 2;\n' + @@ -867,10 +864,10 @@ function makeCopyValues(dest, src, num, type, modifier) { ' }\n' + ' $src$ <<= 2;\n' + ' $dest$ <<= 2;\n' + - '}\n' + + '}' + 'while ($src$ < $stop$) {\n' + ' ' + safety('$dest$', '$src$') + '; HEAP8[$dest$++] = HEAP8[$src$++];\n' + - '}' + '}'; } return null; } @@ -979,14 +976,14 @@ function makeGetSlabs(ptr, type, allowMultiple, unsigned) { return []; } -function finalizeLLVMFunctionCall(item) { +function finalizeLLVMFunctionCall(item, noIndexizeFunctions) { switch(item.intertype) { case 'getelementptr': // TODO finalizeLLVMParameter on the ident and the indexes? return makePointer(makeGetSlabs(item.ident, item.type)[0], getGetElementPtrIndexes(item), null, item.type); case 'bitcast': case 'inttoptr': case 'ptrtoint': - return finalizeLLVMParameter(item.params[0]); + return finalizeLLVMParameter(item.params[0], noIndexizeFunctions); case 'icmp': case 'mul': case 'zext': case 'add': case 'sub': case 'div': var temp = { op: item.intertype, @@ -1078,18 +1075,18 @@ function handleOverflow(text, bits) { } // From parseLLVMSegment -function finalizeLLVMParameter(param) { +function finalizeLLVMParameter(param, noIndexizeFunctions) { var ret; if (isNumber(param)) { return param; } else if (typeof param === 'string') { return toNiceIdentCarefully(param); } else if (param.intertype in PARSABLE_LLVM_FUNCTIONS) { - ret = finalizeLLVMFunctionCall(param); + ret = finalizeLLVMFunctionCall(param, noIndexizeFunctions); } else if (param.intertype == 'value') { ret = parseNumerical(param.ident); } else if (param.intertype == 'structvalue') { - ret = param.values.map(finalizeLLVMParameter); + ret = param.values.map(function(value) { return finalizeLLVMParameter(value, noIndexizeFunctions) }); } else if (param.intertype === 'blockaddress') { return finalizeBlockAddress(param); } else if (param.intertype === 'type') { @@ -1098,7 +1095,8 @@ function finalizeLLVMParameter(param) { throw 'invalid llvm parameter: ' + param.intertype; } assert(param.type || (typeof param === 'string' && param.substr(0, 6) === 'CHECK_'), 'Missing type for param: ' + dump(param)); - return indexizeFunctions(ret, param.type); + if (!noIndexizeFunctions) ret = indexizeFunctions(ret, param.type); + return ret; } function makeSignOp(value, type, op) { @@ -1117,12 +1115,20 @@ function makeSignOp(value, type, op) { } if (!correctSigns() && !CHECK_SIGNS) return value; if (type in Runtime.INT_TYPES) { - // shortcuts for 32-bit case - if (bits === 32 && !CHECK_SIGNS) { - if (op === 're') { - return '((' + value + ')|0)'; - } else { - return '((' + value + ')>>>0)'; + // shortcuts + if (!CHECK_SIGNS) { + if (bits === 32) { + if (op === 're') { + return '((' + value + ')|0)'; + } else { + return '((' + value + ')>>>0)'; + } + } else if (bits < 32) { + if (op === 'un') { + return '((' + value + ')&' + (Math.pow(2, bits)-1) + ')'; + } else { + return '(tempInt=(' + value + '),(tempInt>=' + Math.pow(2, bits-1) + '?tempInt-' + Math.pow(2, bits) + ':tempInt))'; + } } } return full; @@ -1161,6 +1167,8 @@ function processMathop(item) { with(item) { item['ident'+i] = null; // just so it exists for purposes of reading ident2 etc. later on, and no exception is thrown } } + var originalIdent1 = ident1; + var originalIdent2 = ident2; if (isUnsignedOp(op, variant)) { ident1 = makeSignOp(ident1, paramTypes[0], 'un'); ident2 = makeSignOp(ident2, paramTypes[1], 'un'); @@ -1174,6 +1182,10 @@ function processMathop(item) { with(item) { } var bitsLeft = ident2 ? ident2.substr(2, ident2.length-3) : null; // remove (i and ), to leave number. This value is important in float ops + function integerizeBignum(value) { + return '(tempBigInt=(' + value + '), tempBigInt-tempBigInt%1)'; + } + switch (op) { // basic integer ops case 'add': return handleOverflow(ident1 + ' + ' + ident2, bits); @@ -1217,15 +1229,17 @@ function processMathop(item) { with(item) { } } */ - if (bits > 32) return ident1 + '*Math.pow(2,' + ident2 + ')'; + if (bits > 32) return ident1 + '*Math.pow(2,' + ident2 + ')'; // TODO: calculate Math.pow at runtime for consts, and below too return ident1 + ' << ' + ident2; } case 'ashr': { - if (bits > 32) return ident1 + '/Math.pow(2,' + ident2 + ')'; + if (bits > 32) return integerizeBignum(ident1 + '/Math.pow(2,' + ident2 + ')'); + if (bits === 32) return originalIdent1 + ' >> ' + ident2; // No need to reSign in this case return ident1 + ' >> ' + ident2; } case 'lshr': { - if (bits > 32) return ident1 + '/Math.pow(2,' + ident2 + ')'; + if (bits > 32) return integerizeBignum(ident1 + '/Math.pow(2,' + ident2 + ')'); + if (bits === 32) return originalIdent1 + ' >>> ' + ident2; // No need to unSign in this case return ident1 + ' >>> ' + ident2; } // basic float ops diff --git a/src/preamble.js b/src/preamble.js index 35bb75d7..fe2a0cb4 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -31,7 +31,11 @@ function SAFE_HEAP_ACCESS(dest, type, store, ignore) { } else { #if USE_TYPED_ARRAYS == 0 if (!HEAP[dest] && HEAP[dest] !== 0 && HEAP[dest] !== false) { // false can be the result of a mathop comparator - throw('Warning: Reading an invalid value at ' + dest + ' :: ' + new Error().stack + '\n'); + var error = true; + try { + if (HEAP[dest].toString() === 'NaN') error = false; // NaN is acceptable, as a double value + } catch(e){} + if (error) throw('Warning: Reading an invalid value at ' + dest + ' :: ' + new Error().stack + '\n'); } #endif if (type === null) return; @@ -216,7 +220,7 @@ var CorrectionsMonitor = { items.sort(function(x, y) { return y.total - x.total; }); for (var i = 0; i < items.length; i++) { var item = items[i]; - print(item.sig + ' : ' + item.total + ' hits, %' + (Math.floor(100*item.fails/item.total)) + ' failures'); + print(item.sig + ' : ' + item.total + ' hits, %' + (Math.ceil(100*item.fails/item.total)) + ' failures'); } } }; @@ -274,6 +278,90 @@ var INDENT = ''; var START_TIME = Date.now(); #endif +#if PROFILE +var PROFILING = 0; +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; + +function printProfiling() { + function dumpData(name_, node, indent) { + print(indent + ('________' + node.time).substr(-8) + ': ' + name_ + ' (' + node.calls + ')'); + var children = []; + for (var child in node.children) { + children.push(node.children[child]); + children[children.length-1].name_ = child; + } + children.sort(function(x, y) { return y.time - x.time }); + children.forEach(function(child) { dumpData(child.name_, child, indent + ' ') }); + } + dumpData('root', PROFILING_ROOT, ' '); +} +Module['printProfiling'] = printProfiling; + +function printXULProfiling() { + function dumpData(name_, node, indent) { + var children = []; + for (var child in node.children) { + children.push(node.children[child]); + children[children.length-1].name_ = child; + } + print('<treeitem' + (children.length > 0 ? ' container="true"' : '') + '>'); + print(' <treerow>'); + print(' <treecell label="' + name_ + '"/>'); + print(' <treecell label="' + node.time + '"/>'); + print(' <treecell label="' + node.calls + '"/>'); + print(' </treerow>'); + + if (children.length > 0) { + print(' <treechildren>'); + children.sort(function(x, y) { return y.time - x.time }); + children.forEach(function(child) { dumpData(child.name_, child, indent + ' ') }); + print(' </treechildren>'); + } + + print('</treeitem>'); + } + + print('<?xml version="1.0"?>'); + print('<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> '); + print('<?xml-stylesheet href="file://C:/main.css" type="text/css"?> '); + print('<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> '); + print('<tree id="myTree" flex="1" hidecolumnpicker="false" seltype="single" class="tree"'); + print(' rows="5">'); + print(' <treecols id="myTree2-treeCols">'); + print(' <treecol id="myTree2-treeCol0" primary="true" flex="2" label="Name"'); + print(' persist="width" ordinal="1"/>'); + print(' <splitter class="tree-splitter" ordinal="2"/>'); + print(' <treecol id="myTree2-treeCol1" flex="1" label="Milliseconds"'); + print(' persist="width" ordinal="3"/>'); + print(' <treecol id="myTree2-treeCol2" flex="1" label="Calls"'); + print(' persist="width" ordinal="4"/>'); + print(' </treecols>'); + print(' <treechildren>'); + + dumpData('root', PROFILING_ROOT, ' '); + + print(' </treechildren>'); + print('</tree>'); + print('</window>'); + + // This requires dom.allow_XUL_XBL_for_file +} +Module['printXULProfiling'] = printXULProfiling; +#endif + //======================================== // Runtime essentials //======================================== @@ -288,6 +376,7 @@ var __ATEXIT__ = []; var ABORT = false; var undef = 0; +var tempValue, tempInt, tempBigInt; function abort(text) { print(text + ':\n' + (new Error).stack); @@ -345,6 +434,9 @@ Module['getValue'] = getValue; var ALLOC_NORMAL = 0; // Tries to use _malloc() var ALLOC_STACK = 1; // Lives for the duration of the current function call var ALLOC_STATIC = 2; // Cannot be freed +Module['ALLOC_NORMAL'] = ALLOC_NORMAL; +Module['ALLOC_STACK'] = ALLOC_STACK; +Module['ALLOC_STATIC'] = ALLOC_STATIC; function allocate(slab, types, allocator) { var zeroinit, size; @@ -411,6 +503,8 @@ Module['Array_stringify'] = Array_stringify; // Memory management +var FUNCTION_TABLE; + var PAGE_SIZE = 4096; function alignMemoryPage(x) { return Math.ceil(x/PAGE_SIZE)*PAGE_SIZE; diff --git a/src/runtime.js b/src/runtime.js index abeb0d2a..dcb10de9 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -72,6 +72,13 @@ function unInline(name_, params) { } Runtime = { + stackSave: function() { + return STACKTOP; + }, + stackRestore: function(stackTop) { + STACKTOP = stackTop; + }, + forceAlign: function(target, quantum) { quantum = quantum || QUANTUM_SIZE; if (isNumber(target) && isNumber(quantum)) { @@ -130,7 +137,7 @@ Runtime = { size = Types.types[field].flatSize; alignSize = Types.types[field].alignSize; } else { - dprint('Unclear type in struct: ' + field + ', in ' + type.name_); + dprint('Unclear type in struct: ' + field + ', in ' + type.name_ + ' :: ' + dump(Types.types[type.name_])); assert(0); } alignSize = type.packed ? 1 : Math.min(alignSize, QUANTUM_SIZE); @@ -268,7 +275,7 @@ function reSign(value, bits, ignore, sig) { #if CHECK_SIGNS var noted = false; #endif - if (value >= half) { + if (value >= half && (bits <= 32 || value > half)) { // for huge values, we can hit the precision limit and always get true here. so don't do that #if CHECK_SIGNS if (!ignore) { CorrectionsMonitor.note('ReSign', 0, sig); diff --git a/src/settings.js b/src/settings.js index ab532e67..d94bd7ca 100644 --- a/src/settings.js +++ b/src/settings.js @@ -3,15 +3,10 @@ QUANTUM_SIZE = 4; // This is the size of an individual field in a structure. 1 w // lead to e.g. doubles and chars both taking 1 memory address. This // is a form of 'compressed' memory, with shrinking and stretching // according to the type, when compared to C/C++. On the other hand - // 8 means all fields take 8 memory addresses, so a double takes - // the same as a char. Note that we only actually store something in - // the top address - the others are just empty, an 'alignment cost' - // of sorts. + // the normal value of 4 means all fields take 4 memory addresses, + // as per the norm on a 32-bit machine. // - // 1 is somewhat faster, but dangerous. - // - // TODO: Cleverly analyze malloc, memset, memcpy etc. operations in - // llvm, and replace with the proper values for us + // 1 is somewhat faster than 4, but dangerous. CORRECT_SIGNS = 1; // Whether we make sure to convert unsigned values to signed values. // Decreases performance with additional runtime checks. Might not be @@ -69,14 +64,15 @@ SAFE_HEAP_LOG = 0; // Log out all SAFE_HEAP operations LABEL_DEBUG = 0; // Print out labels and functions as we enter them EXCEPTION_DEBUG = 1; // Print out exceptions in emscriptened code -DISABLE_EXCEPTIONS = 0; // Disables generating code to actually catch exceptions. If the code you - // are compiling does not actually rely on catching exceptions (but the - // compiler generates code for it, maybe because of stdlibc++ stuff), - // then this can make it much faster. If an exception actually happens, - // it will not be caught and the program will halt (so this will not - // introduce silent failures, which is good). - // TODO: Make this also remove cxa_begin_catch etc., optimize relooper - // for it, etc. (perhaps do all of this as preprocessing on .ll?) +LIBRARY_DEBUG = 0; // Print out when we enter a library call (library*.js) +DISABLE_EXCEPTION_CATCHING = 0; // Disables generating code to actually catch exceptions. If the code you + // are compiling does not actually rely on catching exceptions (but the + // compiler generates code for it, maybe because of stdlibc++ stuff), + // then this can make it much faster. If an exception actually happens, + // it will not be caught and the program will halt (so this will not + // introduce silent failures, which is good). + // TODO: Make this also remove cxa_begin_catch etc., optimize relooper + // for it, etc. (perhaps do all of this as preprocessing on .ll?) EXECUTION_TIMEOUT = -1; // Throw an exception after X seconds - useful to debug infinite loops CHECK_OVERFLOWS = 0; // Add code that checks for overflows in integer math operations. // There is currently not much to do to handle overflows if they occur. @@ -113,6 +109,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. + EXPORTED_FUNCTIONS = ['_main']; // Functions that are explicitly exported, so they are guaranteed to // be accessible outside of the generated code. @@ -125,6 +123,17 @@ INCLUDE_FULL_LIBRARY = 0; // Whether to include the whole library rather than ju // dynamically loading modules that make use of runtime // library functions that are not used in the main module. +// A set of defines, for example generated from your header files. This +// lets the emscripten libc (library.js) see the right values. +// The default value here has been generated from system/include. If you +// modify those files, or use different headers, you will need to override +// this. +C_DEFINES = {"M_SQRTPI":"1.77245385091","__RAND_MAX":"2147483647","__FILENAME_MAX__":"255","math_errhandling":"1","M_LOG10E":"0.434294481903","_M_LN2":"0.69314718056","__LONG_MAX__":"2147483647","_POSIX_JOB_CONTROL":"1","FLT_EVAL_METHOD":"0","__BUFSIZ__":"16","_XOPEN_SHM":"1","_POSIX_CHOWN_RESTRICTED":"1","_POSIX_THREAD_ATTR_STACKSIZE":"200112","LONG_MAX":"2147483647","_POSIX_TIMERS":"1","_POSIX2_C_DEV":"200112","_READ_WRITE_RETURN_TYPE":"<type 'int'>","_POSIX_THREAD_SPORADIC_SERVER":"1","SCHED_SPORADIC":"4","_REENT_ASCTIME_SIZE":"26","_POSIX_THREAD_CPUTIME":"1","SHRT_MIN":"-32768","PTHREAD_MUTEX_ERRORCHECK":"2","_LIBC_LIMITS_H_":"1","M_LN2":"0.69314718056","M_LN2HI":"0.693147180369","UINT_MAX":"2147483647","FP_NORMAL":"4","_POSIX_SHARED_MEMORY_OBJECTS":"200112","_POSIX_DEVCTL_DIRECTION":"1","_POSIX_MEMLOCK":"1","FP_NAN":"0","M_PI":"3.14159265359","_POSIX_THREAD_PRIO_PROTECT":"1","___int_least16_t_defined":"1","PTHREAD_SCOPE_SYSTEM":"1","_POSIX_INTERRUPT_CONTROL":"1","NBBY":"8","__LARGE64_FILES":"1","PTHREAD_SCOPE_PROCESS":"0","TLOSS":"5","PTHREAD_CREATE_JOINABLE":"1","M_PI_2":"1.57079632679","_TIME_T_":"<type 'long'>","DOMAIN":"1","SCHAR_MIN":"-128","M_PI_4":"0.785398163397","_POSIX_SPORADIC_SERVER":"1","CHAR_MAX":"127","_POINTER_INT":"<type 'long'>","PTHREAD_STACK_MIN":"200","USHRT_MAX":"65535","_POSIX_V6_ILP32_OFF32":"-1","UCHAR_MAX":"255","__INT_MAX__":"2147483647","_XBS5_LP64_OFF64":"-1","_UNIX98_THREAD_MUTEX_ATTRIBUTES":"1","_POSIX_REALTIME_SIGNALS":"200112","M_E":"2.71828182846","_RAND48_SEED_1":"43981","_RAND48_SEED_0":"13070","_RAND48_SEED_2":"4660","_POSIX_FSYNC":"200112","CHAR_MIN":"-128","_MB_EXTENDED_CHARSETS_WINDOWS":"1","FP_INFINITE":"1","_N_LISTS":"30","___int_least32_t_defined":"1","MB_LEN_MAX":"1","__USE_XOPEN2K":"1","PTHREAD_MUTEX_DEFAULT":"3","_POSIX_REGEXP":"1","PTHREAD_PROCESS_PRIVATE":"0","FP_SUBNORMAL":"3","_POSIX_DEVICE_CONTROL":"1","PTHREAD_PRIO_NONE":"0","___int64_t_defined":"1","_POSIX_THREAD_PRIO_INHERIT":"1","_POSIX_CPUTIME":"1","_XBS5_ILP32_OFFBIG":"1","_POSIX_SPIN_LOCKS":"200112","ARG_MAX":"4096","HUGE_VAL":"inf","PLOSS":"6","_POSIX2_VERSION":"200112","M_LN10":"2.30258509299","_POSIX_THREAD_SAFE_FUNCTIONS":"200112","_POSIX2_CHAR_TERM":"200112","SCHED_RR":"2","_POSIX_C_SOURCE":"2","_XOPEN_ENH_I18N":"1","M_IVLN10":"0.434294481903","_POSIX_SAVED_IDS":"1","_POSIX_MEMLOCK_RANGE":"200112","PTHREAD_INHERIT_SCHED":"1","___int16_t_defined":"1","_LIMITS_H":"1","OVERFLOW":"3","PTHREAD_PROCESS_SHARED":"1","___int8_t_defined":"1","_POSIX2_UPE":"200112","CHAR_BIT":"8","MALLOC_ALIGNMENT":"16","PTHREAD_MUTEX_RECURSIVE":"1","PTHREAD_CREATE_DETACHED":"0","M_LOG2_E":"0.69314718056","M_LOG2E":"1.44269504089","_POSIX_BARRIERS":"200112","H8300":"1","_POSIX_SHELL":"1","_POSIX_MEMORY_PROTECTION":"200112","FP_ILOGBNAN":"2147483647","_POSIX_RAW_SOCKETS":"200112","M_2_SQRTPI":"1.1283791671","PTHREAD_EXPLICIT_SCHED":"2","_POSIX_PRIORITIZED_IO":"1","PATH_MAX":"4096","_POSIX_THREAD_PROCESS_SHARED":"200112","_POSIX2_C_BIND":"200112","_POSIX_V6_LP64_OFF64":"-1","_POSIX_VERSION":"200112","_POSIX_SPAWN":"1","SCHED_FIFO":"1","M_LN2LO":"1.90821492927e-10","_POSIX_ADVISORY_INFO":"200112","_NULL":"0","_POSIX_V6_LPBIG_OFFBIG":"-1","_XOPEN_VERSION":"600","_POSIX_SYNCHRONIZED_IO":"200112","_MB_EXTENDED_CHARSETS_ISO":"1","_POSIX_MAPPED_FILES":"200112","SCHAR_MAX":"127","_POSIX_MONOTONIC_CLOCK":"200112","ULONG_MAX":"2147483647","SHRT_MAX":"32767","_RAND48_MULT_0":"58989","_RAND48_MULT_1":"57068","_RAND48_MULT_2":"5","PTHREAD_MUTEX_NORMAL":"0","NL_ARGMAX":"32","M_1_PI":"0.318309886184","UNDERFLOW":"4","PTHREAD_PRIO_INHERIT":"1","FP_ZERO":"2","SING":"2","___int32_t_defined":"1","M_INVLN2":"1.44269504089","M_2_PI":"0.636619772368","M_TWOPI":"3.14159265359","_POSIX_ASYNCHRONOUS_IO":"1","_POSIX2_RE_DUP_MAX":"255","M_3PI_4":"2.35619449019","_FLOAT_ARG":"<type 'float'>","_POSIX_MESSAGE_PASSING":"200112","_POSIX_THREAD_PRIORITY_SCHEDULING":"200112","SCHED_OTHER":"0","_XOPEN_CRYPT":"1","_ATEXIT_SIZE":"32","_POSIX2_SW_DEV":"200112","_POSIX_PRIORITY_SCHEDULING":"200112","_LARGEFILE64_SOURCE":"1","_REENT_SIGNAL_SIZE":"24","FD_SETSIZE":"64","_POSIX_SEMAPHORES":"200112","_XBS5_ILP32_OFF32":"-1","_POSIX_IPV6":"200112","_LONG_LONG_TYPE":"<type 'long'>","___int_least8_t_defined":"1","INT_MAX":"2147483647","_POSIX_V6_ILP32_OFFBIG":"1","PTHREAD_PRIO_PROTECT":"2","_RAND48_ADD":"11","_REENT_EMERGENCY_SIZE":"25","_POSIX_READER_WRITER_LOCKS":"200112","_XBS5_LPBIG_OFFBIG":"-1","_POSIX_NO_TRUNC":"1","_POSIX_TIMEOUTS":"1","_POSIX_THREAD_ATTR_STACKADDR":"1","M_SQRT3":"1.73205080757","M_SQRT2":"1.41421356237","_POSIX_THREADS":"200112","MATH_ERREXCEPT":"2","MATH_ERRNO":"1","M_SQRT1_2":"0.707106781187", + F_GETLK64: 20, + F_SETLK64: 21, + F_SETLKW64: 22 +}; + SHOW_LABELS = 0; // Show labels in the generated code BUILD_AS_SHARED_LIB = 0; // Whether to build the code as a shared library, which @@ -149,4 +158,5 @@ DEBUG_TAGS_SHOWING = []; // vars // relooping // unparsedFunctions + // metadata |