diff options
-rwxr-xr-x | emscripten.py | 28 | ||||
-rw-r--r-- | settings.py | 2 | ||||
-rw-r--r-- | src/analyzer.js | 31 | ||||
-rw-r--r-- | src/intertyper.js | 94 | ||||
-rw-r--r-- | src/jsifier.js | 66 | ||||
-rw-r--r-- | src/library.js | 12 | ||||
-rw-r--r-- | src/modules.js | 45 | ||||
-rw-r--r-- | src/parseTools.js | 33 | ||||
-rw-r--r-- | src/preamble.js | 1 | ||||
-rw-r--r-- | src/runtime.js | 2 | ||||
-rw-r--r-- | src/settings.js | 2 | ||||
-rw-r--r-- | tests/cases/aliasbitcast.ll | 37 | ||||
-rw-r--r-- | tests/cases/gepoverflow_q1.txt | 1 | ||||
-rw-r--r-- | tests/cases/storestruct_q1.txt | 3 | ||||
-rw-r--r-- | tests/dlmalloc.c | 32 | ||||
-rw-r--r-- | tests/runner.py | 102 | ||||
-rw-r--r-- | tools/autodebugger.py | 19 | ||||
-rw-r--r-- | tools/shared.py | 13 |
18 files changed, 371 insertions, 152 deletions
diff --git a/emscripten.py b/emscripten.py index 022068c8..21abcc1d 100755 --- a/emscripten.py +++ b/emscripten.py @@ -11,11 +11,6 @@ from tools import shared # Temporary files that should be deleted once the program is finished. TEMP_FILES_TO_CLEAN = [] -# The data layout used by llvm-gcc (as opposed to clang, which doesn't have the -# f128:128:128 part). -GCC_DATA_LAYOUT = ('target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16' - '-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64' - '-v128:128:128-a0:0:64-f80:32:32-f128:128:128-n8:16:32"') def path_from_root(*target): @@ -98,37 +93,20 @@ def link(*objects): return out.name -def compile_malloc(compiler): +def compile_malloc(): """Compiles dlmalloc to LLVM bitcode. - Args: - compiler: The compiler command to use, a path to either clang or llvm-gcc. - Returns: The path to the compiled dlmalloc as an LLVM bitcode (.bc) file. """ src = path_from_root('src', 'dlmalloc.c') includes = '-I' + path_from_root('src', 'include') - command = [compiler, '-c', '-g', '-emit-llvm', '-m32', '-o-', includes, src] + command = [shared.CLANG, '-c', '-g', '-emit-llvm', '-m32', '-o-', includes, src] with get_temp_file('.bc') as out: ret = subprocess.call(command, stdout=out) if ret != 0: raise RuntimeError('Could not compile dlmalloc.') return out.name -def determine_compiler(filepath): - """Determines whether a given file uses llvm-gcc or clang data layout. - - Args: - filepath: The .bc or .ll file containing the bitcode/assembly to test. - - Returns: - The path to the compiler, either llvm-gcc or clang. - """ - assembly = open(disassemble(filepath)).read() - is_gcc = GCC_DATA_LAYOUT in assembly - return shared.to_cc(shared.LLVM_GCC if is_gcc else shared.CLANG) - - def has_annotations(filepath): """Tests whether an assembly file contains annotations. @@ -164,7 +142,7 @@ def main(args): if args.dlmalloc or args.optimize or not has_annotations(args.infile): args.infile = assemble(args.infile) if args.dlmalloc: - malloc = compile_malloc(determine_compiler(args.infile)) + malloc = compile_malloc() args.infile = link(args.infile, malloc) if args.optimize: args.infile = optimize(args.infile) args.infile = disassemble(args.infile) diff --git a/settings.py b/settings.py index c6d63341..fe9297f7 100644 --- a/settings.py +++ b/settings.py @@ -7,8 +7,6 @@ TEMP_DIR='/dev/shm' LLVM_ROOT=os.path.expanduser('~/Dev/llvm-2.9/cbuild/bin') -LLVM_GCC=os.path.expanduser('~/Dev/llvm-gcc-2.9/cbuild/install/bin/llvm-g++') - COMPILER_OPTS = ['-m32'] # Need to build as 32bit arch, for now - # various errors on 64bit compilation # WARNING: '-g' here will generate llvm bitcode that lli will crash on! 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/intertyper.js b/src/intertyper.js index 83a49645..280b8a3f 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) { @@ -607,9 +644,10 @@ 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 @@ -629,6 +667,15 @@ function intertyper(data, parseFunctions, baseLineNum) { return makeCall.call(this, item, 'invoke'); } }); + // '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' var allocaPossibleVars = ['allocatedNum']; substrate.addActor('Alloca', { @@ -704,7 +751,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 +780,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 +801,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 c19eda3a..aa1e3c60 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, @@ -257,17 +259,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 @@ -283,7 +286,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]; @@ -318,6 +321,9 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { snippet = snippet.toString(); // 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'; @@ -652,10 +658,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 ';'; @@ -685,9 +691,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); } @@ -705,6 +711,9 @@ 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 @@ -720,6 +729,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; @@ -736,6 +749,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); }); @@ -817,6 +839,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 ? ';' : ''); }); @@ -829,11 +852,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); diff --git a/src/library.js b/src/library.js index 62e4de55..66fa72ae 100644 --- a/src/library.js +++ b/src/library.js @@ -805,6 +805,8 @@ LibraryManager.library = { __01fstat64_: 'fstat', __01stat64_: 'stat', __01lstat64_: 'lstat', + stat64: 'stat', + // TODO: Check if other aliases are needed. // ========================================================================== @@ -2814,6 +2816,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); @@ -2852,6 +2855,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); @@ -3077,6 +3081,7 @@ LibraryManager.library = { vscanf: 'scanf', vfscanf: 'fscanf', vsscanf: 'sscanf', + fopen64: 'fopen', __01fopen64_: 'fopen', __01fseeko64_: 'fseek', __01ftello64_: 'ftell', @@ -4046,6 +4051,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 // ========================================================================== diff --git a/src/modules.js b/src/modules.js index f613c20b..8205e9ff 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'), + 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+, 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 @@ -263,6 +272,12 @@ 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); } }; diff --git a/src/parseTools.js b/src/parseTools.js index a3347139..0e30aa5e 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -53,6 +53,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. |