aboutsummaryrefslogtreecommitdiff
path: root/src/jsifier.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/jsifier.js')
-rw-r--r--src/jsifier.js1659
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';