diff options
Diffstat (limited to 'src/jsifier.js')
-rw-r--r-- | src/jsifier.js | 1659 |
1 files changed, 931 insertions, 728 deletions
diff --git a/src/jsifier.js b/src/jsifier.js index 77aff895..35846d39 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -6,7 +6,6 @@ // Handy sets var STRUCT_LIST = set('struct', 'list'); -var UNDERSCORE_OPENPARENS = set('_', '('); var RELOOP_IGNORED_LASTS = set('return', 'unreachable', 'resume'); var addedLibraryItems = {}; @@ -14,14 +13,21 @@ var asmLibraryFunctions = []; var SETJMP_LABEL = -1; +var INDENTATION = ' '; + +var functionStubSigs = {}; + // JSifier -function JSify(data, functionsOnly, givenFunctions) { +function JSify(data, functionsOnly) { + //B.start('jsifier'); var mainPass = !functionsOnly; + var itemsDict = { type: [], GlobalVariableStub: [], functionStub: [], function: [], GlobalVariable: [], GlobalVariablePostSet: [] }; + if (mainPass) { - var shellFile = SHELL_FILE ? SHELL_FILE : (BUILD_AS_SHARED_LIB ? 'shell_sharedlib.js' : 'shell.js'); + var shellFile = SHELL_FILE ? SHELL_FILE : (BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'shell_sharedlib.js' : 'shell.js'); - if (phase == 'pre') { + if (phase == 'pre' || phase == 'glue') { // We will start to print out the data, but must do so carefully - we are // dealing with potentially *huge* strings. Convenient replacements and // manipulations may create in-memory copies, and we may OOM. @@ -42,28 +48,18 @@ function JSify(data, functionsOnly, givenFunctions) { // things out as they are ready. var shellParts = read(shellFile).split('{{BODY}}'); - print(shellParts[0]); - var preFile = BUILD_AS_SHARED_LIB ? 'preamble_sharedlib.js' : 'preamble.js'; + print(processMacros(preprocess(shellParts[0]))); + var preFile = BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'preamble_sharedlib.js' : 'preamble.js'; var pre = processMacros(preprocess(read(preFile).replace('{{RUNTIME}}', getRuntime()))); print(pre); + // Populate implementedFunctions. Note that this is before types, and will be updated later. data.unparsedFunctions.forEach(function(func) { Functions.implementedFunctions[func.ident] = Functions.getSignature(func.returnType, func.params.map(function(param) { return param.type })); }); } } - // Does simple 'macro' substitution, using Django-like syntax, - // {{{ code }}} will be replaced with |eval(code)|. - function processMacros(text) { - return text.replace(/{{{[^}]+}}}/g, function(str) { - str = str.substr(3, str.length-6); - return eval(str).toString(); - }); - } - - var substrate = new Substrate('JSifyer'); - if (mainPass) { // Handle unparsed types TODO: Batch them analyzer(intertyper(data.unparsedTypess[0].lines, true), true); @@ -71,12 +67,14 @@ function JSify(data, functionsOnly, givenFunctions) { // Add additional necessary items for the main pass. We can now do this since types are parsed (types can be used through // generateStructInfo in library.js) + //B.start('jsifier-libload'); LibraryManager.load(); + //B.stop('jsifier-libload'); - if (phase == 'pre') { + if (phase == 'pre' || phase == 'glue') { var libFuncsToInclude; if (INCLUDE_FULL_LIBRARY) { - assert(!BUILD_AS_SHARED_LIB, 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB set.') + assert(!(BUILD_AS_SHARED_LIB || SIDE_MODULE), 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB/SIDE_MODULE set.') libFuncsToInclude = []; for (var key in LibraryManager.library) { if (!key.match(/__(deps|postset|inline|asm|sig)$/)) { @@ -97,19 +95,6 @@ function JSify(data, functionsOnly, givenFunctions) { // Functions - Functions.currExternalFunctions = !mainPass ? givenFunctions.currExternalFunctions : {}; - - data.functionStubs.forEach(function(func) { - // Don't overwrite stubs that have more info. - if (!Functions.currExternalFunctions.hasOwnProperty(func.ident) || - !Functions.currExternalFunctions[func.ident].numParams === undefined) { - Functions.currExternalFunctions[func.ident] = { - hasVarArgs: func.hasVarArgs, - numParams: func.params && func.params.length - }; - } - }); - if (phase == 'funcs') { // || phase == 'pre') { // pre has function shells, just to defined implementedFunctions var MAX_BATCH_FUNC_LINES = 1000; while (data.unparsedFunctions.length > 0) { @@ -124,7 +109,7 @@ function JSify(data, functionsOnly, givenFunctions) { dprint('unparsedFunctions','====================\n// Processing function batch of ' + currBaseLineNums.length + ' functions, ' + currFuncLines.length + ' lines, functions left: ' + data.unparsedFunctions.length); if (DEBUG_MEMORY) MemoryDebugger.tick('pre-func'); - JSify(analyzer(intertyper(currFuncLines, true, currBaseLineNums), true), true, Functions); + JSify(analyzer(intertyper(currFuncLines, true, currBaseLineNums), true), true); if (DEBUG_MEMORY) MemoryDebugger.tick('post-func'); } currFuncLines = currBaseLineNums = null; // Do not hold on to anything from inside that loop (JS function scoping..) @@ -133,21 +118,6 @@ function JSify(data, functionsOnly, givenFunctions) { // Actors - // type - // FIXME: This is no longer used, we do not actually need to JSify on types. TODO: Remove this and related code - substrate.addActor('Type', { - processItem: function(item) { - var type = Types.types[item.name_]; - var niceName = toNiceIdent(item.name_); - // We might export all of Types.types, cleaner that way, but do not want slowdowns in accessing flatteners - item.JS = 'var ' + niceName + '___SIZE = ' + Types.types[item.name_].flatSize + '; // ' + item.name_ + '\n'; - if (type.needsFlattening && !type.flatFactor) { - item.JS += 'var ' + niceName + '___FLATTENER = ' + JSON.stringify(Types.types[item.name_].flatIndexes) + ';'; - } - return [item]; - } - }); - function makeEmptyStruct(type) { var ret = []; var typeData = Types.types[type]; @@ -172,7 +142,7 @@ function JSify(data, functionsOnly, givenFunctions) { } // Add current value(s) var currValue = values[i]; - if (USE_TYPED_ARRAYS == 2 && typeData.fields[i] == 'i64') { + if (USE_TYPED_ARRAYS == 2 && (typeData.fields[i] == 'i64' || (typeData.flatFactor && typeData.fields[0] == 'i64'))) { // 'flatten' out the 64-bit value into two 32-bit halves var parts = parseI64Constant(currValue, true); ret[index++] = parts[0]; @@ -245,46 +215,69 @@ function JSify(data, functionsOnly, givenFunctions) { function parseConst(value, type, ident) { var constant = makeConst(value, type); + // Sadly, we've thrown away type information in makeConst, so we're not + // passing correct type info to parseNumerical which works around this + // lack. constant = flatten(constant).map(function(x) { return parseNumerical(x) }) return constant; } // globalVariable - substrate.addActor('GlobalVariable', { - processItem: function(item) { - function needsPostSet(value) { - if (typeof value !== 'string') return false; - return value[0] in UNDERSCORE_OPENPARENS || value.substr(0, 14) === 'CHECK_OVERFLOW' - || value.substr(0, 6) === 'GLOBAL'; - } - - item.intertype = 'GlobalVariableStub'; - assert(!item.lines); // FIXME remove this, after we are sure it isn't needed - var ret = [item]; - if (item.ident == '_llvm_global_ctors') { - item.JS = '\n__ATINIT__ = __ATINIT__.concat([\n' + - item.ctors.map(function(ctor) { return ' { func: function() { ' + ctor + '() } }' }).join(',\n') + - '\n]);\n'; - return ret; - } - - var constant = null; - var allocator = (BUILD_AS_SHARED_LIB && !item.external) ? 'ALLOC_NORMAL' : 'ALLOC_STATIC'; - var index = null; - if (item.external && BUILD_AS_SHARED_LIB) { - // External variables in shared libraries should not be declared as - // they would shadow similarly-named globals in the parent. - item.JS = ''; - } else { - item.JS = makeGlobalDef(item.ident); - } + function globalVariableHandler(item) { - if (!NAMED_GLOBALS && isIndexableGlobal(item.ident)) { - index = makeGlobalUse(item.ident); // index !== null indicates we are indexing this - allocator = 'ALLOC_NONE'; - } + function needsPostSet(value) { + if (typeof value !== 'string') return false; + // (' is ok, as it is something we can indexize later into a concrete int: ('{{ FI_ ... + return /^([(_][^']|CHECK_OVERFLOW|GLOBAL).*/.test(value); + } + + item.intertype = 'GlobalVariableStub'; + itemsDict.GlobalVariableStub.push(item); + assert(!item.lines); // FIXME remove this, after we are sure it isn't needed + if (item.ident == '_llvm_global_ctors') { + item.JS = '\n/* global initializers */ __ATINIT__.push(' + + item.ctors.map(function(ctor) { return '{ func: function() { ' + ctor + '() } }' }).join(',') + + ');\n'; + return; + } + + var constant = null; + var allocator = (BUILD_AS_SHARED_LIB && !item.external) ? 'ALLOC_NORMAL' : 'ALLOC_STATIC'; + var index = null; + if (item.external && BUILD_AS_SHARED_LIB) { + // External variables in shared libraries should not be declared as + // they would shadow similarly-named globals in the parent. + item.JS = ''; + } else { + item.JS = makeGlobalDef(item.ident); + } + + if (!NAMED_GLOBALS && isIndexableGlobal(item.ident)) { + index = makeGlobalUse(item.ident); // index !== null indicates we are indexing this + allocator = 'ALLOC_NONE'; + } + + Variables.globals[item.ident].named = item.named; + + if (ASM_JS && (MAIN_MODULE || SIDE_MODULE) && !item.private_ && !NAMED_GLOBALS && isIndexableGlobal(item.ident)) { + // We need this to be named (and it normally would not be), so that it can be linked to and used from other modules + Variables.globals[item.ident].linkable = 1; + } + + if (isBSS(item)) { + // If using indexed globals, go ahead and early out (no need to explicitly + // initialize). + if (!NAMED_GLOBALS) return; + + // If using named globals, we can at least shorten the call to allocate by + // passing an integer representing the size of memory to alloc instead of + // an array of 0s of size length. + constant = Runtime.alignMemory(calcAllocatedSize(item.type)); + } else { if (item.external) { - if (Runtime.isNumberType(item.type) || isPointerType(item.type)) { + if (LibraryManager.library[item.ident.slice(1)]) { + constant = LibraryManager.library[item.ident.slice(1)]; + } else if (Runtime.isNumberType(item.type) || isPointerType(item.type)) { constant = zeros(Runtime.getNativeFieldSize(item.type)); } else { constant = makeEmptyStruct(item.type); @@ -292,87 +285,83 @@ function JSify(data, functionsOnly, givenFunctions) { } else { constant = parseConst(item.value, item.type, item.ident); } - assert(typeof constant === 'object');//, [typeof constant, JSON.stringify(constant), item.external]); // This is a flattened object. We need to find its idents, so they can be assigned to later - constant.forEach(function(value, i) { - if (needsPostSet(value)) { // ident, or expression containing an ident - ret.push({ - intertype: 'GlobalVariablePostSet', - JS: makeSetValue(makeGlobalUse(item.ident), i, value, 'i32', false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors - }); - constant[i] = '0'; - } - }); + if (typeof constant === 'object') { + var structTypes = null; + constant.forEach(function(value, i) { + if (needsPostSet(value)) { // ident, or expression containing an ident + if (!structTypes) structTypes = generateStructTypes(item.type); + itemsDict.GlobalVariablePostSet.push({ + intertype: 'GlobalVariablePostSet', + JS: makeSetValue(makeGlobalUse(item.ident), i, value, structTypes[i], false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors + }); + constant[i] = '0'; + } else { + if (typeof value === 'string') constant[i] = deParenCarefully(value); + } + }); + } if (item.external) { // External variables in shared libraries should not be declared as // they would shadow similarly-named globals in the parent, so do nothing here. - if (BUILD_AS_SHARED_LIB) return ret; + if (BUILD_AS_SHARED_LIB) return; + if (SIDE_MODULE) { + itemsDict.GlobalVariableStub.pop(); // remove this item + return; + } // Library items need us to emit something, but everything else requires nothing. - if (!LibraryManager.library[item.ident.slice(1)]) return ret; + if (!LibraryManager.library[item.ident.slice(1)]) return; } // ensure alignment - constant = constant.concat(zeros(Runtime.alignMemory(constant.length) - constant.length)); - - // Special case: class vtables. We make sure they are null-terminated, to allow easy runtime operations - if (item.ident.substr(0, 5) == '__ZTV') { - constant = constant.concat(zeros(Runtime.alignMemory(QUANTUM_SIZE))); + if (typeof constant === 'object') { + var extra = Runtime.alignMemory(constant.length) - constant.length; + if (item.ident.substr(0, 5) == '__ZTV') extra += Runtime.alignMemory(QUANTUM_SIZE); + while (extra-- > 0) constant.push(0); } + } - // NOTE: This is the only place that could potentially create static - // allocations in a shared library. + // NOTE: This is the only place that could potentially create static + // allocations in a shared library. + if (typeof constant !== 'string') { constant = makePointer(constant, null, allocator, item.type, index); + } - var js = (index !== null ? '' : item.ident + '=') + constant; - if (js) js += ';'; + var js = (index !== null ? '' : item.ident + '=') + constant; + if (js) js += ';'; - if (!ASM_JS && (EXPORT_ALL || (item.ident in EXPORTED_GLOBALS))) { - js += '\nModule["' + item.ident + '"] = ' + item.ident + ';'; - } - if (BUILD_AS_SHARED_LIB == 2 && !item.private_) { - // TODO: make the assert conditional on ASSERTIONS - js += 'if (globalScope) { assert(!globalScope["' + item.ident + '"]); globalScope["' + item.ident + '"] = ' + item.ident + ' }'; - } - if (item.external && !NAMED_GLOBALS) { - js = 'var ' + item.ident + ' = ' + js; // force an explicit naming, even if unnamed globals, for asm forwarding - } - return ret.concat({ - intertype: 'GlobalVariable', - JS: js, - }); + if (!ASM_JS && NAMED_GLOBALS && (EXPORT_ALL || (item.ident in EXPORTED_GLOBALS))) { + js += '\nModule["' + item.ident + '"] = ' + item.ident + ';'; + } + if (BUILD_AS_SHARED_LIB == 2 && !item.private_) { + // TODO: make the assert conditional on ASSERTIONS + js += 'if (globalScope) { assert(!globalScope["' + item.ident + '"]); globalScope["' + item.ident + '"] = ' + item.ident + ' }'; } - }); + if (item.external && !NAMED_GLOBALS) { + js = 'var ' + item.ident + '=' + js; // force an explicit naming, even if unnamed globals, for asm forwarding + } + itemsDict.GlobalVariableStub.push({ + intertype: 'GlobalVariable', + JS: js, + }); + } // alias - substrate.addActor('Alias', { - processItem: function(item) { - 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. We also order by dependencies there - Variables.globals[item.ident].targetIdent = item.value.ident; - var value = Variables.globals[item.ident].resolvedAlias = finalizeLLVMParameter(item.value); - var fix = ''; - if (BUILD_AS_SHARED_LIB == 2 && !item.private_) { - var target = item.ident; - if (isFunctionType(item.type)) { - target = item.value.ident; // the other side does not know this is an alias/function table index. So make it the alias target. - var varData = Variables.globals[target]; - assert(!varData, 'multi-level aliasing does not work yet in shared lib 2 exports'); - } - fix = '\nif (globalScope) { assert(!globalScope["' + item.ident + '"]); globalScope["' + item.ident + '"] = ' + target + ' }' - } - ret.push({ - intertype: 'GlobalVariablePostSet', - ident: item.ident, - dependencies: set([value]), - JS: item.ident + ' = ' + value + ';' + fix - }); - return ret; + function aliasHandler(item) { + item.intertype = 'GlobalVariableStub'; + itemsDict.GlobalVariableStub.push(item); + item.JS = 'var ' + item.ident + ';'; + // Set the actual value in a postset, since it may be a global variable. We also order by dependencies there + Variables.globals[item.ident].targetIdent = item.value.ident; + var value = Variables.globals[item.ident].resolvedAlias = finalizeLLVMParameter(item.value); + if ((MAIN_MODULE || SIDE_MODULE) && isFunctionType(item.type)) { + var target = item.value.ident; + if (!Functions.aliases[target]) Functions.aliases[target] = []; + Functions.aliases[target].push(item.ident); } - }); + } function processLibraryFunction(snippet, ident) { snippet = snippet.toString(); @@ -389,140 +378,178 @@ function JSify(data, functionsOnly, givenFunctions) { } // functionStub - substrate.addActor('FunctionStub', { - processItem: function(item) { - function addFromLibrary(ident) { - if (ident in addedLibraryItems) return ''; - addedLibraryItems[ident] = true; - - // dependencies can be JS functions, which we just run - if (typeof ident == 'function') return ident(); - - // 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 Functions.implementedFunctions) return ''; - - var snippet = LibraryManager.library[ident]; - var redirectedIdent = null; - var deps = LibraryManager.library[ident + '__deps'] || []; - var isFunction = false; - - if (typeof snippet === 'string') { - var target = LibraryManager.library[snippet]; - if (target) { - // Redirection for aliases. We include the parent, and at runtime make ourselves equal to it. - // This avoid having duplicate functions with identical content. - redirectedIdent = snippet; - deps.push(snippet); - snippet = '_' + snippet; - } - // In asm, we need to know about library functions. If there is a target, though, then no - // need to consider this a library function - we will call directly to it anyhow - if (ASM_JS && !redirectedIdent && (typeof target == 'function' || /Math\.\w+/.exec(snippet))) { - Functions.libraryFunctions[ident] = 1; - } - } else if (typeof snippet === 'object') { - snippet = stringifyWithFunctions(snippet); - } else if (typeof snippet === 'function') { - isFunction = true; - snippet = processLibraryFunction(snippet, ident); - if (ASM_JS) Functions.libraryFunctions[ident] = 1; - } + function functionStubHandler(item) { + // note the signature + if (item.returnType && item.params) { + functionStubSigs[item.ident] = Functions.getSignature(item.returnType.text, item.params.map(function(arg) { return arg.type }), false); + } - var postsetId = ident + '__postset'; - var postset = LibraryManager.library[postsetId]; - if (postset && !addedLibraryItems[postsetId]) { - addedLibraryItems[postsetId] = true; - ret.push({ - intertype: 'GlobalVariablePostSet', - JS: postset - }); - } + function addFromLibrary(ident) { + if (ident in addedLibraryItems) return ''; + addedLibraryItems[ident] = true; - if (redirectedIdent) { - deps = deps.concat(LibraryManager.library[redirectedIdent + '__deps'] || []); - } - if (ASM_JS) { - // In asm, dependencies implemented in C might be needed by JS library functions. - // We don't know yet if they are implemented in C or not. To be safe, export such - // special cases. - [LIBRARY_DEPS_TO_AUTOEXPORT].forEach(function(special) { - deps.forEach(function(dep) { - if (dep == special && !EXPORTED_FUNCTIONS[dep]) { - EXPORTED_FUNCTIONS[dep] = 1; - } - }); - }); - } - // $ident's are special, we do not prefix them with a '_'. - if (ident[0] === '$') { - ident = ident.substr(1); - } else { - ident = '_' + ident; - } - var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : ''); - var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';'); - if (ASM_JS) { - var sig = LibraryManager.library[ident.substr(1) + '__sig']; - if (isFunction && sig && LibraryManager.library[ident.substr(1) + '__asm']) { - // asm library function, add it as generated code alongside the generated code - Functions.implementedFunctions[ident] = sig; - asmLibraryFunctions.push(contentText); - contentText = ' '; - EXPORTED_FUNCTIONS[ident] = 1; - Functions.libraryFunctions[ident.substr(1)] = 2; - } + // dependencies can be JS functions, which we just run + if (typeof ident == 'function') return ident(); + + // 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 Functions.implementedFunctions) return ''; + + if (!LibraryManager.library.hasOwnProperty(ident) && !LibraryManager.library.hasOwnProperty(ident + '__inline')) { + if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + ident); + else if (VERBOSE || (WARN_ON_UNDEFINED_SYMBOLS && !LINKABLE)) warn('unresolved symbol: ' + ident); + // emit a stub that will fail at runtime + LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);"); + } + + var snippet = LibraryManager.library[ident]; + var redirectedIdent = null; + var deps = LibraryManager.library[ident + '__deps'] || []; + deps.forEach(function(dep) { + if (typeof snippet === 'string' && !(dep in LibraryManager.library)) warn('missing library dependency ' + dep + ', make sure you are compiling with the right options (see #ifdefs in src/library*.js)'); + }); + var isFunction = false; + + if (typeof snippet === 'string') { + var target = LibraryManager.library[snippet]; + if (target) { + // Redirection for aliases. We include the parent, and at runtime make ourselves equal to it. + // This avoid having duplicate functions with identical content. + redirectedIdent = snippet; + deps.push(snippet); + snippet = '_' + snippet; } - if ((!ASM_JS || phase == 'pre') && - (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS))) { - contentText += '\nModule["' + ident + '"] = ' + ident + ';'; + // In asm, we need to know about library functions. If there is a target, though, then no + // need to consider this a library function - we will call directly to it anyhow + if (ASM_JS && !redirectedIdent && (typeof target == 'function' || /Math_\w+/.exec(snippet))) { + Functions.libraryFunctions[ident] = 1; } - return depsText + contentText; + } else if (typeof snippet === 'object') { + snippet = stringifyWithFunctions(snippet); + } else if (typeof snippet === 'function') { + isFunction = true; + snippet = processLibraryFunction(snippet, ident); + if (ASM_JS) Functions.libraryFunctions[ident] = 1; + } + + var postsetId = ident + '__postset'; + var postset = LibraryManager.library[postsetId]; + if (postset && !addedLibraryItems[postsetId] && !SIDE_MODULE) { + addedLibraryItems[postsetId] = true; + itemsDict.GlobalVariablePostSet.push({ + intertype: 'GlobalVariablePostSet', + JS: postset + }); } - var ret = [item]; - if (IGNORED_FUNCTIONS.indexOf(item.ident) >= 0) return null; - var shortident = item.ident.substr(1); - if (BUILD_AS_SHARED_LIB) { - // Shared libraries reuse the runtime of their parents. - item.JS = ''; + if (redirectedIdent) { + deps = deps.concat(LibraryManager.library[redirectedIdent + '__deps'] || []); + } + if (ASM_JS) { + // In asm, dependencies implemented in C might be needed by JS library functions. + // We don't know yet if they are implemented in C or not. To be safe, export such + // special cases. + [LIBRARY_DEPS_TO_AUTOEXPORT].forEach(function(special) { + deps.forEach(function(dep) { + if (dep == special && !EXPORTED_FUNCTIONS[dep]) { + EXPORTED_FUNCTIONS[dep] = 1; + } + }); + }); + } + // $ident's are special, we do not prefix them with a '_'. + if (ident[0] === '$') { + ident = ident.substr(1); } else { - // If this is not linkable, anything not in the library is definitely missing - var cancel = false; - if (!LINKABLE && !LibraryManager.library.hasOwnProperty(shortident) && !LibraryManager.library.hasOwnProperty(shortident + '__inline')) { - if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + shortident); - if (VERBOSE || WARN_ON_UNDEFINED_SYMBOLS) printErr('warning: unresolved symbol: ' + shortident); - if (ASM_JS || item.ident in DEAD_FUNCTIONS) { - // emit a stub that will fail during runtime. this allows asm validation to succeed. - LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);"); - } else { - cancel = true; // emit nothing, not even var X = undefined; - } + ident = '_' + ident; + } + if (VERBOSE) printErr('adding ' + ident + ' and deps ' + deps + ' : ' + (snippet + '').substr(0, 40)); + var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : ''); + var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';'); + if (ASM_JS) { + var sig = LibraryManager.library[ident.substr(1) + '__sig']; + if (isFunction && sig && LibraryManager.library[ident.substr(1) + '__asm']) { + // asm library function, add it as generated code alongside the generated code + Functions.implementedFunctions[ident] = sig; + asmLibraryFunctions.push(contentText); + contentText = ' '; + EXPORTED_FUNCTIONS[ident] = 1; + Functions.libraryFunctions[ident.substr(1)] = 2; } - item.JS = cancel ? ';' : addFromLibrary(shortident); } - return ret; + if (SIDE_MODULE) return ';'; // we import into the side module js library stuff from the outside parent + if ((!ASM_JS || phase == 'pre' || phase == 'glue') && + (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS))) { + contentText += '\nModule["' + ident + '"] = ' + ident + ';'; + } + return depsText + contentText; } - }); - // function splitter - substrate.addActor('FunctionSplitter', { - processItem: function(item) { - var ret = [item]; - item.splitItems = 0; - item.labels.forEach(function(label) { - label.lines.forEach(function(line) { - line.func = item.ident; - line.funcData = item; // TODO: remove all these, access it globally - line.parentLabel = label.ident; - ret.push(line); - item.splitItems ++; - }); - }); - - this.forwardItems(ret, 'FuncLineTriager'); + itemsDict.functionStub.push(item); + if (IGNORED_FUNCTIONS.indexOf(item.ident) >= 0) return; + var shortident = item.ident.substr(1); + if (BUILD_AS_SHARED_LIB) { + // Shared libraries reuse the runtime of their parents. + item.JS = ''; + } else { + // If this is not linkable, anything not in the library is definitely missing + if (item.ident in DEAD_FUNCTIONS) { + if (LibraryManager.library[shortident + '__asm']) { + warn('cannot kill asm library function ' + item.ident); + } else { + LibraryManager.library[shortident] = new Function("Module['printErr']('dead function: " + shortident + "'); abort(-1);"); + delete LibraryManager.library[shortident + '__inline']; + delete LibraryManager.library[shortident + '__deps']; + } + } + item.JS = addFromLibrary(shortident); } - }); + } + + // function splitter + function functionSplitter(item) { + item.lines.forEach(function(line) { + //B.start('jsifier-handle-' + line.intertype); + Framework.currItem = line; + line.funcData = item; // TODO: remove all these, access it globally + switch (line.intertype) { + case 'value': line.JS = valueHandler(line); break; + case 'noop': line.JS = noopHandler(line); break; + case 'var': line.JS = varHandler(line); break; + case 'store': line.JS = storeHandler(line); break; + case 'deleted': line.JS = deletedHandler(line); break; + case 'branch': line.JS = branchHandler(line); break; + case 'switch': line.JS = switchHandler(line); break; + case 'return': line.JS = returnHandler(line); break; + case 'resume': line.JS = resumeHandler(line); break; + case 'invoke': line.JS = invokeHandler(line); break; + case 'atomic': line.JS = atomicHandler(line); break; + case 'landingpad': line.JS = landingpadHandler(line); break; + case 'load': line.JS = loadHandler(line); break; + case 'extractvalue': line.JS = extractvalueHandler(line); break; + case 'insertvalue': line.JS = insertvalueHandler(line); break; + case 'insertelement': line.JS = insertelementHandler(line); break; + case 'extracttelement': line.JS = extractelementHandler(line); break; + case 'shufflevector': line.JS = shufflevectorHandler(line); break; + case 'indirectbr': line.JS = indirectbrHandler(line); break; + case 'alloca': line.JS = allocaHandler(line); break; + case 'va_arg': line.JS = va_argHandler(line); break; + case 'mathop': line.JS = mathopHandler(line); break; + case 'bitcast': line.JS = bitcastHandler(line); break; + case 'getelementptr': line.JS = getelementptrHandler(line); break; + case 'call': line.JS = callHandler(line); break; + case 'unreachable': line.JS = unreachableHandler(line); break; + default: throw 'what is this line? ' + dump(line); + } + //if (ASM_JS) assert(line.JS.indexOf('var ') < 0, dump(line)); + if (line.assignTo) makeAssign(line); + Framework.currItem = null; + //B.stop('jsifier-handle-' + line.intertype); + }); + //B.start('jsifier-frec'); + functionReconstructor(item); + //B.stop('jsifier-frec'); + } // function for filtering functions for label debugging if (LABEL_FUNCTION_FILTERS.length > 0) { @@ -538,295 +565,306 @@ function JSify(data, functionsOnly, givenFunctions) { } // function reconstructor & post-JS optimizer - substrate.addActor('FunctionReconstructor', { - funcs: {}, - seen: {}, - processItem: function(item) { - if (this.seen[item.__uid__]) return null; - if (item.intertype == 'function') { - this.funcs[item.ident] = item; - item.relines = {}; - this.seen[item.__uid__] = true; - return null; - } - var line = item; - var func = this.funcs[line.func]; - if (!func) return null; - - // Re-insert our line - this.seen[item.__uid__] = true; - var label = func.labels.filter(function(label) { return label.ident == line.parentLabel })[0]; - label.lines = label.lines.map(function(line2) { - return (line2.lineNum !== line.lineNum) ? line2 : line; - }); - func.splitItems --; - // OLD delete line.funcData; // clean up - if (func.splitItems > 0) return null; + function functionReconstructor(func) { + // We have this function all reconstructed, go and finalize it's JS! - // We have this function all reconstructed, go and finalize it's JS! + if (IGNORED_FUNCTIONS.indexOf(func.ident) >= 0) return null; - if (IGNORED_FUNCTIONS.indexOf(func.ident) >= 0) return null; + func.JS = '\n'; - func.JS = '\n'; + var paramIdents = func.params.map(function(param) { + return toNiceIdent(param.ident); + }); - var paramIdents = func.params.map(function(param) { - return toNiceIdent(param.ident); + if (CLOSURE_ANNOTATIONS) { + func.JS += '/**\n'; + paramIdents.forEach(function(param) { + func.JS += ' * @param {number} ' + param + '\n'; }); + func.JS += ' * @return {number}\n' + func.JS += ' */\n'; + } - if (CLOSURE_ANNOTATIONS) { - func.JS += '/**\n'; - paramIdents.forEach(function(param) { - func.JS += ' * @param {number} ' + param + '\n'; - }); - func.JS += ' * @return {number}\n' - func.JS += ' */\n'; - } + if (PRINT_SPLIT_FILE_MARKER) { + func.JS += '\n//FUNCTION_BEGIN_MARKER\n' + var associatedSourceFile = "NO_SOURCE"; + } + + if (DLOPEN_SUPPORT) Functions.getIndex(func.ident); - if (PRINT_SPLIT_FILE_MARKER) { - func.JS += '\n//FUNCTION_BEGIN_MARKER\n' - var associatedSourceFile = "NO_SOURCE"; - } - - func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n'; + func.JS += 'function ' + func.ident + '(' + paramIdents.join(',') + '){\n'; - if (PGO) { - func.JS += ' PGOMonitor.called["' + func.ident + '"] = 1;\n'; - } + if (PGO) { + func.JS += INDENTATION + 'PGOMonitor.called["' + func.ident + '"] = 1;\n'; + } - if (ASM_JS) { - // spell out argument types - func.params.forEach(function(param) { - func.JS += ' ' + param.ident + ' = ' + asmCoercion(param.ident, param.type) + ';\n'; - }); + if (ASM_JS) { + // spell out argument types + func.params.forEach(function(param) { + func.JS += INDENTATION + param.ident + '=' + deParen(asmCoercion(param.ident, param.type)) + ';\n'; + }); - // spell out local variables - var vars = values(func.variables).filter(function(v) { return v.origin != 'funcparam' }); - if (vars.length > 0) { - var chunkSize = 8; - var chunks = []; - var i = 0; - while (i < vars.length) { - chunks.push(vars.slice(i, i+chunkSize)); - i += chunkSize; - } - for (i = 0; i < chunks.length; i++) { - func.JS += ' var ' + chunks[i].map(function(v) { - var type = getImplementationType(v); - if (!isIllegalType(type) || v.ident.indexOf('$', 1) > 0) { // not illegal, or a broken up illegal - return v.ident + ' = ' + asmInitializer(type); //, func.variables[v.ident].impl); - } else { - return range(Math.ceil(getBits(type)/32)).map(function(i) { - return v.ident + '$' + i + '= 0'; - }).join(','); - } - }).join(', ') + ';\n'; - } - } - } + addVariable('label', 'i32', func); - if (true) { // TODO: optimize away when not needed - if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */'; - func.JS += ' var label = 0;\n'; + if (func.setjmpTable) { + addVariable('setjmpLabel', 'i32', func); + addVariable('setjmpTable', 'i32', func); } - if (ASM_JS) { - var hasByVal = false; - func.params.forEach(function(param) { - hasByVal = hasByVal || param.byVal; - }); - if (hasByVal) { - func.JS += ' var tempParam = 0;\n'; + // spell out local variables + var vars = values(func.variables).filter(function(v) { + return v.origin !== 'funcparam' && + (!isIllegalType(getImplementationType(v)) || v.ident.indexOf('$', 1) > 0); // not illegal, or a broken up illegal (we have illegal chunks explicitly anyhow) + }); + if (vars.length > 0) { + var chunkSize = 20; + var chunks = []; + var i = 0; + while (i < vars.length) { + chunks.push(vars.slice(i, i+chunkSize)); + i += chunkSize; + } + for (i = 0; i < chunks.length; i++) { + func.JS += INDENTATION + 'var ' + chunks[i].map(function(v) { + return v.ident + '=' + asmInitializer(getImplementationType(v)); //, func.variables[v.ident].impl); + }).join(',') + ';\n'; } } + } - // Prepare the stack, if we need one. If we have other stack allocations, force the stack to be set up. - func.JS += ' ' + RuntimeGenerator.stackEnter(func.initialStack, func.otherStackAllocations) + ';\n'; + if (!ASM_JS) { + if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */'; + func.JS += INDENTATION + 'var label=0;\n'; + } - // Make copies of by-value params - // XXX It is not clear we actually need this. While without this we fail, it does look like - // Clang normally does the copy itself, in the calling function. We only need this code - // when Clang optimizes the code and passes the original, not the copy, to the other - // function. But Clang still copies, the copy is just unused! Need to figure out if that - // is caused by our running just some optimizations (the safe ones), or if its a bug - // in Clang, or a bug in our understanding of the IR. + if (ASM_JS) { + var hasByVal = false; func.params.forEach(function(param) { - if (param.byVal) { - var type = removePointing(param.type); - var typeInfo = Types.types[type]; - func.JS += ' ' + (ASM_JS ? '' : 'var ') + 'tempParam = ' + param.ident + '; ' + param.ident + ' = ' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' + - makeCopyValues(param.ident, 'tempParam', typeInfo.flatSize, 'null', null, param.byVal) + ';\n'; - } + hasByVal = hasByVal || param.byVal; }); + if (hasByVal) { + func.JS += INDENTATION + 'var tempParam=0;\n'; |