aboutsummaryrefslogtreecommitdiff
path: root/src/jsifier.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/jsifier.js')
-rw-r--r--src/jsifier.js1222
1 files changed, 578 insertions, 644 deletions
diff --git a/src/jsifier.js b/src/jsifier.js
index 1f53b1a2..96cb8d9a 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -22,6 +22,8 @@ var functionStubSigs = {};
function JSify(data, functionsOnly, givenFunctions) {
var mainPass = !functionsOnly;
+ var itemsDict = { type: [], GlobalVariableStub: [], functionStub: [], function: [], GlobalVariable: [], GlobalVariablePostSet: [] };
+
if (mainPass) {
var shellFile = SHELL_FILE ? SHELL_FILE : (BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'shell_sharedlib.js' : 'shell.js');
@@ -58,17 +60,6 @@ function JSify(data, functionsOnly, givenFunctions) {
}
}
- // 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);
@@ -138,21 +129,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];
@@ -255,146 +231,144 @@ function JSify(data, functionsOnly, givenFunctions) {
}
// 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/* global initializers */ __ATINIT__.push(' +
- item.ctors.map(function(ctor) { return '{ func: function() { ' + ctor + '() } }' }).join(',') +
- ');\n';
- return ret;
- }
+ function globalVariableHandler(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';
+ 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);
- }
+ 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';
- }
+ 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;
+ 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 (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)) {
- var length = calcAllocatedSize(item.type);
- length = Runtime.alignMemory(length);
+ if (isBSS(item)) {
+ var length = calcAllocatedSize(item.type);
+ length = Runtime.alignMemory(length);
- // If using indexed globals, go ahead and early out (no need to explicitly
- // initialize).
- if (!NAMED_GLOBALS) {
- return ret;
- }
- // 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.
- else {
- constant = length;
+ // 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.
+ else {
+ constant = length;
+ }
+ } else {
+ if (item.external) {
+ if (Runtime.isNumberType(item.type) || isPointerType(item.type)) {
+ constant = zeros(Runtime.getNativeFieldSize(item.type));
+ } else {
+ constant = makeEmptyStruct(item.type);
}
} else {
- if (item.external) {
- if (Runtime.isNumberType(item.type) || isPointerType(item.type)) {
- constant = zeros(Runtime.getNativeFieldSize(item.type));
- } else {
- constant = makeEmptyStruct(item.type);
- }
- } else {
- constant = parseConst(item.value, item.type, item.ident);
+ 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
+ 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';
}
- 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
- var structTypes = null;
- constant.forEach(function(value, i) {
- if (needsPostSet(value)) { // ident, or expression containing an ident
- if (!structTypes) structTypes = generateStructTypes(item.type);
- ret.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';
- }
- });
+ });
- 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 (SIDE_MODULE) return [];
- // Library items need us to emit something, but everything else requires nothing.
- if (!LibraryManager.library[item.ident.slice(1)]) return ret;
+ 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;
+ 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;
+ }
- // ensure alignment
- constant = constant.concat(zeros(Runtime.alignMemory(constant.length) - constant.length));
+ // 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)));
- }
+ // 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)));
}
+ }
- // NOTE: This is the only place that could potentially create static
- // allocations in a shared library.
- constant = makePointer(constant, null, allocator, item.type, index);
+ // NOTE: This is the only place that could potentially create static
+ // allocations in a shared library.
+ 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 && 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
- }
- 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);
- 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);
- }
- 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();
@@ -411,147 +385,163 @@ function JSify(data, functionsOnly, givenFunctions) {
}
// functionStub
- substrate.addActor('FunctionStub', {
- processItem: function(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);
- }
-
- 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);
+ }
+
+ 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;
}
-
- var postsetId = ident + '__postset';
- var postset = LibraryManager.library[postsetId];
- if (postset && !addedLibraryItems[postsetId] && !SIDE_MODULE) {
- addedLibraryItems[postsetId] = true;
- ret.push({
- intertype: 'GlobalVariablePostSet',
- JS: postset
- });
+ // 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;
+ }
+
+ 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
+ });
+ }
- 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;
- }
- });
+ 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;
+ }
+ if (VERBOSE) printErr('adding ' + ident + ' and deps ' + deps);
+ 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;
}
- // $ident's are special, we do not prefix them with a '_'.
- if (ident[0] === '$') {
- ident = ident.substr(1);
- } else {
- ident = '_' + ident;
- }
- if (VERBOSE) printErr('adding ' + ident + ' and deps ' + deps);
- 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;
- }
- }
- if (SIDE_MODULE) return ';'; // we import into the side module js library stuff from the outside parent
- if ((!ASM_JS || phase == 'pre') &&
- (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS))) {
- contentText += '\nModule["' + ident + '"] = ' + ident + ';';
- }
- return depsText + contentText;
}
+ if (SIDE_MODULE) return ';'; // we import into the side module js library stuff from the outside parent
+ if ((!ASM_JS || phase == 'pre') &&
+ (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS))) {
+ contentText += '\nModule["' + ident + '"] = ' + ident + ';';
+ }
+ return depsText + contentText;
+ }
- 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 = '';
- } 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;
- }
+ 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
+ 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;
}
- item.JS = cancel ? ';' : addFromLibrary(shortident);
}
- return ret;
+ item.JS = cancel ? ';' : addFromLibrary(shortident);
}
- });
+ }
// 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');
- }
- });
+ function functionSplitter(item) {
+ item.lines.forEach(function(line) {
+ 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 '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);
+ }
+ assert(line.JS);
+ if (line.assignTo) makeAssign(line);
+ Framework.currItem = null;
+ });
+ functionReconstructor(item);
+ }
// function for filtering functions for label debugging
if (LABEL_FUNCTION_FILTERS.length > 0) {
@@ -567,322 +557,297 @@ 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";
+ }
+
+ if (DLOPEN_SUPPORT) Functions.getIndex(func.ident);
- func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n';
+ func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n';
- if (PGO) {
- func.JS += INDENTATION + '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 += INDENTATION + 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 + ' = ' + 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 += INDENTATION + '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';
- }
+ // 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 += INDENTATION + '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';
}
}
+ }
- if (true) { // TODO: optimize away when not needed
- if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */';
- func.JS += INDENTATION + 'var label = 0;\n';
- }
+ if (true) { // TODO: optimize away when not needed
+ if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */';
+ func.JS += INDENTATION + 'var label = 0;\n';
+ }
- if (ASM_JS) {
- var hasByVal = false;
- func.params.forEach(function(param) {
- hasByVal = hasByVal || param.byVal;
- });
- if (hasByVal) {
- func.JS += INDENTATION + 'var tempParam = 0;\n';
- }
+ if (ASM_JS) {
+ var hasByVal = false;
+ func.params.forEach(function(param) {
+ hasByVal = hasByVal || param.byVal;
+ });
+ if (hasByVal) {
+ func.JS += INDENTATION + 'var tempParam = 0;\n';
}
+ }
+
+ if (func.hasVarArgsCall) {
+ func.JS += INDENTATION + 'var tempVarArgs = 0;\n';
+ }
+
+ // Prepare the stack, if we need one. If we have other stack allocations, force the stack to be set up.
+ func.JS += INDENTATION + RuntimeGenerator.stackEnter(func.initialStack, func.otherStackAllocations) + ';\n';
- if (func.hasVarArgsCall) {
- func.JS += INDENTATION + 'var tempVarArgs = 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.
+ func.params.forEach(function(param) {
+ if (param.byVal) {
+ var type = removePointing(param.type);
+ var typeInfo = Types.types[type];
+ func.JS += INDENTATION + (ASM_JS ? '' : 'var ') + 'tempParam = ' + param.ident + '; ' + param.ident + ' = ' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' +
+ makeCopyValues(param.ident, 'tempParam', typeInfo.flatSize, 'null', null, param.byVal) + ';\n';
}
+ });
- // Prepare the stack, if we need one. If we have other stack allocations, force the stack to be set up.
- func.JS += INDENTATION + RuntimeGenerator.stackEnter(func.initialStack, func.otherStackAllocations) + ';\n';
+ if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " Module.print(INDENT + ' Entering: " + func.ident + ": ' + Array.prototype.slice.call(arguments)); INDENT += ' ';\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.
- func.params.forEach(function(param) {
- if (param.byVal) {
- var type = removePointing(param.type);
- var typeInfo = Types.types[type];
- func.JS += INDENTATION + (ASM_JS ? '' : 'var ') + 'tempParam = ' + param.ident + '; ' + param.ident + ' = ' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' +
- makeCopyValues(param.ident, 'tempParam', typeInfo.flatSize, 'null', null, param.byVal) + ';\n';
+ // Walk function blocks and generate JS
+ function walkBlock(block, indent) {
+ if (!block) return '';
+ dprint('relooping', 'walking block: ' + block.type + ',' + block.entries + ' : ' + block.labels.length);
+ function getLabelLines(label, indent, relooping) {
+ if (!label) return '';
+ var ret = '';
+ if ((LABEL_DEBUG >= 2) && functionNameFilterTest(func.ident)) {
+ ret += indent + "Module.print(INDENT + '" + func.ident + ":" + label.ident + "');\n";
}
- });
-
- if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " Module.print(INDENT + ' Entering: " + func.ident + ": ' + Array.prototype.slice.call(arguments)); INDENT += ' ';\n";
-
- // Walk function blocks and generate JS
- function walkBlock(block, indent) {
- if (!block) return '';
- dprint('relooping', 'walking block: ' + block.type + ',' + block.entries + ' : ' + block.labels.length);
- function getLabelLines(label, indent, relooping) {
- if (!label) return '';
- var ret = '';
- if ((LABEL_DEBUG >= 2) && functionNameFilterTest(func.ident)) {
- ret += indent + "Module.print(INDENT + '" + func.ident + ":" + label.ident + "');\n";
- }
- if (EXECUTION_TIMEOUT > 0) {
- ret += indent + 'if (Date.now() - START_TIME >= ' + (EXECUTION_TIMEOUT*1000) + ') throw "Timed out!" + (new Error().stack);\n';
- }
-
- if (PRINT_SPLIT_FILE_MARKER && Debugging.on && Debugging.getAssociatedSourceFile(line.lineNum)) {
- // Overwrite the associated source file for every line. The last line should contain the source file associated to
- // the return value/address of outer most block (the marked function).
- associatedSourceFile = Debugging.getAssociatedSourceFile(line.lineNum);
- }
-
- // for special labels we care about (for phi), mark that we visited them
- var i = 0;
- return ret + label.lines.map(function(line) {
- var JS = line.JS;
- if (relooping && i == label.lines.length-1) {
- if (line.intertype == 'branch' || line.intertype == 'switch') {
- JS = ''; // just branching operations - done in the relooper, so nothing need be done here
- } else if (line.intertype == 'invoke') {
- JS = line.reloopingJS; // invokes have code that is not rendered in the relooper (the call inside a try-catch)
- }
- }
- i++;
- // invoke instructions span two lines, and the debug info is located
- // on the second line, hence the +1
- return JS + (Debugging.on ? Debugging.getComment(line.lineNum + (line.intertype === 'invoke' ? 1 : 0)) : '');
- })
- .join('\n')
- .split('\n') // some lines include line breaks
- .map(function(line) { return indent + line })
- .join('\n');
+ if (EXECUTION_TIMEOUT > 0) {
+ ret += indent + 'if (Date.now() - START_TIME >= ' + (EXECUTION_TIMEOUT*1000) + ') throw "Timed out!" + (new Error().stack);\n';
}
- var ret = '';
- if (!RELOOP || func.forceEmulated) { // TODO: also if just 1 label?
- if (block.labels.length > 1) {
- if (block.entries.length == 1) {
- ret += indent + 'label = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + getOriginalLabelId(block.entries[0]) + ' */' : '') + '\n';
- } // otherwise, should have been set before!
- if (func.setjmpTable) {
- if (!ASM_JS) {
- var setjmpTable = {};
- ret += indent + 'var mySetjmpIds = {};\n';
- ret += indent + 'var setjmpTable = {';
- func.setjmpTable.forEach(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
- ret += '"' + getLabelId(triple.oldLabel) + '": ' + 'function(value) { label = ' + getLabelId(triple.newLabel) + '; ' + triple.assignTo + ' = value },';
- });
- ret += 'dummy: 0';
- ret += '};\n';
- } else {
- ret += 'var setjmpLabel = 0;\n';
- ret += 'var setjmpTable = ' + RuntimeGenerator.stackAlloc(4 * (MAX_SETJMPS + 1) * 2) + ';\n';
- ret += makeSetValue('setjmpTable', '0', '0', 'i32') + ';'; // initialize first entry to 0
- }
- }
- ret += indent + 'while(1) ';
- if (func.setjmpTable && !ASM_JS) {
- ret += 'try { ';
- }
- ret += 'switch(' + asmCoercion('label', 'i32') + ') {\n';
- ret += block.labels.map(function(label) {
- return indent + INDENTATION + 'case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n'
- + getLabelLines(label, indent + INDENTATION + INDENTATION);
- }).join('\n') + '\n';
- if (func.setjmpTable && ASM_JS) {
- // emit a label in which we write to the proper local variable, before jumping to the actual label
- ret += INDENTATION + 'case ' + SETJMP_LABEL + ': ';
- ret += func.setjmpTable.map(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
- return 'if ((setjmpLabel|0) == ' + getLabelId(triple.oldLabel) + ') { ' + triple.assignTo + ' = threwValue; label = ' + triple.newLabel + ' }\n';
- }).join(' else ');
- if (ASSERTIONS) ret += 'else abort(-3);\n';
- ret += '__THREW__ = threwValue = 0;\n';
- ret += 'break;\n';
- }
- if (ASSERTIONS) ret += indent + INDENTATION + 'default: assert(0' + (ASM_JS ? '' : ', "bad label: " + label') + ');\n';
- ret += indent + '}\n';
- if (func.setjmpTable && !ASM_JS) {
- ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }';
+
+ if (PRINT_SPLIT_FILE_MARKER && Debugging.on && Debugging.getAssociatedSourceFile(label.lines[label.lines.length-1].lineNum)) {
+ // Overwrite the associated source file for every line. The last line should contain the source file associated to
+ // the return value/address of outer most block (the marked function).
+ associatedSourceFile = Debugging.getAssociatedSourceFile(label.lines[label.lines.length-1].lineNum);
+ }
+
+ // for special labels we care about (for phi), mark that we visited them
+ var i = 0;
+ return ret + label.lines.map(function(line) {
+ var JS = line.JS;
+ if (relooping && i == label.lines.length-1) {
+ if (line.intertype == 'branch' || line.intertype == 'switch') {
+ JS = ''; // just branching operations - done in the relooper, so nothing need be done here
+ } else if (line.intertype == 'invoke') {
+ JS = line.reloopingJS; // invokes have code that is not rendered in the relooper (the call inside a try-catch)
}
- } else {
- ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent);
}
- ret += '\n';
- } else {
- // Reloop multiple blocks using the compiled relooper
-
- //Relooper.setDebug(1);
- Relooper.init();
-
- if (ASM_JS) Relooper.setAsmJSMode(1);
-
- var blockMap = {};
- // add blocks
- for (var i = 0; i < block.labels.length; i++) {
- var label = block.labels[i];
- var content = getLabelLines(label, '', true);
- //printErr(func.ident + ' : ' + label.ident + ' : ' + content + '\n');
- var last = label.lines[label.lines.length-1];
- if (!last.signedIdent) {
- blockMap[label.ident] = Relooper.addBlock(content);
+ i++;
+ // invoke instructions span two lines, and the debug info is located
+ // on the second line, hence the +1
+ return JS + (Debugging.on ? Debugging.getComment(line.lineNum + (line.intertype === 'invoke' ? 1 : 0)) : '');
+ })
+ .join('\n')
+ .split('\n') // some lines include line breaks
+ .map(function(line) { return indent + line })
+ .join('\n');
+ }
+ var ret = '';
+ if (!RELOOP || func.forceEmulated) { // TODO: also if just 1 label?
+ if (block.labels.length > 1) {
+ if (block.entries.length == 1) {
+ ret += indent + 'label = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + getOriginalLabelId(block.entries[0]) + ' */' : '') + '\n';
+ } // otherwise, should have been set before!
+ if (func.setjmpTable) {
+ if (!ASM_JS) {
+ var setjmpTable = {};
+ ret += indent + 'var mySetjmpIds = {};\n';
+ ret += indent + 'var setjmpTable = {';
+ func.setjmpTable.forEach(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
+ ret += '"' + getLabelId(triple.oldLabel) + '": ' + 'function(value) { label = ' + getLabelId(triple.newLabel) + '; ' + triple.assignTo + ' = value },';
+ });
+ ret += 'dummy: 0';
+ ret += '};\n';
} else {
- assert(last.intertype == 'switch');
- blockMap[label.ident] = Relooper.addBlock(content, last.signedIdent);
+ ret += 'var setjmpLabel = 0;\n';
+ ret += 'var setjmpTable = ' + RuntimeGenerator.stackAlloc(4 * (MAX_SETJMPS + 1) * 2) + ';\n';
+ ret += makeSetValue('setjmpTable', '0', '0', 'i32') + ';'; // initialize first entry to 0
}
}
- // add branchings
- function relevant(x) { return x && x.length > 2 ? x : 0 } // ignores ';' which valueJS and label*JS can be if empty
- for (var i = 0; i < block.labels.length; i++) {
- var label = block.labels[i];
- var ident = label.ident;
- var last = label.lines[label.lines.length-1];
- //printErr('zz last ' + dump(last));
- if (last.intertype == 'branch') {
- if (last.label) { // 1 target
- Relooper.addBranch(blockMap[ident], blockMap[last.label], 0, relevant(last.labelJS));
- } else { // 2 targets
- Relooper.addBranch(blockMap[ident], blockMap[last.labelTrue], last.valueJS, relevant(last.labelTrueJS));
- Relooper.addBranch(blockMap[ident], blockMap[last.labelFalse], 0, relevant(last.labelFalseJS));
- }
- } else if (last.intertype == 'switch') {
- last.groupedLabels.forEach(function(switchLabel) {
- Relooper.addBranch(blockMap[ident], blockMap[switchLabel.label], switchLabel.value, relevant(switchLabel.labelJS));
- });
- Relooper.addBranch(blockMap[ident], blockMap[last.defaultLabel], 0, relevant(last.defaultLabelJS));
- } else if (last.intertype == 'invoke') {
- Relooper.addBranch(blockMap[ident], blockMap[last.toLabel], '!__THREW__', relevant(last.toLabelJS));
- Relooper.addBranch(blockMap[ident], blockMap[last.unwindLabel], 0, relevant(last.unwindLabelJS));
- } else if (last.intertype in RELOOP_IGNORED_LASTS) {
- } else {
- throw 'unknown reloop last line: ' + last.intertype;
+ ret += indent + 'while(1) ';
+ if (func.setjmpTable && !ASM_JS) {
+ ret += 'try { ';
+ }
+ ret += 'switch(' + asmCoercion('label', 'i32') + ') {\n';
+ ret += block.labels.map(function(label) {
+ return indent + INDENTATION + 'case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n'
+ + getLabelLines(label, indent + INDENTATION + INDENTATION);
+ }).join('\n') + '\n';
+ if (func.setjmpTable && ASM_JS) {
+ // emit a label in which we write to the proper local variable, before jumping to the actual label
+ ret += INDENTATION + 'case ' + SETJMP_LABEL + ': ';
+ ret += func.setjmpTable.map(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
+ return 'if ((setjmpLabel|0) == ' + getLabelId(triple.oldLabel) + ') { ' + triple.assignTo + ' = threwValue; label = ' + triple.newLabel + ' }\n';
+ }).join(' else ');
+ if (ASSERTIONS) ret += 'else abort(-3);\n';
+ ret += '__THREW__ = threwValue = 0;\n';
+ ret += 'break;\n';
+ }
+ if (ASSERTIONS) ret += indent + INDENTATION + 'default: assert(0' + (ASM_JS ? '' : ', "bad label: " + label') + ');\n';
+ ret += indent + '}\n';
+ if (func.setjmpTable && !ASM_JS) {
+ ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }';
+ }
+ } else {
+ ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent);
+ }
+ ret += '\n';
+ } else {
+ // Reloop multiple blocks using the compiled relooper
+
+ //Relooper.setDebug(1);
+ Relooper.init();
+
+ if (ASM_JS) Relooper.setAsmJSMode(1);
+
+ var blockMap = {};
+ // add blocks
+ for (var i = 0; i < block.labels.length; i++) {
+ var label = block.labels[i];
+ var content = getLabelLines(label, '', true);
+ //printErr(func.ident + ' : ' + label.ident + ' : ' + content + '\n');
+ var last = label.lines[label.lines.length-1];
+ if (!last.signedIdent) {
+ blockMap[label.ident] = Relooper.addBlock(content);
+ } else {
+ assert(last.intertype == 'switch');
+ blockMap[label.ident] = Relooper.addBlock(content, last.signedIdent);
+ }
+ }
+ // add branchings
+ function relevant(x) { return x && x.length > 2 ? x : 0 } // ignores ';' which valueJS and label*JS can be if empty
+ for (var i = 0; i < block.labels.length; i++) {
+ var label = block.labels[i];
+ var ident = label.ident;
+ var last = label.lines[label.lines.length-1];
+ //printErr('zz last ' + dump(last));
+ if (last.intertype == 'branch') {
+ if (last.label) { // 1 target
+ Relooper.addBranch(blockMap[ident], blockMap[last.label], 0, relevant(last.labelJS));
+ } else { // 2 targets
+ Relooper.addBranch(blockMap[ident], blockMap[last.labelTrue], last.valueJS, relevant(last.labelTrueJS));
+ Relooper.addBranch(blockMap[ident], blockMap[last.labelFalse], 0, relevant(last.labelFalseJS));
}
+ } else if (last.intertype == 'switch') {
+ last.groupedLabels.forEach(function(switchLabel) {
+ Relooper.addBranch(blockMap[ident], blockMap[switchLabel.label], switchLabel.value, relevant(switchLabel.labelJS));
+ });
+ Relooper.addBranch(blockMap[ident], blockMap[last.defaultLabel], 0, relevant(last.defaultLabelJS));
+ } else if (last.intertype == 'invoke') {
+ Relooper.addBranch(blockMap[ident], blockMap[last.toLabel], '!__THREW__', relevant(last.toLabelJS));
+ Relooper.addBranch(blockMap[ident], blockMap[last.unwindLabel], 0, relevant(last.unwindLabelJS));
+ } else if (last.intertype in RELOOP_IGNORED_LASTS) {
+ } else {
+ throw 'unknown reloop last line: ' + last.intertype;
}
- ret += Relooper.render(blockMap[block.entries[0]]);
}
- return ret;
- }
- func.JS += walkBlock(func.block, INDENTATION);
- // Finalize function
- if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " INDENT = INDENT.substr(0, INDENT.length-2);\n";
- // Ensure a return in a function with a type that returns, even if it lacks a return (e.g., if it aborts())
- if (RELOOP && func.lines.length > 0 && func.returnType != 'void') {
- var returns = func.labels.filter(function(label) { return label.lines[label.lines.length-1].intertype == 'return' }).length;
- if (returns == 0) func.JS += INDENTATION + 'return ' + asmCoercion('0', func.returnType);
- }
- func.JS += '}\n';
-
- if (PRINT_SPLIT_FILE_MARKER) {
- func.JS += '\n//FUNCTION_END_MARKER_OF_SOURCE_FILE_' + associatedSourceFile + '\n';
+ ret += Relooper.render(blockMap[block.entries[0]]);
+ Relooper.cleanup();
}
+ return ret;
+ }
+ func.JS += walkBlock(func.block, INDENTATION);
+ // Finalize function
+ if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " INDENT = INDENT.substr(0, INDENT.length-2);\n";
+ // Ensure a return in a function with a type that returns, even if it lacks a return (e.g., if it aborts())
+ if (RELOOP && func.lines.length > 0 && func.returnType != 'void') {
+ var returns = func.labels.filter(function(label) { return label.lines[label.lines.length-1].intertype == 'return' }).length;
+ if (returns == 0) func.JS += INDENTATION + 'return ' + asmCoercion('0', func.returnType);
+ }
+ func.JS += '}\n';
+
+ if (PRINT_SPLIT_FILE_MARKER) {
+ func.JS += '\n//FUNCTION_END_MARKER_OF_SOURCE_FILE_' + associatedSourceFile + '\n';
+ }
- if (!ASM_JS && (EXPORT_ALL || (func.ident in EXPORTED_FUNCTIONS))) {
- func.JS += 'Module["' + func.ident + '"] = ' + func.ident + ';';
- }
+ if (!ASM_JS && (EXPORT_ALL || (func.ident in EXPORTED_FUNCTIONS))) {
+ func.JS += 'Module["' + func.ident + '"] = ' + func.ident + ';';
+ }
- if (!ASM_JS && INLINING_LIMIT && func.lines.length >= INLINING_LIMIT) {
- func.JS += func.ident + '["X"]=1;';
- }
+ if (!ASM_JS && INLINING_LIMIT && func.lines.length >= INLINING_LIMIT) {
+ func.JS += func.ident + '["X"]=1;';
+ }
- if (BUILD_AS_SHARED_LIB == 2) {
- // TODO: make the assert conditional on ASSERTIONS
- func.JS += 'if (globalScope) { assert(!globalScope["' + func.ident + '"]); globalScope["' + func.ident + '"] = ' + func.ident + ' }';
- }
+ if (BUILD_AS_SHARED_LIB == 2) {
+ // TODO: make the assert conditional on ASSERTIONS
+ func.JS += 'if (globalScope) { assert(!globalScope["' + func.ident + '"]); globalScope["' + func.ident + '"] = ' + func.ident + ' }';
+ }
- func.JS = func.JS.replace(/\n *;/g, '\n'); // remove unneeded lines
+ func.JS = func.JS.replace(/\n *;/g, '\n'); // remove unneeded lines
- if (MAIN_MODULE || SIDE_MODULE) {
- // Clone the function for each of its aliases. We do not know which name it will be used by in another module,
- // and we do not have a heavyweight metadata system to resolve aliases during linking
- var aliases = Functions.aliases[func.ident];
- if (aliases) {
- var body = func.JS.substr(func.JS.indexOf('('));
- aliases.forEach(function(alias) {
- func.JS += '\n' + 'function ' + alias + body;
- });
- }
+ if (MAIN_MODULE || SIDE_MODULE) {
+ // Clone the function for each of its aliases. We do not know which name it will be used by in another module,
+ // and we do not have a heavyweight metadata system to resolve aliases during linking
+ var aliases = Functions.aliases[func.ident];
+ if (aliases) {
+ var body = func.JS.substr(func.JS.indexOf('('));
+ aliases.forEach(function(alias) {
+ func.JS += '\n' + 'function ' + alias + body;
+ });
}
-
- return func;
}
- });
+ itemsDict.function.push(func);
+ }
function getVarData(funcData, ident) {
var local = funcData.variables[ident];
@@ -898,18 +863,6 @@ function JSify(data, functionsOnly, givenFunctions) {
return data.impl;
}
- substrate.addActor('FuncLineTriager', {
- processItem: function(item) {
- if (item.intertype == 'function') {
- this.forwardItem(item, 'FunctionReconstructor'); // XXX not really needed
- } else if (item.JS) {
- this.forwardItem(item, 'FunctionReconstructor'); // XXX not really needed
- } else {
- this.forwardItem(item, 'Intertype:' + item.intertype);
- }
- }
- });
-
// An interitem that has |assignTo| is an assign to that item. They call this function which
// generates the actual assignment.
function makeAssign(item) {
@@ -944,29 +897,16 @@ function JSify(data, functionsOnly, givenFunctions) {
}
// Function lines
- function makeFuncLineActor(intertype, func) {
- return substrate.addActor('Intertype:' + intertype, {
- processItem: function(item) {
- item.JS = func(item);
- if (!item.JS) throw "No JS generated for " + dump((item.funcData=null,item));
- if (item.assignTo) {
- makeAssign(item);
- if (!item.JS) throw "No assign JS generated for " + dump(item);
- }
- this.forwardItem(item, 'FunctionReconstructor');
- }
- });
- }
- makeFuncLineActor('value', function(item) {
+ function valueHandler(item) {
return item.ident;
- });
- makeFuncLineActor('noop', function(item) {
+ }
+ function noopHandler(item) {
return ';';
- });
- makeFuncLineActor('var', function(item) { // assigns into phis become simple vars
+ }
+ function varHandler(item) { // assigns into phis become simple vars
return ASM_JS ? ';' : ('var ' + item.ident + ';');
- });
- makeFuncLineActor('store', function(item) {
+ }
+ function storeHandler(item) {
var value = finalizeLLVMParameter(item.value);
if (pointingLevels(item.pointerType) == 1) {
value = parseNumerical(value, item.valueType);
@@ -998,9 +938,9 @@ function JSify(data, functionsOnly, givenFunctions) {
throw 'unknown [store] impl: ' + impl;
}
return null;
- });
+ }
- makeFuncLineActor('deleted', function(item) { return ';' });
+ function deletedHandler(item) { return ';' }
function getOriginalLabelId(label) {
var funcData = Framework.currItem.funcData;
@@ -1113,7 +1053,7 @@ function JSify(data, functionsOnly, givenFunctions) {
*/
}
- makeFuncLineActor('branch', function(item) {
+ function branchHandler(item) {
var phiSets = calcPhiSets(item);
if (!item.value) {
return (item.labelJS = getPhiSetsForLabel(phiSets, item.label)) + makeBranch(item.label, item.currLabelId);
@@ -1134,8 +1074,8 @@ function JSify(data, functionsOnly, givenFunctions) {
return head + labelTrue + else_ + labelFalse + tail;
}
}
- });
- makeFuncLineActor('switch', function(item) {
+ }
+ function switchHandler(item) {
// use a switch if the range is not too big or sparse
var minn = Infinity, maxx = -Infinity;
item.switchLabels.forEach(function(switchLabel) {
@@ -1174,7 +1114,8 @@ function JSify(data, functionsOnly, givenFunctions) {
if (!useIfs) {
ret += 'switch(' + signedIdent + ') {\n';
}
- for (var targetLabel in targetLabels) {
+ // process target labels, sorting them so output is consistently ordered
+ keys(targetLabels).sort().forEach(function(targetLabel) {
if (!first && useIfs) {
ret += 'else ';
} else {
@@ -1202,7 +1143,7 @@ function JSify(data, functionsOnly, givenFunctions) {
labelJS: phiSet
});
}
- }
+ });
var phiSet = item.defaultLabelJS = getPhiSetsForLabel(phiSets, item.defaultLabel);
if (useIfs) {
if (item.switchLabels.length > 0) ret += 'else {\n';
@@ -1219,8 +1160,8 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += ' ' + toNiceIdent(item.value);
}
return ret;
- });
- makeFuncLineActor('return', function(item) {
+ }
+ function returnHandler(item) {
var ret = RuntimeGenerator.stackExit(item.funcData.initialStack, item.funcData.otherStackAllocations) + ';\n';
if (LABEL_DEBUG && functionNameFilterTest(item.funcData.ident)) {
ret += "Module.print(INDENT + 'Exiting: " + item.funcData.ident + "');\n"
@@ -1233,9 +1174,9 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += ' ' + asmCoercion(value, item.type);
}
return ret + ';';
- });
- makeFuncLineActor('resume', function(item) {
- if (DISABLE_EXCEPTION_CATCHING) return 'abort()';
+ }
+ function resumeHandler(item) {
+ if (DISABLE_EXCEPTION_CATCHING && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST)) return 'abort()';
if (item.ident == 0) {
// No exception to resume, so we can just bail.
// This is related to issue #917 and http://llvm.org/PR15518
@@ -1244,8 +1185,8 @@ function JSify(data, functionsOnly, givenFunctions) {
// If there is no current exception, set this one as it (during a resume, the current exception can be wiped out)
var ptr = makeStructuralAccess(item.ident, 0);
return '___resumeException(' + asmCoercion(ptr, 'i32') + ')';
- });
- makeFuncLineActor('invoke', function(item) {
+ }
+ function invokeHandler(item) {
// Wrapping in a function lets us easily return values if we are
// in an assignment
var disabled = DISABLE_EXCEPTION_CATCHING == 2 && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST);
@@ -1291,14 +1232,14 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += 'if (!__THREW__) { ' + item.toLabelJS + makeBranch(item.toLabel, item.currLabelId)
+ ' } else { ' + item.unwindLabelJS + makeBranch(item.unwindLabel, item.currLabelId) + ' }';
return ret;
- });
- makeFuncLineActor('atomic', function(item) {
+ }
+ function atomicHandler(item) {
var type = item.params[0].type;
var param1 = finalizeLLVMParameter(item.params[0]);
var param2 = finalizeLLVMParameter(item.params[1]);
switch (item.op) {
- case 'add': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue+' + param2, type, null, null, null, null, ',') + ',tempValue)';
- case 'sub': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue-' + param2, type, null, null, null, null, ',') + ',tempValue)';
+ case 'add': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, asmCoercion('tempValue+' + param2, type), type, null, null, null, null, ',') + ',tempValue)';
+ case 'sub': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, asmCoercion('tempValue-' + param2, type), type, null, null, null, null, ',') + ',tempValue)';
case 'or': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue|' + param2, type, null, null, null, null, ',') + ',tempValue)';
case 'and': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue&' + param2, type, null, null, null, null, ',') + ',tempValue)';
case 'xor': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue^' + param2, type, null, null, null, null, ',') + ',tempValue)';
@@ -1309,9 +1250,9 @@ function JSify(data, functionsOnly, givenFunctions) {
}
default: throw 'unhandled atomic op: ' + item.op;
}
- });
- makeFuncLineActor('landingpad', function(item) {
- if (DISABLE_EXCEPTION_CATCHING && USE_TYPED_ARRAYS == 2) {
+ }
+ function landingpadHandler(item) {
+ if (DISABLE_EXCEPTION_CATCHING && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST) && USE_TYPED_ARRAYS == 2) {
ret = makeVarDef(item.assignTo) + '$0 = 0; ' + item.assignTo + '$1 = 0;';
item.assignTo = null;
if (VERBOSE) warnOnce('landingpad, but exceptions are disabled!');
@@ -1324,18 +1265,18 @@ function JSify(data, functionsOnly, givenFunctions) {
item.assignTo = null;
}
return ret;
- });
- makeFuncLineActor('load', function(item) {
+ }
+ function loadHandler(item) {
var value = finalizeLLVMParameter(item.pointer);
var impl = item.ident ? getVarImpl(item.funcData, item.ident) : VAR_EMULATED;
switch (impl) {
case VAR_NATIVIZED: {
if (isNumber(item.ident)) {
- item.assignTo = null;
// Direct read from a memory address; this may be an intentional segfault, if not, it is a bug in the source
if (ASM_JS) {
- return 'abort(' + item.ident + ')';
+ return asmCoercion('abort(' + item.ident + ')', item.type);
} else {
+ item.assignTo = null;
return 'throw "fault on read from ' + item.ident + '";';
}
}
@@ -1344,8 +1285,8 @@ function JSify(data, functionsOnly, givenFunctions) {
case VAR_EMULATED: return makeGetValue(value, 0, item.type, 0, item.unsigned, 0, item.align);
default: throw "unknown [load] impl: " + impl;
}
- });
- makeFuncLineActor('extractvalue', function(item) {
+ }
+ function extractvalueHandler(item) {
assert(item.indexes.length == 1); // TODO: use getelementptr parsing stuff, for depth. For now, we assume that LLVM aggregates are flat,
// and we emulate them using simple JS objects { f1: , f2: , } etc., for speed
var index = item.indexes[0][0].text;
@@ -1358,8 +1299,8 @@ function JSify(data, functionsOnly, givenFunctions) {
return 'var ' + assignTo + '$0 = ' + item.ident + '.f' + index + '[0];' +
'var ' + assignTo + '$1 = ' + item.ident + '.f' + index + '[1];';
}
- });
- makeFuncLineActor('insertvalue', function(item) {
+ }
+ function insertvalueHandler(item) {
assert(item.indexes.length == 1); // TODO: see extractvalue
var ret = '(', ident;
if (item.ident === '0') {
@@ -1367,24 +1308,24 @@ function JSify(data, functionsOnly, givenFunctions) {
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) {
+ }
+ function indirectbrHandler(item) {
var phiSets = calcPhiSets(item);
var js = 'var ibr = ' + finalizeLLVMParameter(item.value) + ';\n';
for (var targetLabel in phiSets) {
js += 'if (' + makeComparison('ibr', '==', targetLabel, 'i32') + ') { ' + getPhiSetsForLabel(phiSets, targetLabel) + ' }\n';
}
return js + makeBranch('ibr', item.currLabelId, true);
- });
- makeFuncLineActor('alloca', function(item) {
+ }
+ function allocaHandler(item) {
if (typeof item.allocatedIndex === 'number') {
if (item.allocatedSize === 0) return ''; // This will not actually be shown - it's nativized
return asmCoercion(getFastValue('sp', '+', item.allocatedIndex.toString()), 'i32');
} else {
return RuntimeGenerator.stackAlloc(getFastValue(calcAllocatedSize(item.allocatedType), '*', item.allocatedNum));
}
- });
- makeFuncLineActor('va_arg', function(item) {
+ }
+ function va_argHandler(item) {
assert(TARGET_LE32);
var ident = item.value.ident;
var move = Runtime.STACK_ALIGN;
@@ -1393,11 +1334,11 @@ function JSify(data, functionsOnly, givenFunctions) {
return '(tempInt=' + makeGetValue(ident, Runtime.QUANTUM_SIZE, '*') + ',' +
makeSetValue(ident, Runtime.QUANTUM_SIZE, 'tempInt + ' + move, '*') + ',' +
makeGetValue(makeGetValue(ident, 0, '*'), 'tempInt', item.type) + ')';
- });
+ }
- makeFuncLineActor('mathop', processMathop);
+ var mathopHandler = processMathop;
- makeFuncLineActor('bitcast', function(item) {
+ function bitcastHandler(item) {
var temp = {
op: 'bitcast', variant: null, type: item.type,
assignTo: item.assignTo,
@@ -1406,7 +1347,7 @@ function JSify(data, functionsOnly, givenFunctions) {
var ret = processMathop(temp);
if (!temp.assignTo) item.assignTo = null; // If the assign was stolen, propagate that
return ret;
- });
+ }
function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn, invoke) {
// We cannot compile assembly. See comment in intertyper.js:'Call'
@@ -1614,33 +1555,26 @@ function JSify(data, functionsOnly, givenFunctions) {
return js;
}
- makeFuncLineActor('getelementptr', function(item) { return finalizeLLVMFunctionCall(item) });
- makeFuncLineActor('call', function(item) {
+ function getelementptrHandler(item) { return finalizeLLVMFunctionCall(item) }
+ function callHandler(item) {
if (item.standalone && LibraryManager.isStubFunction(item.ident)) return ';';
var ret = makeFunctionCall(item.ident, item.params, item.funcData, item.type, false, !!item.assignTo || !item.standalone) + (item.standalone ? ';' : '');
return makeVarArgsCleanup(ret);
- });
+ }
- makeFuncLineActor('unreachable', function(item) {
+ function unreachableHandler(item) {
var ret = '';
if (ASM_JS && item.funcData.returnType != 'void') ret = 'return ' + asmCoercion('0', item.funcData.returnType) + ';';
if (ASSERTIONS) {
ret = (ASM_JS ? 'abort()' : 'throw "Reached an unreachable!"') + ';' + ret;
}
return ret || ';';
- });
+ }
// Final combiner
- function finalCombiner(items) {
+ function finalCombiner() {
dprint('unparsedFunctions', 'Starting finalCombiner');
- var itemsDict = { type: [], GlobalVariableStub: [], functionStub: [], function: [], GlobalVariable: [], GlobalVariablePostSet: [] };
- items.forEach(function(item) {
- item.lines = null;
- 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;
@@ -1897,7 +1831,7 @@ function JSify(data, functionsOnly, givenFunctions) {
Functions.implementedFunctions[func.ident] = Functions.getSignature(func.returnType, func.params.map(function(param) { return param.type }));
});
}
- substrate.addItems(data.functionStubs, 'FunctionStub');
+ data.functionStubs.forEach(functionStubHandler);
assert(data.functions.length == 0);
} else {
if (phase == 'pre') {
@@ -1906,21 +1840,21 @@ function JSify(data, functionsOnly, givenFunctions) {
data.globalVariables._llvm_global_ctors.ctors.unshift('runPostSets'); // run postsets right before global initializers
hasCtors = true;
} else {
- substrate.addItems([{
+ globalVariableHandler({
intertype: 'GlobalVariableStub',
ident: '_llvm_global_ctors',
type: '[1 x { i32, void ()* }]',
ctors: ["runPostSets"],
- }], 'GlobalVariable');
+ });
}
}
- substrate.addItems(sortGlobals(data.globalVariables), 'GlobalVariable');
- substrate.addItems(data.aliass, 'Alias');
- substrate.addItems(data.functions, 'FunctionSplitter');
+ sortGlobals(data.globalVariables).forEach(globalVariableHandler);
+ data.aliass.forEach(aliasHandler);
+ data.functions.forEach(functionSplitter);
}
- finalCombiner(substrate.solve());
+ finalCombiner();
dprint('framework', 'Big picture: Finishing JSifier, main pass=' + mainPass);
}