diff options
-rw-r--r-- | src/intertyper.js | 21 | ||||
-rw-r--r-- | src/jsifier.js | 51 | ||||
-rw-r--r-- | src/library.js | 15 | ||||
-rw-r--r-- | src/parseTools.js | 60 | ||||
-rw-r--r-- | tests/runner.py | 16 |
5 files changed, 103 insertions, 60 deletions
diff --git a/src/intertyper.js b/src/intertyper.js index 9e3f7f1e..715b6fa4 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -1,6 +1,8 @@ // LLVM assembly => internal intermediate representation, which is ready // to be processed by the later stages. +var tokenizer; // TODO: Clean this up/out + //! @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 //! lines and data in memory at the same time. @@ -92,7 +94,7 @@ function intertyper(data, parseFunctions, baseLineNum) { }; // Line tokenizer - var tokenizer = substrate.addActor('Tokenizer', { + tokenizer = substrate.addActor('Tokenizer', { processItem: function(item, inner) { //assert(item.lineNum != 40000); //if (item.lineNum) print(item.lineNum); @@ -327,7 +329,7 @@ function intertyper(data, parseFunctions, baseLineNum) { // Handle a single segment (after comma separation) function handleSegment(segment) { if (segment[1].text == 'null') { - return { intertype: 'value', value: 0 }; + return { intertype: 'value', value: 0, type: 'i32' }; } else if (segment[1].text == 'zeroinitializer') { return { intertype: 'emptystruct', type: segment[0].text }; } else if (segment[1].text in PARSABLE_LLVM_FUNCTIONS) { @@ -339,12 +341,12 @@ function intertyper(data, parseFunctions, baseLineNum) { } else if (segment[1].type && segment[1].type == '[') { return { intertype: 'list', type: segment[0].text, contents: handleSegments(segment[1].item.tokens) }; } else if (segment.length == 2) { - return { intertype: 'value', value: toNiceIdent(segment[1].text) }; + return { intertype: 'value', type: segment[0].text, value: toNiceIdent(segment[1].text) }; } else if (segment[1].text === 'c') { // string var text = segment[2].text; text = text.substr(1, text.length-2); - return { intertype: 'string', text: text }; + return { intertype: 'string', text: text, type: 'i8*' }; } else if (segment[1].text === 'blockaddress') { return parseBlockAddress(segment); } else { @@ -636,15 +638,17 @@ function intertyper(data, parseFunctions, baseLineNum) { processItem: function(item) { item.intertype = 'phi'; item.type = item.tokens[1].text; + var typeToken = [item.tokens[1]]; Types.needAnalysis[item.type] = 0; var last = getTokenIndexByText(item.tokens, ';'); item.params = splitTokenList(item.tokens.slice(2, last)).map(function(segment) { var subSegments = splitTokenList(segment[0].item.tokens); - return { + var ret = { intertype: 'phiparam', label: toNiceIdent(subSegments[1][0].text), - value: parseLLVMSegment(subSegments[0]) + value: parseLLVMSegment(typeToken.concat(subSegments[0])) }; + return ret; }); this.forwardItem(item, 'Reintegrator'); } @@ -675,6 +679,9 @@ function intertyper(data, parseFunctions, baseLineNum) { } else { item.type = item.param1.type; } + for (var i = 1; i <= 4; i++) { + if (item['param'+i]) item['param'+i].type = item.type; // All params have the same type + } Types.needAnalysis[item.type] = 0; this.forwardItem(item, 'Reintegrator'); } @@ -726,7 +733,7 @@ function intertyper(data, parseFunctions, baseLineNum) { return [{ intertype: 'return', type: type, - value: (item.tokens[2] && type !== 'void') ? parseLLVMSegment(item.tokens.slice(2)) : null, + value: (item.tokens[2] && type !== 'void') ? parseLLVMSegment(item.tokens.slice(1)) : null, lineNum: item.lineNum }]; } diff --git a/src/jsifier.js b/src/jsifier.js index 62acf8d7..7784c1ab 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -3,8 +3,10 @@ // Main function function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { + var mainPass = !functionsOnly; + // Add additional necessary items for the main pass - if (!functionsOnly) { + if (mainPass) { var libFuncsToInclude; if (INCLUDE_FULL_LIBRARY) { assert(!BUILD_AS_SHARED_LIB, 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB set.') @@ -31,11 +33,12 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { substrate = new Substrate('JSifyer'); - var GLOBAL_VARIABLES = functionsOnly ? givenGlobalVariables : data.globalVariables; + var GLOBAL_VARIABLES = !mainPass ? givenGlobalVariables : data.globalVariables; + + Functions.currFunctions = !mainPass ? givenFunctions : {}; - Functions.currFunctions = functionsOnly ? givenFunctions : {}; - // Now that analysis has completed, we can get around to handling unparsedFunctions - (functionsOnly ? data.functions : data.unparsedFunctions.concat(data.functions)).forEach(function(func) { + // Now that first-pass analysis has completed (so we have basic types, etc.), we can get around to handling unparsedFunctions + (!mainPass ? data.functions : data.unparsedFunctions.concat(data.functions)).forEach(function(func) { // Save just what we need, to save memory Functions.currFunctions[func.ident] = { hasVarArgs: func.hasVarArgs, @@ -57,7 +60,11 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { if (data.unparsedFunctions.length > 0) { // We are now doing the final JS generation dprint('unparsedFunctions', '== Completed unparsedFunctions ==\n'); - //Debugging.clear(); // Save some memory, before the final heavy lifting + + // Save some memory, before the final heavy lifting + //Functions.currFunctions = null; + //Functions.currExternalFunctions = null; + //Debugging.clear(); } // Actors @@ -117,7 +124,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { if (value.intertype in PARSABLE_LLVM_FUNCTIONS) { return [finalizeLLVMFunctionCall(value)]; } else if (Runtime.isNumberType(type) || pointingLevels(type) >= 1) { - return indexizeFunctions(parseNumerical(value.value)); + return indexizeFunctions(parseNumerical(value.value), type); } else if (value.intertype === 'emptystruct') { return makeEmptyStruct(type); } else if (value.intertype === 'string') { @@ -128,23 +135,26 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { function handleSegments(tokens) { // Handle a single segment (after comma separation) function handleSegment(segment) { + var ret; if (segment.intertype === 'value') { - return segment.value.toString(); + ret = segment.value.toString(); } else if (segment.intertype === 'emptystruct') { - return makeEmptyStruct(segment.type); + ret = makeEmptyStruct(segment.type); } else if (segment.intertype in PARSABLE_LLVM_FUNCTIONS) { - return finalizeLLVMFunctionCall(segment); + ret = finalizeLLVMFunctionCall(segment); } else if (segment.intertype in set('struct', 'list')) { - return alignStruct(handleSegments(segment.contents), segment.type); + ret = alignStruct(handleSegments(segment.contents), segment.type); } else if (segment.intertype === 'string') { - return parseLLVMString(segment.text); // + ' /* ' + text + '*/'; + ret = parseLLVMString(segment.text); // + ' /* ' + text + '*/'; } else if (segment.intertype === 'blockaddress') { - return finalizeBlockAddress(segment); + ret = finalizeBlockAddress(segment); } else { throw 'Invalid segment: ' + dump(segment); } + assert(segment.type, 'Missing type for constant segment: ' + dump(segment)); + return indexizeFunctions(ret, segment.type); }; - return tokens.map(handleSegment).map(indexizeFunctions); + return tokens.map(handleSegment) } return alignStruct(handleSegments(value.contents), type); } @@ -292,7 +302,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { } item.JS = addFromLibrary(shortident); } else { - item.JS = '// stub for ' + item.ident; + item.JS = 'var ' + item.ident + '; // stub for ' + item.ident; } return ret; } @@ -720,6 +730,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { var func = Functions.currFunctions[ident]; var args = []; + var argsTypes = []; var varargs = []; var varargsTypes = []; @@ -727,6 +738,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { var val = finalizeParam(param); if (!func || !func.hasVarArgs || i < func.numParams-1) { // unrecognized functions (like library ones) cannot have varargs args.push(val); + argsTypes.push(param.type); } else { varargs.push(val); varargs = varargs.concat(zeros(getNativeFieldSize(param.type)-1)); @@ -735,8 +747,9 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { } }); - args = args.map(indexizeFunctions); - varargs = varargs.map(indexizeFunctions); + args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) }); + varargs = varargs.map(function(vararg, i) { return vararg === 0 ? 0 : indexizeFunctions(vararg, varargsTypes[i]) }); + if (func && func.hasVarArgs) { if (varargs.length === 0) { varargs = [0]; @@ -771,12 +784,12 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { items = null; var generated = []; - if (!functionsOnly) { + if (mainPass) { generated = generated.concat(itemsDict.type).concat(itemsDict.GlobalVariableStub).concat(itemsDict.functionStub); } generated = generated.concat(itemsDict.function).concat(data.unparsedFunctions); - if (functionsOnly) return generated.map(function(item) { return item.JS }).join('\n'); + if (!mainPass) return generated.map(function(item) { return item.JS }).join('\n'); // We are ready to print out the data, but must do so carefully - we are // dealing with potentially *huge* strings. Convenient replacements and diff --git a/src/library.js b/src/library.js index 284f093d..685f839a 100644 --- a/src/library.js +++ b/src/library.js @@ -1476,7 +1476,17 @@ var Library = { // ========================================================================== - // dlfcn.h + // dlfcn.h - Dynamic library loading + // + // Some limitations: + // + // * Minification on each file separately may not work, as they will + // have different shortened names. You can in theory combine them, then + // minify, then split... perhaps. + // + // * LLVM optimizations may fail. If the child wants to access a function + // in the parent, LLVM opts may remove it from the parent when it is + // being compiled. Not sure how to tell LLVM to not do so. // ========================================================================== // Data for dlfcn.h. @@ -1510,6 +1520,9 @@ var Library = { try { var lib_module = eval(lib_data)(FUNCTION_TABLE.length); } catch (e) { +#if ASSERTIONS + print('Error in loading dynamic library: ' + e); +#endif DLFCN_DATA.isError = true; return 0; } diff --git a/src/parseTools.js b/src/parseTools.js index 4f0cda35..debfb6da 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -144,11 +144,10 @@ function isFunctionDef(token) { function isFunctionType(type) { var parts = type.split(' '); - if (parts.length != 2) return false; if (pointingLevels(type) !== 1) return false; - var text = removeAllPointing(parts[1]); - var ret = isType(parts[0]) && isFunctionDef({ text: text, item: {tokens: [{text: text.substr(1, text.length-2)}]} }); - return ret; + 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) }); } function isType(type) { // TODO! @@ -346,13 +345,20 @@ function finalizeParam(param) { function parseLLVMSegment(segment) { var type; if (segment.length == 1) { - type = isType(segment[0].text) ? segment[0].text : '?'; - Types.needAnalysis[type] = 0; - return { - intertype: 'value', - ident: toNiceIdent(segment[0].text), - type: type - }; + if (isType(segment[0].text)) { + Types.needAnalysis[segment[0].text] = 0; + return { + intertype: 'type', + ident: toNiceIdent(segment[0].text), + type: segment[0].text + }; + } else { + return { + intertype: 'value', + ident: toNiceIdent(segment[0].text), + type: 'i32' + }; + } } else if (segment[1].type && segment[1].type == '{') { type = segment[0].text; Types.needAnalysis[type] = 0; @@ -694,27 +700,16 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned) { } } -function indexizeFunctions(value) { - if (value in Functions.currFunctions) { +function indexizeFunctions(value, type) { + assert((type && type !== '?') || (typeof value === 'string' && value.substr(0, 6) === 'CHECK_'), 'No type given for function indexizing: ' + [value, type]); + assert(value !== type, 'Type set to value: ' + [value, type]); + if (type && isFunctionType(type) && value[0] === '_') { // checking for _ differentiates from $ (local vars) if (BUILD_AS_SHARED_LIB) { return '(FUNCTION_TABLE_OFFSET + ' + Functions.getIndex(value) + ')'; } else { return Functions.getIndex(value); } } - if (value && value[0] && value[0] == '_') { - var rootIdent = LibraryManager.getRootIdent(value.slice(1)); - if (!rootIdent) return value; - if (typeof Library[rootIdent] === 'function') { - return Functions.getIndex('_' + rootIdent); - } else if (rootIdent.substr(0, 5) === 'Math.') { - // Library[..] can be a string, in which case we apply that string. There is one - // case where this can be a function: Math.*, since we try to optimize those as much - // as possible. In other words, we don't want to have a wrapper function(x) return Math.sqrt(x). - // If other functions are deemed important as well, we will need to add them here. - return Functions.getIndex(rootIdent); - } - } return value; } @@ -738,7 +733,7 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore) { return ret.join('; '); } - value = indexizeFunctions(value); + value = indexizeFunctions(value, type); var offset = calcFastOffset(ptr, pos, noNeedFirst); if (SAFE_HEAP) { if (type !== 'null' && type[0] !== '#') type = '"' + safeQuote(type) + '"'; @@ -1046,7 +1041,7 @@ function finalizeLLVMParameter(param) { if (isNumber(param)) { return param; } else if (typeof param === 'string') { - ret = toNiceIdentCarefully(param); + return toNiceIdentCarefully(param); } else if (param.intertype in PARSABLE_LLVM_FUNCTIONS) { ret = finalizeLLVMFunctionCall(param); } else if (param.intertype == 'value') { @@ -1055,10 +1050,13 @@ function finalizeLLVMParameter(param) { ret = param.values.map(finalizeLLVMParameter); } else if (param.intertype === 'blockaddress') { return finalizeBlockAddress(param); + } else if (param.intertype === 'type') { + return param.ident; // we don't really want the type here } else { throw 'invalid llvm parameter: ' + param.intertype; } - return indexizeFunctions(ret); + assert(param.type || (typeof param === 'string' && param.substr(0, 6) === 'CHECK_'), 'Missing type for param: ' + dump(param)); + return indexizeFunctions(ret, param.type); } function makeSignOp(value, type, op) { @@ -1264,8 +1262,6 @@ function processMathop(item) { with(item) { // TODO: Use this in analyzer, possibly also in jsifier function walkInterdata(item, pre, post, obj) { if (!item || !item.intertype) return false; -//print(' walk: ' + [item.lineNum, item.intertype]); -//if (item.intertype === 'value') print(dump(item)); if (pre(item, obj)) return true; var originalObj = obj; if (obj.replaceWith) obj = obj.replaceWith; // allow pre to replace the object we pass to all its children @@ -1283,7 +1279,7 @@ function walkInterdata(item, pre, post, obj) { } function parseBlockAddress(segment) { - return { intertype: 'blockaddress', func: toNiceIdent(segment[2].item.tokens[0].text), label: toNiceIdent(segment[2].item.tokens[2].text) }; + return { intertype: 'blockaddress', func: toNiceIdent(segment[2].item.tokens[0].text), label: toNiceIdent(segment[2].item.tokens[2].text), type: 'i32' }; } function finalizeBlockAddress(param) { diff --git a/tests/runner.py b/tests/runner.py index 4b433b13..fdf15d44 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -1924,14 +1924,24 @@ if 'benchmark' not in sys.argv: output_nicerizer=lambda x: x.replace('\n', '*')) def test_dlfcn_data_and_fptr(self): + global LLVM_OPTS + if LLVM_OPTS: return self.skip() # LLVM opts will optimize out parent_func + global BUILD_AS_SHARED_LIB, EXPORTED_FUNCTIONS, EXPORTED_GLOBALS lib_src = ''' #include <stdio.h> int global = 42; + extern void parent_func(); // a function that is defined in the parent + void lib_fptr() { printf("Second calling lib_fptr from main.\\n"); + parent_func(); + // call it also through a pointer, to check indexizing + void (*p_f)(); + p_f = parent_func; + p_f(); } void (*func(int x, void(*fptr)()))() { @@ -1956,6 +1966,10 @@ if 'benchmark' not in sys.argv: FUNCTYPE func; + void parent_func() { + printf("parent_func called from child\\n"); + } + void main_fptr() { printf("First calling main_fptr from lib.\\n"); } @@ -1997,7 +2011,7 @@ if 'benchmark' not in sys.argv: BUILD_AS_SHARED_LIB = 0 EXPORTED_FUNCTIONS = ['_main'] EXPORTED_GLOBALS = [] - self.do_test(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*Var: 42*', + self.do_test(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*parent_func called from child*parent_func called from child*Var: 42*', output_nicerizer=lambda x: x.replace('\n', '*')) def test_strtod(self): |