aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2013-01-11 16:17:16 -0800
committerAlon Zakai <alonzakai@gmail.com>2013-01-11 16:17:16 -0800
commitc47f7eba9be951c8e308e66c2541091c6b057af8 (patch)
tree59aed1cd174e91921e867955507c13f63303f40e /src
parentada59f0a9d23d8ec19ee6a1326977ddf6e93f5f9 (diff)
parent2113958017b5def518bd4bcf0bf77e8be233a93f (diff)
Merge branch 'incoming'
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js167
-rw-r--r--src/compiler.js23
-rw-r--r--src/intertyper.js19
-rw-r--r--src/jsifier.js208
-rw-r--r--src/library.js595
-rw-r--r--src/library_browser.js78
-rw-r--r--src/library_gc.js4
-rw-r--r--src/library_gl.js123
-rw-r--r--src/library_glut.js28
-rw-r--r--src/library_sdl.js20
-rw-r--r--src/long.js33
-rw-r--r--src/modules.js88
-rw-r--r--src/parseTools.js291
-rw-r--r--src/postamble.js8
-rw-r--r--src/preamble.js73
-rw-r--r--src/relooper/Relooper.cpp16
-rw-r--r--src/relooper/Relooper.h4
-rw-r--r--src/relooper/emscripten/Makefile18
-rw-r--r--src/relooper/emscripten/glue.js3
-rw-r--r--src/relooper/test.txt4
-rw-r--r--src/runtime.js86
-rw-r--r--src/settings.js25
-rw-r--r--src/utility.js6
23 files changed, 1263 insertions, 657 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 014579f4..0ad3e017 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -19,7 +19,7 @@ function recomputeLines(func) {
var BRANCH_INVOKE = set('branch', 'invoke');
var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic');
-var UNUNFOLDABLE = set('value', 'type', 'phiparam');
+var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam');
// Analyzer
@@ -120,12 +120,14 @@ function analyzer(data, sidePass) {
processItem: function(data) {
// Legalization
if (USE_TYPED_ARRAYS == 2) {
- function getLegalVars(base, bits) {
- assert(!isNumber(base));
+ function getLegalVars(base, bits, allowLegal) {
+ if (allowLegal && bits <= 32) return [{ ident: base, bits: bits }];
+ if (isNumber(base)) return getLegalLiterals(base, bits);
var ret = new Array(Math.ceil(bits/32));
var i = 0;
+ if (base == 'zeroinitializer' || base == 'undef') base = 0;
while (bits > 0) {
- ret[i] = { ident: base + '$' + i, bits: Math.min(32, bits) };
+ ret[i] = { ident: base ? base + '$' + i : '0', bits: Math.min(32, bits) };
bits -= 32;
i++;
}
@@ -142,6 +144,23 @@ function analyzer(data, sidePass) {
}
return ret;
}
+ function getLegalStructuralParts(value) {
+ return value.params.slice(0);
+ }
+ function getLegalParams(params, bits) {
+ return params.map(function(param) {
+ var value = param.value || param;
+ if (isNumber(value.ident)) {
+ return getLegalLiterals(value.ident, bits);
+ } else if (value.intertype == 'structvalue') {
+ return getLegalStructuralParts(value).map(function(part) {
+ return { ident: part.ident, bits: part.type.substr(1) };
+ });
+ } else {
+ return getLegalVars(value.ident, bits);
+ }
+ });
+ }
// Uses the right factor to multiply line numbers by so that they fit in between
// the line[i] and the line after it
function interpLines(lines, i, toAdd) {
@@ -191,6 +210,7 @@ function analyzer(data, sidePass) {
// Legalize lines in labels
var tempId = 0;
func.labels.forEach(function(label) {
+ if (dcheck('legalizer')) dprint('zz legalizing: \n' + dump(label.lines));
var i = 0, bits;
while (i < label.lines.length) {
var item = label.lines[i];
@@ -207,8 +227,12 @@ function analyzer(data, sidePass) {
if (isIllegalType(item.valueType) || isIllegalType(item.type)) {
isIllegal = true;
}
+ if ((item.intertype == 'load' || item.intertype == 'store') && isStructType(item.valueType)) {
+ isIllegal = true; // storing an entire structure is illegal
+ }
});
if (!isIllegal) {
+ //if (dcheck('legalizer')) dprint('no need to legalize \n' + dump(item));
i++;
continue;
}
@@ -222,10 +246,10 @@ function analyzer(data, sidePass) {
if (subItem != item && (!(subItem.intertype in UNUNFOLDABLE) ||
(subItem.intertype == 'value' && isNumber(subItem.ident) && isIllegalType(subItem.type)))) {
if (item.intertype == 'phi') {
- assert(subItem.intertype == 'value', 'We can only unfold illegal constants in phis');
+ assert(subItem.intertype == 'value' || subItem.intertype == 'structvalue', 'We can only unfold illegal constants in phis');
// we must handle this in the phi itself, if we unfold normally it will not be pushed back with the phi
} else {
- var tempIdent = '$$emscripten$temp$' + (tempId++);
+ var tempIdent = '$$etemp$' + (tempId++);
subItem.assignTo = tempIdent;
unfolded.unshift(subItem);
fixUnfolded(subItem);
@@ -234,7 +258,7 @@ function analyzer(data, sidePass) {
} else if (subItem.intertype == 'switch' && isIllegalType(subItem.type)) {
subItem.switchLabels.forEach(function(switchLabel) {
if (switchLabel.value[0] != '$') {
- var tempIdent = '$$emscripten$temp$' + (tempId++);
+ var tempIdent = '$$etemp$' + (tempId++);
unfolded.unshift({
assignTo: tempIdent,
intertype: 'value',
@@ -258,8 +282,7 @@ function analyzer(data, sidePass) {
case 'store': {
var toAdd = [];
bits = getBits(item.valueType);
- var elements;
- elements = getLegalVars(item.value.ident, bits);
+ var elements = getLegalParams([item.value], bits)[0];
var j = 0;
elements.forEach(function(element) {
var tempVar = '$st$' + i + '$' + j;
@@ -290,32 +313,43 @@ function analyzer(data, sidePass) {
i += removeAndAdd(label.lines, i, toAdd);
continue;
}
- // call, return: Return value is in an unlegalized array literal. Not fully optimal.
+ // call, return: Return the first 32 bits, the rest are in temp
case 'call': {
bits = getBits(value.type);
var elements = getLegalVars(item.assignTo, bits);
var toAdd = [value];
// legalize parameters
legalizeFunctionParameters(value.params);
- if (value.assignTo) {
+ if (value.assignTo && isIllegalType(item.type)) {
// legalize return value
- var j = 0;
- toAdd = toAdd.concat(elements.map(function(element) {
- return {
+ value.assignTo = elements[0].ident;
+ for (var j = 1; j < elements.length; j++) {
+ var element = elements[j];
+ toAdd.push({
intertype: 'value',
assignTo: element.ident,
- type: 'i' + bits,
- ident: value.assignTo + '[' + (j++) + ']'
- };
- }));
+ type: element.bits,
+ ident: 'tempRet' + (j - 1)
+ });
+ assert(j<10); // TODO: dynamically create more than 10 tempRet-s
+ }
}
i += removeAndAdd(label.lines, i, toAdd);
continue;
}
+ case 'landingpad': {
+ // not much to legalize
+ i++;
+ continue;
+ }
case 'return': {
bits = getBits(item.type);
var elements = getLegalVars(item.value.ident, bits);
- item.value.ident = '[' + elements.map(function(element) { return element.ident }).join(',') + ']';
+ item.value.ident = '(';
+ for (var j = 1; j < elements.length; j++) {
+ item.value.ident += 'tempRet' + (j-1) + '=' + elements[j].ident + ',';
+ }
+ item.value.ident += elements[0].ident + ')';
i++;
continue;
}
@@ -341,6 +375,21 @@ function analyzer(data, sidePass) {
i += removeAndAdd(label.lines, i, toAdd);
continue;
}
+ case 'structvalue': {
+ bits = getBits(value.type);
+ var elements = getLegalVars(item.assignTo, bits);
+ var toAdd = [];
+ for (var j = 0; j < item.params.length; j++) {
+ toAdd[j] = {
+ intertype: 'value',
+ assignTo: elements[j].ident,
+ type: 'i32',
+ ident: item.params[j].ident
+ };
+ }
+ i += removeAndAdd(label.lines, i, toAdd);
+ continue;
+ }
case 'load': {
bits = getBits(value.valueType);
var elements = getLegalVars(item.assignTo, bits);
@@ -382,13 +431,9 @@ function analyzer(data, sidePass) {
var toAdd = [];
var elements = getLegalVars(item.assignTo, bits);
var j = 0;
- var literalValues = {}; // special handling of literals - we cannot unfold them normally
- value.params.map(function(param) {
- if (isNumber(param.value.ident)) {
- literalValues[param.value.ident] = getLegalLiterals(param.value.ident, bits);
- }
- });
+ var values = getLegalParams(value.params, bits);
elements.forEach(function(element) {
+ var k = 0;
toAdd.push({
intertype: 'phi',
assignTo: element.ident,
@@ -399,7 +444,7 @@ function analyzer(data, sidePass) {
label: param.label,
value: {
intertype: 'value',
- ident: (param.value.ident in literalValues) ? literalValues[param.value.ident][j].ident : (param.value.ident + '$' + j),
+ ident: values[k++][j].ident,
type: 'i' + element.bits,
}
};
@@ -414,6 +459,62 @@ function analyzer(data, sidePass) {
i++;
continue; // special case, handled in makeComparison
}
+ case 'extractvalue': { // XXX we assume 32-bit alignment in extractvalue/insertvalue,
+ // but in theory they can run on packed structs too (see use getStructuralTypePartBits)
+ // potentially legalize the actual extracted value too if it is >32 bits, not just the extraction in general
+ var index = item.indexes[0][0].text;
+ var parts = getStructureTypeParts(item.type);
+ var indexedType = parts[index];
+ var targetBits = getBits(indexedType);
+ var sourceBits = getBits(item.type);
+ var elements = getLegalVars(item.assignTo, targetBits, true); // possibly illegal
+ var sourceElements = getLegalVars(item.ident, sourceBits); // definitely illegal
+ var toAdd = [];
+ var sourceIndex = 0;
+ for (var partIndex = 0; partIndex < parts.length; partIndex++) {
+ if (partIndex == index) {
+ for (var j = 0; j < elements.length; j++) {
+ toAdd.push({
+ intertype: 'value',
+ assignTo: elements[j].ident,
+ type: 'i' + elements[j].bits,
+ ident: sourceElements[sourceIndex+j].ident
+ });
+ }
+ break;
+ }
+ sourceIndex += getStructuralTypePartBits(parts[partIndex])/32;
+ }
+ i += removeAndAdd(label.lines, i, toAdd);
+ continue;
+ }
+ case 'insertvalue': {
+ var index = item.indexes[0][0].text; // the modified index
+ var parts = getStructureTypeParts(item.type);
+ var indexedType = parts[index];
+ var indexBits = getBits(indexedType);
+ var bits = getBits(item.type); // source and target
+ bits = getBits(value.type);
+ var toAdd = [];
+ var elements = getLegalVars(item.assignTo, bits);
+ var sourceElements = getLegalVars(item.ident, bits);
+ var indexElements = getLegalVars(item.value.ident, indexBits, true); // possibly legal
+ var sourceIndex = 0;
+ for (var partIndex = 0; partIndex < parts.length; partIndex++) {
+ var currNum = getStructuralTypePartBits(parts[partIndex])/32;
+ for (var j = 0; j < currNum; j++) {
+ toAdd.push({
+ intertype: 'value',
+ assignTo: elements[sourceIndex+j].ident,
+ type: 'i' + elements[sourceIndex+j].bits,
+ ident: partIndex == index ? indexElements[j].ident : sourceElements[sourceIndex+j].ident
+ });
+ }
+ sourceIndex += currNum;
+ }
+ i += removeAndAdd(label.lines, i, toAdd);
+ continue;
+ }
case 'bitcast': {
var inType = item.type2;
var outType = item.type;
@@ -477,17 +578,16 @@ function analyzer(data, sidePass) {
}
case 'select': {
sourceBits = targetBits = getBits(value.params[1].type);
- var otherElementsA = getLegalVars(value.params[1].ident, sourceBits);
- var otherElementsB = getLegalVars(value.params[2].ident, sourceBits);
+ var params = getLegalParams(value.params.slice(1), sourceBits);
processor = function(result, j) {
return {
intertype: 'mathop',
op: 'select',
- type: 'i' + otherElementsA[j].bits,
+ type: 'i' + params[0][j].bits,
params: [
value.params[0],
- { intertype: 'value', ident: otherElementsA[j].ident, type: 'i' + otherElementsA[j].bits },
- { intertype: 'value', ident: otherElementsB[j].ident, type: 'i' + otherElementsB[j].bits }
+ { intertype: 'value', ident: params[0][j].ident, type: 'i' + params[0][j].bits },
+ { intertype: 'value', ident: params[1][j].ident, type: 'i' + params[1][j].bits }
]
};
};
@@ -554,9 +654,10 @@ function analyzer(data, sidePass) {
// We can't statically legalize this, do the operation at runtime TODO: optimize
assert(sourceBits == 64, 'TODO: handle nonconstant shifts on != 64 bits');
value.intertype = 'value';
- value.ident = 'Runtime.bitshift64(' + sourceElements[0].ident + ', ' +
+ value.ident = 'Runtime' + (ASM_JS ? '_' : '.') + 'bitshift64(' + sourceElements[0].ident + ', ' +
sourceElements[1].ident + ',"' + value.op + '",' + value.params[1].ident + '$0);' +
- 'var ' + value.assignTo + '$0 = ' + value.assignTo + '[0], ' + value.assignTo + '$1 = ' + value.assignTo + '[1];';
+ 'var ' + value.assignTo + '$0 = ' + makeGetTempDouble(0) + ', ' + value.assignTo + '$1 = ' + makeGetTempDouble(1) + ';';
+ value.assignTo = null;
i++;
continue;
}
diff --git a/src/compiler.js b/src/compiler.js
index 35f746a5..118ca83a 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -174,18 +174,25 @@ RUNTIME_DEBUG = LIBRARY_DEBUG || GL_DEBUG;
// Settings sanity checks
assert(!(USE_TYPED_ARRAYS === 2 && QUANTUM_SIZE !== 4), 'For USE_TYPED_ARRAYS == 2, must have normal QUANTUM_SIZE of 4');
+if (ASM_JS) {
+ assert(!ALLOW_MEMORY_GROWTH, 'Cannot grow asm.js heap');
+ assert((TOTAL_MEMORY&(TOTAL_MEMORY-1)) == 0, 'asm.js heap must be power of 2');
+}
+assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB)); // shared libraries must have named globals
// Output some info and warnings based on settings
-if (!MICRO_OPTS || !RELOOP || ASSERTIONS || CHECK_SIGNS || CHECK_OVERFLOWS || INIT_STACK || INIT_HEAP ||
- !SKIP_STACK_IN_SMALL || SAFE_HEAP || PGO || PROFILE || !DISABLE_EXCEPTION_CATCHING) {
- print('// Note: Some Emscripten settings will significantly limit the speed of the generated code.');
-} else {
- print('// Note: For maximum-speed code, see "Optimizing Code" on the Emscripten wiki, https://github.com/kripken/emscripten/wiki/Optimizing-Code');
-}
+if (phase == 'pre') {
+ if (!MICRO_OPTS || !RELOOP || ASSERTIONS || CHECK_SIGNS || CHECK_OVERFLOWS || INIT_STACK || INIT_HEAP ||
+ !SKIP_STACK_IN_SMALL || SAFE_HEAP || PGO || PROFILE || !DISABLE_EXCEPTION_CATCHING) {
+ print('// Note: Some Emscripten settings will significantly limit the speed of the generated code.');
+ } else {
+ print('// Note: For maximum-speed code, see "Optimizing Code" on the Emscripten wiki, https://github.com/kripken/emscripten/wiki/Optimizing-Code');
+ }
-if (DOUBLE_MODE || CORRECT_SIGNS || CORRECT_OVERFLOWS || CORRECT_ROUNDINGS) {
- print('// Note: Some Emscripten settings may limit the speed of the generated code.');
+ if (DOUBLE_MODE || CORRECT_SIGNS || CORRECT_OVERFLOWS || CORRECT_ROUNDINGS) {
+ print('// Note: Some Emscripten settings may limit the speed of the generated code.');
+ }
}
// Load compiler code
diff --git a/src/intertyper.js b/src/intertyper.js
index 5af291c7..5bca9236 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -69,11 +69,13 @@ function intertyper(data, sidePass, baseLineNums) {
if (mainPass && (line[0] == '%' || line[0] == '@')) {
// If this isn't a type, it's a global variable, make a note of the information now, we will need it later
- var testType = /[@%\w\d\.\" $-]+ = type .*/.exec(line);
+ var parts = line.split(' = ');
+ assert(parts.length >= 2);
+ var left = parts[0], right = parts.slice(1).join(' = ');
+ var testType = /^type .*/.exec(right);
if (!testType) {
- var global = /([@%\w\d\.\" $-]+) = .*/.exec(line);
- var globalIdent = toNiceIdent(global[1]);
- var testAlias = /[@%\w\d\.\" $-]+ = (hidden )?alias .*/.exec(line);
+ var globalIdent = toNiceIdent(left);
+ var testAlias = /^(hidden )?alias .*/.exec(right);
Variables.globals[globalIdent] = {
name: globalIdent,
alias: !!testAlias,
@@ -125,6 +127,7 @@ function intertyper(data, sidePass, baseLineNums) {
// We need this early, to know basic function info - ident, params, varargs
ident: toNiceIdent(func.ident),
params: func.params,
+ returnType: func.returnType,
hasVarArgs: func.hasVarArgs,
lineNum: currFunctionLineNum,
lines: currFunctionLines
@@ -520,7 +523,12 @@ function intertyper(data, sidePass, baseLineNums) {
if (item.tokens[3].item) {
var subTokens = item.tokens[3].item.tokens;
splitTokenList(subTokens).forEach(function(segment) {
- ret.ctors.push(segment[1].tokens.slice(-1)[0].text);
+ var ctor = toNiceIdent(segment[1].tokens.slice(-1)[0].text);
+ ret.ctors.push(ctor);
+ if (ASM_JS) { // must export the global constructors from asm.js module, so mark as implemented and exported
+ Functions.implementedFunctions[ctor] = 'v';
+ EXPORTED_FUNCTIONS[ctor] = 1;
+ }
});
}
} else if (!external) {
@@ -741,6 +749,7 @@ function intertyper(data, sidePass, baseLineNums) {
}
var last = getTokenIndexByText(item.tokens, ';');
item.params = splitTokenList(item.tokens.slice(1, last)).map(parseLLVMSegment);
+ item.type = item.params[1].type;
this.forwardItem(item, 'Reintegrator');
}
});
diff --git a/src/jsifier.js b/src/jsifier.js
index 70329b3f..e41bcf67 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -9,6 +9,8 @@ var STRUCT_LIST = set('struct', 'list');
var UNDERSCORE_OPENPARENS = set('_', '(');
var RELOOP_IGNORED_LASTS = set('return', 'unreachable', 'resume');
+var addedLibraryItems = {};
+
// JSifier
function JSify(data, functionsOnly, givenFunctions) {
var mainPass = !functionsOnly;
@@ -42,7 +44,9 @@ function JSify(data, functionsOnly, givenFunctions) {
var pre = processMacros(preprocess(read(preFile).replace('{{RUNTIME}}', getRuntime())));
print(pre);
- Functions.implementedFunctions = set(data.unparsedFunctions.map(function(func) { return func.ident }));
+ data.unparsedFunctions.forEach(function(func) {
+ Functions.implementedFunctions[func.ident] = Functions.getSignature(func.returnType, func.params.map(function(param) { return param.type }));
+ });
}
}
@@ -258,7 +262,7 @@ function JSify(data, functionsOnly, givenFunctions) {
var ret = [item];
if (item.ident == '_llvm_global_ctors') {
item.JS = '\n__ATINIT__ = __ATINIT__.concat([\n' +
- item.ctors.map(function(ctor) { return ' { func: ' + toNiceIdent(ctor) + ' }' }).join(',\n') +
+ item.ctors.map(function(ctor) { return ' { func: function() { ' + ctor + '() } }' }).join(',\n') +
'\n]);\n';
return ret;
} else {
@@ -273,12 +277,13 @@ function JSify(data, functionsOnly, givenFunctions) {
item.JS = makeGlobalDef(item.ident);
}
- if (item.external) {
+ if (item.external && !ASM_JS) { // ASM_JS considers externs to be globals
// Import external global variables from the library if available.
var shortident = item.ident.slice(1);
if (LibraryManager.library[shortident] &&
LibraryManager.library[shortident].length &&
!BUILD_AS_SHARED_LIB) {
+ if (addedLibraryItems[shortident]) return ret;
var val = LibraryManager.library[shortident];
var padding;
if (Runtime.isNumberType(item.type) || isPointerType(item.type)) {
@@ -302,7 +307,17 @@ function JSify(data, functionsOnly, givenFunctions) {
index = makeGlobalUse(item.ident); // index !== null indicates we are indexing this
allocator = 'ALLOC_NONE';
}
- constant = parseConst(item.value, item.type, item.ident);
+ if (item.external) {
+ assert(ASM_JS);
+ if (Runtime.isNumberType(item.type) || isPointerType(item.type)) {
+ constant = zeros(Runtime.getNativeFieldSize(item.type));
+ } else {
+ constant = makeEmptyStruct(item.type);
+ }
+ constant = JSON.stringify(constant);
+ } else {
+ constant = parseConst(item.value, item.type, item.ident);
+ }
if (typeof constant === 'string' && constant[0] != '[') {
constant = [constant]; // A single item. We may need a postset for it.
}
@@ -333,13 +348,17 @@ function JSify(data, functionsOnly, givenFunctions) {
}
js += '\n' + makePointer('[0]', null, allocator, ['void*'], index) + ';';
}
- if (EXPORT_ALL || (item.ident in EXPORTED_GLOBALS)) {
+ 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) {
+ assert(ASM_JS);
+ js = 'var ' + item.ident + ' = ' + js; // force an explicit naming, even if unnamed globals, for asm forwarding
+ }
return ret.concat({
intertype: 'GlobalVariable',
JS: js,
@@ -377,8 +396,6 @@ function JSify(data, functionsOnly, givenFunctions) {
}
});
- var addedLibraryItems = {};
-
// functionStub
substrate.addActor('FunctionStub', {
processItem: function(item) {
@@ -399,13 +416,19 @@ function JSify(data, functionsOnly, givenFunctions) {
var isFunction = false;
if (typeof snippet === 'string') {
- if (LibraryManager.library[snippet]) {
+ 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\..+/.exec(snippet))) {
+ Functions.libraryFunctions[ident] = 1;
+ }
} else if (typeof snippet === 'object') {
snippet = stringifyWithFunctions(snippet);
} else if (typeof snippet === 'function') {
@@ -420,6 +443,7 @@ function JSify(data, functionsOnly, givenFunctions) {
snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.print("[library call:' + ident + ': " + Array.prototype.slice.call(arguments).map(Runtime.prettyPrint) + "]"); ');
snippet = snippet.substr(0, snippet.length-1) + '}).apply(this, arguments); if (Runtime.debug && typeof ret !== "undefined") Module.print(" [ return:" + Runtime.prettyPrint(ret)); return ret; }';
}
+ if (ASM_JS) Functions.libraryFunctions[ident] = 1;
}
var postsetId = ident + '__postset';
@@ -435,6 +459,18 @@ function JSify(data, functionsOnly, givenFunctions) {
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);
@@ -442,7 +478,8 @@ function JSify(data, functionsOnly, givenFunctions) {
ident = '_' + ident;
}
var text = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : '');
- text += isFunction ? snippet : 'var ' + ident + '=' + snippet + ';';
+ // redirected idents just need a var, but no value assigned to them - it would be unused
+ text += isFunction ? snippet : ('var ' + ident + (redirectedIdent ? '' : '=' + snippet) + ';');
if (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS)) {
text += '\nModule["' + ident + '"] = ' + ident + ';';
}
@@ -532,8 +569,8 @@ function JSify(data, functionsOnly, givenFunctions) {
func.JS = '\n';
var paramIdents = func.params.map(function(param) {
- return (param.intertype == 'varargs') ? null : toNiceIdent(param.ident);
- }).filter(function(param) { return param != null; })
+ return toNiceIdent(param.ident);
+ });
if (CLOSURE_ANNOTATIONS) {
func.JS += '/**\n';
@@ -551,6 +588,34 @@ function JSify(data, functionsOnly, givenFunctions) {
func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n';
+ if (ASM_JS) {
+ // spell out argument types
+ func.params.forEach(function(param) {
+ func.JS += ' ' + 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 += ' var ' + chunks[i].map(function(v) {
+ if (v.type != 'i64') {
+ return v.ident + ' = ' + asmInitializer(v.type); //, func.variables[v.ident].impl);
+ } else {
+ return v.ident + '$0 = 0, ' + v.ident + '$1 = 1';
+ }
+ }).join(', ') + ';\n';
+ }
+ }
+ }
+
if (PROFILE) {
func.JS += ' if (PROFILING) { '
+ 'var __parentProfilingNode__ = PROFILING_NODE; PROFILING_NODE = PROFILING_NODE.children["' + func.ident + '"]; '
@@ -560,6 +625,21 @@ function JSify(data, functionsOnly, givenFunctions) {
+ '}\n';
}
+ if (true) { // TODO: optimize away when not needed
+ if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */';
+ func.JS += ' var label = 0;\n';
+ }
+
+ if (ASM_JS) {
+ var hasByVal = false;
+ func.params.forEach(function(param) {
+ hasByVal = hasByVal || param.byVal;
+ });
+ if (hasByVal) {
+ func.JS += ' var tempParam = 0;\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';
@@ -574,18 +654,13 @@ function JSify(data, functionsOnly, givenFunctions) {
if (param.byVal) {
var type = removePointing(param.type);
var typeInfo = Types.types[type];
- func.JS += ' var tempParam = ' + param.ident + '; ' + param.ident + ' = ' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' +
+ 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';
}
});
if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " Module.print(INDENT + ' Entering: " + func.ident + ": ' + Array.prototype.slice.call(arguments)); INDENT += ' ';\n";
- if (true) { // TODO: optimize away when not needed
- if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */';
- func.JS += ' var label;\n';
- }
-
// Walk function blocks and generate JS
function walkBlock(block, indent) {
if (!block) return '';
@@ -664,6 +739,8 @@ function JSify(data, functionsOnly, givenFunctions) {
//Relooper.setDebug(1);
Relooper.init();
+ if (ASM_JS) Relooper.setAsmJSMode(1);
+
var blockMap = {};
// add blocks
for (var i = 0; i < block.labels.length; i++) {
@@ -716,12 +793,12 @@ function JSify(data, functionsOnly, givenFunctions) {
if (PRINT_SPLIT_FILE_MARKER) {
func.JS += '\n//FUNCTION_END_MARKER_OF_SOURCE_FILE_' + associatedSourceFile + '\n';
}
-
- if (EXPORT_ALL || (func.ident in EXPORTED_FUNCTIONS)) {
+
+ if (!ASM_JS && (EXPORT_ALL || (func.ident in EXPORTED_FUNCTIONS))) {
func.JS += 'Module["' + func.ident + '"] = ' + func.ident + ';';
}
- if (INLINING_LIMIT && func.lines.length >= INLINING_LIMIT) {
+ if (!ASM_JS && INLINING_LIMIT && func.lines.length >= INLINING_LIMIT) {
func.JS += func.ident + '["X"]=1;';
}
@@ -767,8 +844,9 @@ function JSify(data, functionsOnly, givenFunctions) {
var valueJS = item.JS;
item.JS = '';
if (CLOSURE_ANNOTATIONS) item.JS += '/** @type {number} */ ';
- item.JS += (item.overrideSSA ? '' : 'var ') + toNiceIdent(item.assignTo);
-
+ if (!ASM_JS || item.intertype != 'alloca' || item.funcData.variables[item.assignTo].impl == VAR_EMULATED) { // asm only needs non-allocas
+ item.JS += ((ASM_JS || item.overrideSSA) ? '' : 'var ') + toNiceIdent(item.assignTo);
+ }
var value = parseNumerical(valueJS);
var impl = getVarImpl(item.funcData, item.assignTo);
switch (impl) {
@@ -798,7 +876,7 @@ function JSify(data, functionsOnly, givenFunctions) {