summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
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) {
return substrate.addActor('Intertype:' + intertype, {
processItem: function(item) {
item.JS = func(item);
- if (!item.JS) throw "No JS generated for " + dump(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);
@@ -814,7 +892,7 @@ function JSify(data, functionsOnly, givenFunctions) {
return ';';
});
makeFuncLineActor('var', function(item) { // assigns into phis become simple vars
- return 'var ' + item.ident + ';';
+ return ASM_JS ? ';' : ('var ' + item.ident + ';');
});
makeFuncLineActor('store', function(item) {
var value = finalizeLLVMParameter(item.value);
@@ -908,7 +986,7 @@ function JSify(data, functionsOnly, givenFunctions) {
var labelSets = phiSets[label];
// FIXME: Many of the |var |s here are not needed, but without them we get slowdowns with closure compiler. TODO: remove this workaround.
if (labelSets.length == 1) {
- return 'var ' + labelSets[0].ident + ' = ' + labelSets[0].valueJS + ';';
+ return (ASM_JS ? '' : 'var ') + labelSets[0].ident + ' = ' + labelSets[0].valueJS + ';';
}
// TODO: eliminate unneeded sets (to undefined etc.)
var deps = {}; // for each ident we will set, which others it depends on
@@ -1044,33 +1122,36 @@ function JSify(data, functionsOnly, givenFunctions) {
}
ret += 'return';
if (item.value) {
- ret += ' ' + finalizeLLVMParameter(item.value);
+ ret += ' ' + asmCoercion(finalizeLLVMParameter(item.value), item.type);
}
return ret + ';';
});
makeFuncLineActor('resume', function(item) {
// 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 (EXCEPTION_DEBUG ? 'Module.print("Resuming exception");' : '') +
- 'if (' + makeGetValue('_llvm_eh_exception.buf', 0, 'void*') + ' == 0) { ' + makeSetValue('_llvm_eh_exception.buf', 0, item.ident + '.f0', 'void*') + ' } ' +
- 'throw ' + item.ident + '.f0;';
+ 'if (' + makeGetValue('_llvm_eh_exception.buf', 0, 'void*') + ' == 0) { ' + makeSetValue('_llvm_eh_exception.buf', 0, ptr, 'void*') + ' } ' +
+ 'throw ' + ptr + ';';
});
makeFuncLineActor('invoke', function(item) {
// Wrapping in a function lets us easily return values if we are
// in an assignment
var phiSets = calcPhiSets(item);
var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type);
- var ret = '(function() { try { __THREW__ = false; return '
+ var ret = '(function() { try { __THREW__ = 0; return '
+ call_ + ' '
+ '} catch(e) { '
+ 'if (typeof e != "number") throw e; '
- + 'if (ABORT) throw e; __THREW__ = true; '
+ + 'if (ABORT) throw e; __THREW__ = 1; '
+ (EXCEPTION_DEBUG ? 'Module.print("Exception: " + e + ", currently at: " + (new Error().stack)); ' : '')
+ 'return null } })();';
if (item.assignTo) {
ret = 'var ' + item.assignTo + ' = ' + ret;
- if (isIllegalType(item.type)) {
- assert(item.type == 'i64', 'Can only handle i64 invoke among illegal invokes');
- ret += 'var ' + item.assignTo + '$0 = ' + item.assignTo + '[0], ' + item.assignTo + '$1 = ' + item.assignTo + '[1];';
+ if (USE_TYPED_ARRAYS == 2 && isIllegalType(item.type)) {
+ var bits = getBits(item.type);
+ for (var i = 0; i < bits/32; i++) {
+ ret += 'var ' + item.assignTo + '$' + i + ' = ' + (i == 0 ? item.assignTo : 'tempRet' + (i-1)) + ';'
+ }
}
item.assignTo = null;
}
@@ -1098,7 +1179,12 @@ function JSify(data, functionsOnly, givenFunctions) {
});
makeFuncLineActor('landingpad', function(item) {
var catchTypeArray = item.catchables.map(finalizeLLVMParameter).join(',');
- return '___cxa_find_matching_catch('+ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') +',' + makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') + ',[' + catchTypeArray +'])';
+ var ret = '___cxa_find_matching_catch('+ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') +',' + makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') + ',[' + catchTypeArray +'])';
+ if (USE_TYPED_ARRAYS == 2) {
+ ret = makeVarDef(item.assignTo) + '$0 = ' + ret + '; ' + item.assignTo + '$1 = tempRet0;';
+ item.assignTo = null;
+ }
+ return ret;
});
makeFuncLineActor('load', function(item) {
var value = finalizeLLVMParameter(item.pointer);
@@ -1140,7 +1226,7 @@ function JSify(data, functionsOnly, givenFunctions) {
makeFuncLineActor('alloca', function(item) {
if (typeof item.allocatedIndex === 'number') {
if (item.allocatedSize === 0) return ''; // This will not actually be shown - it's nativized
- return getFastValue('__stackBase__', '+', item.allocatedIndex.toString());
+ return asmCoercion(getFastValue('__stackBase__', '+', item.allocatedIndex.toString()), 'i32');
} else {
return RuntimeGenerator.stackAlloc(getFastValue(calcAllocatedSize(item.allocatedType), '*', item.allocatedNum));
}
@@ -1163,7 +1249,16 @@ function JSify(data, functionsOnly, givenFunctions) {
// We cannot compile assembly. See comment in intertyper.js:'Call'
assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!');
- var shortident = LibraryManager.getRootIdent(ident.slice(1)) || ident.slice(1); // ident may not be in library, if all there is is ident__inline
+ var shortident = ident.slice(1);
+ var callIdent = LibraryManager.getRootIdent(shortident);
+ if (callIdent) {
+ shortident = callIdent; // ident may not be in library, if all there is is ident__inline, but in this case it is
+ if (callIdent.indexOf('.') < 0) {
+ callIdent = '_' + callIdent; // Not Math.*, so add the normal prefix
+ }
+ } else {
+ callIdent = ident;
+ }
var args = [];
var argsTypes = [];
var varargs = [];
@@ -1173,6 +1268,7 @@ function JSify(data, functionsOnly, givenFunctions) {
var useJSArgs = (shortident + '__jsargs') in LibraryManager.library;
var hasVarArgs = isVarArgsFunctionType(type);
var normalArgs = (hasVarArgs && !useJSArgs) ? countNormalArgs(type) : -1;
+ var byPointer = getVarData(funcData, ident);
params.forEach(function(param, i) {
var val = finalizeParam(param);
@@ -1196,6 +1292,10 @@ function JSify(data, functionsOnly, givenFunctions) {
});
args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) });
+ if (ASM_JS && shortident in Functions.libraryFunctions) {
+ args = args.map(function(arg, i) { return asmCoercion(arg, argsTypes[i]) });
+ }
+
varargs = varargs.map(function(vararg, i) {
if (ignoreFunctionIndexizing.indexOf(i) >= 0) return vararg;
return vararg === 0 ? 0 : indexizeFunctions(vararg, varargsTypes[i])
@@ -1226,6 +1326,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}).filter(function(arg) {
return arg !== null;
}).join(',') + ',tempInt)';
+ varargs = asmCoercion(varargs, 'i32');
}
args = args.concat(varargs);
@@ -1238,11 +1339,26 @@ function JSify(data, functionsOnly, givenFunctions) {
return inline.apply(null, args); // Warning: inlining does not prevent recalculation of the arguments. They should be simple identifiers
}
- if (getVarData(funcData, ident)) {
- ident = 'FUNCTION_TABLE[' + ident + ']';
+ var returnType;
+ if (byPointer || ASM_JS) returnType = type.split(' ')[0];
+
+ if (byPointer) {
+ var sig = Functions.getSignature(returnType, argsTypes);
+ if (ASM_JS) {
+ assert(returnType.search(/\("'\[,/) == -1); // XXX need isFunctionType(type, out)
+ callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py
+ }
+ callIdent = Functions.getTable(sig) + '[' + callIdent + ']';
}
- return ident + '(' + args.join(', ') + ')';
+ var ret = callIdent + '(' + args.join(', ') + ')';
+ if (ASM_JS) { // TODO: do only when needed (library functions and Math.*?) XXX && shortident in Functions.libraryFunctions) {
+ ret = asmCoercion(ret, returnType);
+ if (shortident == 'abort' && funcData.returnType != 'void') {
+ ret += '; return 0'; // special case: abort() can happen without return, breaking the return type of asm functions. ensure a return
+ }
+ }
+ return ret;
}
makeFuncLineActor('getelementptr', function(item) { return finalizeLLVMFunctionCall(item) });
makeFuncLineActor('call', function(item) {
@@ -1297,8 +1413,9 @@ function JSify(data, functionsOnly, givenFunctions) {
if (phase == 'pre' && !Variables.generatedGlobalBase) {
Variables.generatedGlobalBase = true;
if (Variables.nextIndexedOffset > 0) {
- // Variables have been calculated, print out the base generation before we print them
- print('var GLOBAL_BASE = STATICTOP;\n');
+ // Variables have been calculated, get to base stuff before we print them
+ // GLOBAL_BASE is statically known to be equal to STACK_MAX and to TOTAL_STACK, assert on this
+ print('assert(STATICTOP == STACK_MAX); assert(STACK_MAX == TOTAL_STACK);\n');
print('STATICTOP += ' + Variables.nextIndexedOffset + ';\n');
print('assert(STATICTOP < TOTAL_MEMORY);\n');
}
@@ -1343,8 +1460,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}
if (phase == 'pre' || phase == 'funcs') {
- // serialize out the data that later passes need
- PassManager.serialize(); // XXX for funcs pass, do not serialize it all. I think we just need which were indexized.
+ PassManager.serialize();
return;
}
@@ -1371,11 +1487,11 @@ function JSify(data, functionsOnly, givenFunctions) {
var postParts = processMacros(preprocess(read(postFile))).split('{{GLOBAL_VARS}}');
print(postParts[0]);
- print(Functions.generateIndexing()); // done last, as it may rely on aliases set in postsets
+ Functions.generateIndexing(); // done last, as it may rely on aliases set in postsets
// Load runtime-linked libraries
RUNTIME_LINKED_LIBS.forEach(function(lib) {
- print('eval(Module["read"]("' + lib + '"))(FUNCTION_TABLE.length, this);');
+ print('eval(Module["read"]("' + lib + '"))(' + Functions.getTable('x') + '.length, this);');
});
print(postParts[1]);
@@ -1387,6 +1503,8 @@ function JSify(data, functionsOnly, givenFunctions) {
return IGNORED_FUNCTIONS.indexOf(func.ident) < 0;
})) + '\n');
+ PassManager.serialize();
+
return null;
}
diff --git a/src/library.js b/src/library.js
index 0b59b404..be57e445 100644
--- a/src/library.js
+++ b/src/library.js
@@ -20,10 +20,11 @@ LibraryManager.library = {
// File system base.
// ==========================================================================
- stdin: 0,
- stdout: 0,
- stderr: 0,
- _impure_ptr: 0,
+ // keep this low in memory, because we flatten arrays with them in them
+ stdin: 'allocate(1, "i32*", ALLOC_STACK)',
+ stdout: 'allocate(1, "i32*", ALLOC_STACK)',
+ stderr: 'allocate(1, "i32*", ALLOC_STACK)',
+ _impure_ptr: 'allocate(1, "i32*", ALLOC_STACK)',
$FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'],
$FS__postset: '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' +
@@ -572,10 +573,10 @@ LibraryManager.library = {
eof: false,
ungotten: []
};
- // Allocate these on the stack (and never free, we are called from ATINIT or earlier), to keep their locations low
- _stdin = allocate([1], 'void*', ALLOC_STACK);
- _stdout = allocate([2], 'void*', ALLOC_STACK);
- _stderr = allocate([3], 'void*', ALLOC_STACK);
+ assert(Math.max(_stdin, _stdout, _stderr) < 128); // make sure these are low, we flatten arrays with these
+ {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 1, 'void*') }}};
+ {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 2, 'void*') }}};
+ {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 3, 'void*') }}};
// Other system paths
FS.createPath('/', 'dev/shm/tmp', true, true); // temp files
@@ -591,9 +592,9 @@ LibraryManager.library = {
FS.checkStreams();
assert(FS.streams.length < 1024); // at this early stage, we should not have a large set of file descriptors - just a few
#endif
- __impure_ptr = allocate([ allocate(
+ allocate([ allocate(
{{{ Runtime.QUANTUM_SIZE === 4 ? '[0, 0, 0, 0, _stdin, 0, 0, 0, _stdout, 0, 0, 0, _stderr, 0, 0, 0]' : '[0, _stdin, _stdout, _stderr]' }}},
- 'void*', ALLOC_STATIC) ], 'void*', ALLOC_STATIC);
+ 'void*', ALLOC_STATIC) ], 'void*', ALLOC_NONE, {{{ makeGlobalUse('__impure_ptr') }}});
},
quit: function() {
@@ -1226,6 +1227,15 @@ LibraryManager.library = {
// http://pubs.opengroup.org/onlinepubs/009695399/functions/creat.html
return _open(path, {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_TRUNC') }}}, allocate([mode, 0, 0, 0], 'i32', ALLOC_STACK));
},
+ mkstemp__deps: ['creat'],
+ mkstemp: function(template) {
+ if (!_mkstemp.counter) _mkstemp.counter = 0;
+ var c = (_mkstemp.counter++).toString();
+ var rep = 'XXXXXX';
+ while (c.length < rep.length) c = '0' + c;
+ writeArrayToMemory(intArrayFromString(c), template + Pointer_stringify(template).indexOf(rep));
+ return _creat(template, 0600);
+ },
fcntl__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__flock_struct_layout'],
fcntl: function(fildes, cmd, varargs, dup2) {
// int fcntl(int fildes, int cmd, ...);
@@ -3068,7 +3078,7 @@ LibraryManager.library = {
getchar: function() {
// int getchar(void);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/getchar.html
- return _fgetc({{{ makeGetValue('_stdin', '0', 'void*') }}});
+ return _fgetc({{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}});
},
fgetpos__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'],
fgetpos: function(stream, pos) {
@@ -3113,7 +3123,7 @@ LibraryManager.library = {
gets: function(s) {
// char *gets(char *s);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/gets.html
- return _fgets(s, 1e6, {{{ makeGetValue('_stdin', '0', 'void*') }}});
+ return _fgets(s, 1e6, {{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}});
},
fileno: function(stream) {
// int fileno(FILE *stream);
@@ -3185,7 +3195,7 @@ LibraryManager.library = {
putchar: function(c) {
// int putchar(int c);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/putchar.html
- return _fputc(c, {{{ makeGetValue('_stdout', '0', 'void*') }}});
+ return _fputc(c, {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}});
},
putchar_unlocked: 'putchar',
fputs__deps: ['write', 'strlen'],
@@ -3199,7 +3209,7 @@ LibraryManager.library = {
// int puts(const char *s);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/puts.html
// NOTE: puts() always writes an extra newline.
- var stdout = {{{ makeGetValue('_stdout', '0', 'void*') }}};
+ var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}};
var ret = _fputs(s, stdout);
if (ret < 0) {
return ret;
@@ -3467,7 +3477,7 @@ LibraryManager.library = {
scanf: function(format, varargs) {
// int scanf(const char *restrict format, ... );
// http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html
- var stdin = {{{ makeGetValue('_stdin', '0', 'void*') }}};
+ var stdin = {{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}};
return _fscanf(stdin, format, varargs);
},
sscanf__deps: ['_scanString'],
@@ -3506,7 +3516,7 @@ LibraryManager.library = {
printf: function(format, varargs) {
// int printf(const char *restrict format, ...);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html
- var stdout = {{{ makeGetValue('_stdout', '0', 'void*') }}};
+ var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}};
return _fprintf(stdout, format, varargs);
},
sprintf__deps: ['snprintf'],
@@ -3535,7 +3545,7 @@ LibraryManager.library = {
_ZNSo3putEc: 'putchar',
_ZNSo5flushEv__deps: ['fflush', 'stdout'],
_ZNSo5flushEv: function() {
- _fflush({{{ makeGetValue('_stdout', '0', 'void*') }}});
+ _fflush({{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}});
},
// ==========================================================================
@@ -3593,6 +3603,10 @@ LibraryManager.library = {
// stdlib.h
// ==========================================================================
+ // tiny, fake malloc/free implementation. If the program actually uses malloc,
+ // a compiled version will be used; this will only be used if the runtime
+ // needs to allocate something, for which this is good enough if otherwise
+ // no malloc is needed.
malloc: function(bytes) {
/* Over-allocate to make sure it is byte-aligned by 8.
* This will leak memory, but this is only the dummy
@@ -3602,14 +3616,7 @@ LibraryManager.library = {
ptr = Runtime.staticAlloc(bytes + 8);
return (ptr+8) & 0xFFFFFFF8;
},
- _Znwj: 'malloc',
- _Znaj: 'malloc',
- _Znam: 'malloc',
- _Znwm: 'malloc',
-
free: function(){},
- _ZdlPv: 'free',
- _ZdaPv: 'free',
calloc__deps: ['malloc'],
calloc: function(n, s) {
@@ -3637,7 +3644,9 @@ LibraryManager.library = {
},
bsearch: function(key, base, num, size, compar) {
- var cmp = FUNCTION_TABLE[compar];
+ var cmp = function(x, y) {
+ return Runtime.dynCall('iii', compar, [x, y])
+ };
var left = 0;
var right = num;
var mid, test, addr;
@@ -3831,7 +3840,7 @@ LibraryManager.library = {
#if USE_TYPED_ARRAYS == 2
if (bits == 64) {
- ret = [{{{ splitI64('ret') }}}];
+ {{{ makeStructuralReturn(splitI64('ret')) }}};
}
#endif
@@ -3882,13 +3891,7 @@ LibraryManager.library = {
}
if (!ok) {
___setErrNo(ERRNO_CODES.EINVAL);
- return [0, 0];
- }
-
- try {
- i64Math.fromString(Pointer_stringify(start, str - start), finalBase, min, max, unsign);
- } catch(e) {
- ___setErrNo(ERRNO_CODES.ERANGE); // not quite correct
+ {{{ makeStructuralReturn(['0', '0']) }}};
}
// Set end pointer.
@@ -3896,9 +3899,13 @@ LibraryManager.library = {
{{{ makeSetValue('endptr', 0, 'str', '*') }}}
}
- var ret = i64Math.result.slice(0);
+ try {
+ i64Math.fromString(Pointer_stringify(start, str - start), finalBase, min, max, unsign);
+ } catch(e) {
+ ___setErrNo(ERRNO_CODES.ERANGE); // not quite correct
+ }
- return ret;
+ {{{ makeStructuralReturn([makeGetTempDouble(0), makeGetTempDouble(1)]) }}};
},
#endif
strtoll__deps: ['_parseInt64'],
@@ -3939,11 +3946,13 @@ LibraryManager.library = {
},
qsort__deps: ['memcpy'],
- qsort: function(base, num, size, comparator) {
+ qsort: function(base, num, size, cmp) {
if (num == 0 || size == 0) return;
// forward calls to the JavaScript sort method
// first, sort the items logically
- comparator = FUNCTION_TABLE[comparator];
+ var comparator = function(x, y) {
+ return Runtime.dynCall('iii', cmp, [x, y]);
+ }
var keys = [];
for (var i = 0; i < num; i++) keys.push(i);
keys.sort(function(a, b) {
@@ -3959,9 +3968,10 @@ LibraryManager.library = {
_free(temp);
},
- environ: null,
- __environ: null,
- __buildEnvironment__deps: ['environ', '__environ'],
+ environ: 'allocate(1, "i32*", ALLOC_STACK)',
+ __environ__deps: ['environ'],
+ __environ: '_environ',
+ __buildEnvironment__deps: ['__environ'],
__buildEnvironment: function(env) {
// WARNING: Arbitrary limit!
var MAX_ENV_VALUES = 64;
@@ -3970,7 +3980,8 @@ LibraryManager.library = {
// Statically allocate memory for the environment.
var poolPtr;
var envPtr;
- if (_environ === null) {
+ if (!___buildEnvironment.called) {
+ ___buildEnvironment.called = true;
// Set default values. Use string keys for Closure Compiler compatibility.
ENV['USER'] = 'root';
ENV['PATH'] = '/';
@@ -3983,11 +3994,9 @@ LibraryManager.library = {
envPtr = allocate(MAX_ENV_VALUES * {{{ Runtime.QUANTUM_SIZE }}},
'i8*', ALLOC_STATIC);
{{{ makeSetValue('envPtr', '0', 'poolPtr', 'i8*') }}}
- _environ = allocate([envPtr], 'i8**', ALLOC_STATIC);
- // Set up global variable alias.
- ___environ = _environ;
+ {{{ makeSetValue(makeGlobalUse('_environ'), 0, 'envPtr', 'i8*') }}};
} else {
- envPtr = {{{ makeGetValue('_environ', '0', 'i8**') }}};
+ envPtr = {{{ makeGetValue(makeGlobalUse('_environ'), '0', 'i8**') }}};
poolPtr = {{{ makeGetValue('envPtr', '0', 'i8*') }}};
}
@@ -4182,55 +4191,22 @@ LibraryManager.library = {
memcpy__inline: function (dest, src, num, align) {
var ret = '';
#if ASSERTIONS
- ret += "assert(" + num + " % 1 === 0, 'memcpy given ' + " + num + " + ' bytes to copy. Problem with quantum=1 corrections perhaps?');";
+ ret += "assert(" + num + " % 1 === 0);"; //, 'memcpy given ' + " + num + " + ' bytes to copy. Problem with quantum=1 corrections perhaps?');";
#endif
ret += makeCopyValues(dest, src, num, 'null', null, align);
return ret;
},
- memcpy: function (dest, src, num, align) {
-#if ASSERTIONS
- assert(num % 1 === 0, 'memcpy given ' + num + ' bytes to copy. Problem with quantum=1 corrections perhaps?');
-#endif
-#if USE_TYPED_ARRAYS == 2
- if (num >= {{{ SEEK_OPTIMAL_ALIGN_MIN }}} && src % 2 == dest % 2) {
- // This is unaligned, but quite large, and potentially alignable, so work hard to get to aligned settings
- if (src % 4 == dest % 4) {
- var stop = src + num;
- while (src % 4) { // no need to check for stop, since we have large num
- HEAP8[dest++] = HEAP8[src++];
- }
- var src4 = src >> 2, dest4 = dest >> 2, stop4 = stop >> 2;
- while (src4 < stop4) {
- HEAP32[dest4++] = HEAP32[src4++];
- }
- src = src4 << 2;
- dest = dest4 << 2;
- while (src < stop) {
- HEAP8[dest++] = HEAP8[src++];
- }
- } else {
- var stop = src + num;
- if (src % 2) { // no need to check for stop, since we have large num
- HEAP8[dest++] = HEAP8[src++];
- }
- var src2 = src >> 1, dest2 = dest >> 1, stop2 = stop >> 1;
- while (src2 < stop2) {
- HEAP16[dest2++] = HEAP16[src2++];
- }
- src = src2 << 1;
- dest = dest2 << 1;
- if (src < stop) {
- HEAP8[dest++] = HEAP8[src++];
- }
- }
- } else {
- while (num--) {
- HEAP8[dest++] = HEAP8[src++];
- }
+
+ memcpy: function (dest, src, num) {
+ // simple version, in general it should not be used - we should pull it in from libc
+ if (!_memcpy.shown) {
+ _memcpy.shown = true;
+ Module.printErr('warning: library.js memcpy should not be running, it is only for testing!');
}
-#else
- {{{ makeCopyValues('dest', 'src', 'num', 'null', null, 'align') }}};
#endif
+ while (num--) {
+ HEAP8[dest++] = HEAP8[src++];
+ }
},
llvm_memcpy_i32: 'memcpy',
@@ -4250,7 +4226,7 @@ LibraryManager.library = {
{{{ makeCopyValues('dest', 'src', 1, 'null', null, 1) }}};
}
} else {
- _memcpy(dest, src, num, align);
+ _memcpy(dest, src, num);
}
},
llvm_memmove_i32: 'memmove',
@@ -4504,6 +4480,7 @@ LibraryManager.library = {
} while (val);
return 0;
},
+ index: 'strchr',
strrchr__deps: ['strlen'],
strrchr: function(ptr, chr) {
@@ -4514,6 +4491,7 @@ LibraryManager.library = {
} while (ptr2 >= ptr);
return 0;
},
+ rindex: 'strrchr',
strdup: function(ptr) {
var len = String_len(ptr);
@@ -4801,9 +4779,8 @@ LibraryManager.library = {
// ==========================================================================
llvm_va_start__inline: function(ptr) {
- // varargs - we received a pointer to the varargs as a final 'extra' parameter
- var data = 'arguments[' + Framework.currItem.funcData.ident + '.length]';
- return makeSetValue(ptr, 0, data, 'void*');
+ // varargs - we received a pointer to the varargs as a final 'extra' parameter called 'varrp'
+ return makeSetValue(ptr, 0, 'varrp', 'void*');
},
llvm_va_end: function() {},
@@ -4832,7 +4809,7 @@ LibraryManager.library = {
var retl = _llvm_bswap_i32(h)>>>0;
var reth = _llvm_bswap_i32(l)>>>0;
#if USE_TYPED_ARRAYS == 2
- return [retl, reth];
+ {{{ makeStructuralReturn(['retl', 'reth']) }}};
#else
throw 'unsupported';
#endif
@@ -4852,7 +4829,7 @@ LibraryManager.library = {
var ret = _llvm_ctlz_i32(h);
if (ret == 32) ret += _llvm_ctlz_i32(l);
#if USE_TYPED_ARRAYS == 2
- return [ret, 0];
+ {{{ makeStructuralReturn(['ret', '0']) }}};
#else
return ret;
#endif
@@ -4896,13 +4873,13 @@ LibraryManager.library = {
__cxa_throw: function(ptr, type, destructor) {
if (!___cxa_throw.initialized) {
try {
- {{{ makeSetValue('__ZTVN10__cxxabiv119__pointer_type_infoE', '0', '0', 'i32') }}}; // Workaround for libcxxabi integration bug
+ {{{ makeSetValue(makeGlobalUse('__ZTVN10__cxxabiv119__pointer_type_infoE'), '0', '0', 'i32') }}}; // Workaround for libcxxabi integration bug
} catch(e){}
try {
- {{{ makeSetValue('__ZTVN10__cxxabiv117__class_type_infoE', '0', '1', 'i32') }}}; // Workaround for libcxxabi integration bug
+ {{{ makeSetValue(makeGlobalUse('__ZTVN10__cxxabiv117__class_type_infoE'), '0', '1', 'i32') }}}; // Workaround for libcxxabi integration bug
} catch(e){}
try {
- {{{ makeSetValue('__ZTVN10__cxxabiv120__si_class_type_infoE', '0', '2', 'i32') }}}; // Workaround for libcxxabi integration bug
+ {{{ makeSetValue(makeGlobalUse('__ZTVN10__cxxabiv120__si_class_type_infoE'), '0', '2', 'i32') }}}; // Workaround for libcxxabi integration bug
} catch(e){}
___cxa_throw.initialized = true;
}
@@ -4951,14 +4928,18 @@ LibraryManager.library = {
return;
}
// Clear state flag.
- __THREW__ = false;
+#if ASM_JS
+ asm.setThrew(0);
+#else
+ __THREW__ = 0;
+#endif
// Clear type.
{{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, '0', 'void*') }}}
// Call destructor if one is registered then clear it.
var ptr = {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}};
var destructor = {{{ makeGetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, 'void*') }}};
if (destructor) {
- FUNCTION_TABLE[destructor](ptr);
+ Runtime.dynCall('vi', destructor, [ptr]);
{{{ makeSetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, '0', 'i32') }}}
}
// Free ptr if it isn't null.
@@ -4996,20 +4977,20 @@ LibraryManager.library = {
__cxa_is_number_type: function(type) {
var isNumber = false;
- try { if (type == __ZTIi) isNumber = true } catch(e){}
- try { if (type == __ZTIj) isNumber = true } catch(e){}
- try { if (type == __ZTIl) isNumber = true } catch(e){}
- try { if (type == __ZTIm) isNumber = true } catch(e){}
- try { if (type == __ZTIx) isNumber = true } catch(e){}
- try { if (type == __ZTIy) isNumber = true } catch(e){}
- try { if (type == __ZTIf) isNumber = true } catch(e){}
- try { if (type == __ZTId) isNumber = true } catch(e){}
- try { if (type == __ZTIe) isNumber = true } catch(e){}
- try { if (type == __ZTIc) isNumber = true } catch(e){}
- try { if (type == __ZTIa) isNumber = true } catch(e){}
- try { if (type == __ZTIh) isNumber = true } catch(e){}
- try { if (type == __ZTIs) isNumber = true } catch(e){}
- try { if (type == __ZTIt) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIi') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIj') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIl') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIm') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIx') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIy') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIf') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTId') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIe') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIc') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIa') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIh') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIs') }}}) isNumber = true } catch(e){}
+ try { if (type == {{{ makeGlobalUse('__ZTIt') }}}) isNumber = true } catch(e){}
return isNumber;
},
@@ -5040,12 +5021,12 @@ LibraryManager.library = {
// return the type of the catch block which should be called.
for (var i = 0; i < typeArray.length; i++) {
if (___cxa_does_inherit(typeArray[i], throwntype, thrown))
- return { f0:thrown, f1:typeArray[i] };
+ {{{ makeStructuralReturn(['thrown', 'typeArray[i]']) }}};
}
// Shouldn't happen unless we have bogus data in typeArray
// or encounter a type for which emscripten doesn't have suitable
// typeinfo defined. Best-efforts match just in case.
- return { f0:thrown, f1 :throwntype };
+ {{{ makeStructuralReturn(['thrown', 'throwntype']) }}};
},
// Recursively walks up the base types of 'possibilityType'
@@ -5087,6 +5068,8 @@ LibraryManager.library = {
}
},
+ _ZNSt9exceptionD2Ev: function(){}, // XXX a dependency of dlmalloc, but not actually needed if libcxx is not anyhow included
+
// RTTI hacks for exception handling, defining type_infos for common types.
// The values are dummies. We simply use the addresses of these statically
// allocated variables as unique identifiers.
@@ -5111,73 +5094,51 @@ LibraryManager.library = {
llvm_uadd_with_overflow_i8: function(x, y) {
x = x & 0xff;
y = y & 0xff;
- return {
- f0: (x+y) & 0xff,
- f1: x+y > 255
- };
+ {{{ makeStructuralReturn(['(x+y) & 0xff', 'x+y > 255']) }}};
},
llvm_umul_with_overflow_i8: function(x, y) {
x = x & 0xff;
y = y & 0xff;
- return {
- f0: (x*y) & 0xff,
- f1: x*y > 255
- };
+ {{{ makeStructuralReturn(['(x*y) & 0xff', 'x*y > 255']) }}};
},
llvm_uadd_with_overflow_i16: function(x, y) {
x = x & 0xffff;
y = y & 0xffff;
- return {
- f0: (x+y) & 0xffff,
- f1: x+y > 65535
- };
+ {{{ makeStructuralReturn(['(x+y) & 0xffff', 'x+y > 65535']) }}};
},
llvm_umul_with_overflow_i16: function(x, y) {
x = x & 0xffff;
y = y & 0xffff;
- return {
- f0: (x*y) & 0xffff,
- f1: x*y > 65535
- };
+ {{{ makeStructuralReturn(['(x*y) & 0xffff', 'x*y > 65535']) }}};
},
llvm_uadd_with_overflow_i32: function(x, y) {
x = x>>>0;
y = y>>>0;
- return {
- f0: (x+y)>>>0,
- f1: x+y > 4294967295
- };
+ {{{ makeStructuralReturn(['(x+y)>>>0', 'x+y > 4294967295']) }}};
},
llvm_umul_with_overflow_i32: function(x, y) {
x = x>>>0;
y = y>>>0;
- return {
- f0: (x*y)>>>0,
- f1: x*y > 4294967295
- };
+ {{{ makeStructuralReturn(['(x*y)>>>0', 'x*y > 4294967295']) }}};
},
llvm_uadd_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }],
llvm_uadd_with_overflow_i64: function(xl, xh, yl, yh) {
i64Math.add(xl, xh, yl, yh);
- return {
- f0: i64Math.result,
- f1: 0 // XXX Need to hack support for this in long.js
- };
+ {{{ makeStructuralReturn(['HEAP32[tempDoublePtr>>2]', 'HEAP32[tempDoublePtr+4>>2]', '0']) }}};
+ // XXX Need to hack support for second param in long.js
},
llvm_umul_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }],
llvm_umul_with_overflow_i64: function(xl, xh, yl, yh) {
- i64Math.mul(xl, xh, yl, yh);
- return {
- f0: i64Math.result,
- f1: 0 // XXX Need to hack support for this in long.js
- };
+ i64Math.multiply(xl, xh, yl, yh);
+ {{{ makeStructuralReturn(['HEAP32[tempDoublePtr>>2]', 'HEAP32[tempDoublePtr+4>>2]', '0']) }}};
+ // XXX Need to hack support for second param in long.js
},
llvm_stacksave: function() {
@@ -5617,7 +5578,7 @@ LibraryManager.library = {
}
try {
- var lib_module = eval(lib_data)(FUNCTION_TABLE.length);
+ var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length);
} catch (e) {
#if ASSERTIONS
Module.printErr('Error in loading dynamic library: ' + e);
@@ -5690,9 +5651,9 @@ LibraryManager.library = {
} else {
var result = lib.module[symbol];
if (typeof result == 'function') {
- FUNCTION_TABLE.push(result);
- FUNCTION_TABLE.push(0);
- result = FUNCTION_TABLE.length - 2;
+ {{{ Functions.getTable('x') }}}.push(result);
+ {{{ Functions.getTable('x') }}}.push(0);
+ result = {{{ Functions.getTable('x') }}}.length - 2;
lib.cached_functions = result;
}
return result;
@@ -5760,11 +5721,11 @@ LibraryManager.library = {
'tm_gmtoff',
'tm_zone'], '%struct.tm'),
// Statically allocated time struct.
- __tm_current: 0,
+ __tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STACK)',
// Statically allocated timezone strings.
__tm_timezones: {},
// Statically allocated time strings.
- __tm_formatted: 0,
+ __tm_formatted: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STACK)',
mktime__deps: ['__tm_struct_layout', 'tzset'],
mktime: function(tmPtr) {
@@ -5787,7 +5748,6 @@ LibraryManager.library = {
gmtime__deps: ['malloc', '__tm_struct_layout', '__tm_current', 'gmtime_r'],
gmtime: function(time) {
- if (!___tm_current) ___tm_current = _malloc(___tm_struct_layout.__size__);
return _gmtime_r(time, ___tm_current);
},
@@ -5821,8 +5781,8 @@ LibraryManager.library = {
timegm__deps: ['mktime'],
timegm: function(tmPtr) {
_tzset();
- var offset = {{{ makeGetValue('__timezone', 0, 'i32') }}};
- var daylight = {{{ makeGetValue('__daylight', 0, 'i32') }}};
+ var offset = {{{ makeGetValue(makeGlobalUse('__timezone'), 0, 'i32') }}};
+ var daylight = {{{ makeGetValue(makeGlobalUse('__daylight'), 0, 'i32') }}};
daylight = (daylight == 1) ? 60 * 60 : 0;
var ret = _mktime(tmPtr) + offset - daylight;
return ret;
@@ -5830,7 +5790,6 @@ LibraryManager.library = {
localtime__deps: ['malloc', '__tm_struct_layout', '__tm_current', 'localtime_r'],
localtime: function(time) {
- if (!___tm_current) ___tm_current = _malloc(___tm_struct_layout.__size__);
return _localtime_r(time, ___tm_current);
},
@@ -5866,7 +5825,6 @@ LibraryManager.library = {
asctime__deps: ['malloc', '__tm_formatted', 'asctime_r'],
asctime: function(tmPtr) {
- if (!___tm_formatted) ___tm_formatted = _malloc(26);
return _asctime_r(tmPtr, ___tm_formatted);
},
@@ -5901,29 +5859,27 @@ LibraryManager.library = {
// TODO: Initialize these to defaults on startup from system settings.
// Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm)
- _tzname: null,
- _daylight: null,
- _timezone: null,
+ _tzname: 'allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STACK)',
+ _daylight: 'allocate(1, "i32*", ALLOC_STACK)',
+ _timezone: 'allocate(1, "i32*", ALLOC_STACK)',
tzset__deps: ['_tzname', '_daylight', '_timezone'],
tzset: function() {
// TODO: Use (malleable) environment variables instead of system settings.
- if (__tzname) return; // glibc does not need the double __
+ if (_tzset.called) return;
+ _tzset.called = true;
- __timezone = _malloc({{{ Runtime.QUANTUM_SIZE }}});
- {{{ makeSetValue('__timezone', '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}}
+ {{{ makeSetValue(makeGlobalUse('__timezone'), '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}}
- __daylight = _malloc({{{ Runtime.QUANTUM_SIZE }}});
var winter = new Date(2000, 0, 1);
var summer = new Date(2000, 6, 1);
- {{{ makeSetValue('__daylight', '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}}
+ {{{ makeSetValue(makeGlobalUse('__daylight'), '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}}
var winterName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | winter.toString().match(/\(([A-Z]+)\)/)[1];
var summerName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | summer.toString().match(/\(([A-Z]+)\)/)[1];
var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL);
var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL);
- __tzname = _malloc(2 * {{{ Runtime.QUANTUM_SIZE }}}); // glibc does not need the double __
- {{{ makeSetValue('__tzname', '0', 'winterNamePtr', 'i32') }}}
- {{{ makeSetValue('__tzname', Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}}
+ {{{ makeSetValue(makeGlobalUse('__tzname'), '0', 'winterNamePtr', 'i32') }}}
+ {{{ makeSetValue(makeGlobalUse('__tzname'), Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}}
},
stime__deps: ['$ERRNO_CODES', '__setErrNo'],
@@ -6084,18 +6040,26 @@ LibraryManager.library = {
},
sigemptyset: function(set) {
// int sigemptyset(sigset_t *set);
- // TODO: Implement for real; don't hardcode offsets.
- {{{ makeSetValue('set', '0', '0', 'i32') }}}
- {{{ makeSetValue('set', '4', '0', 'i32') }}}
- {{{ makeSetValue('set', '8', '0', 'i32') }}}
- {{{ makeSetValue('set', '12', '0', 'i32') }}}
+ {{{ makeSetValue('set', '0', '0', 'i32') }}};
+ return 0;
+ },
+ sigfillset: function(set) {
+ {{{ makeSetValue('set', '0', '-1>>>0', 'i32') }}};
+ return 0;
+ },
+ sigaddset: function(set, signum) {
+ {{{ makeSetValue('set', '0', makeGetValue('set', '0', 'i32') + '| (1 << (signum-1))', 'i32') }}};
+ return 0;
+ },
+ sigdelset: function(set, signum) {
+ {{{ makeSetValue('set', '0', makeGetValue('set', '0', 'i32') + '& (~(1 << (signum-1)))', 'i32') }}};
return 0;
},
- sigfillset: 'sigemptyset',
- sigdelset: 'sigemptyset',
+ sigismember: function(set, signum) {
+ return {{{ makeGetValue('set', '0', 'i32') }}} & (1 << (signum-1));
+ },
sigaction: function(set) {
- // int sigemptyset(sigset_t *set);
- // TODO: Implement for real.
+ // TODO:
return 0;
},
sigprocmask: 'sigaction',
@@ -6141,6 +6105,8 @@ LibraryManager.library = {
return 0;
},
+ freelocale: function(locale) {},
+
uselocale: function(locale) {
return 0;
},
@@ -6580,6 +6546,8 @@ LibraryManager.library = {
pthread_mutexattr_destroy: function() {},
pthread_mutex_lock: function() {},
pthread_mutex_unlock: function() {},
+ pthread_cond_init: function() {},
+ pthread_cond_destroy: function() {},
pthread_cond_broadcast: function() {},
pthread_self: function() {
//FIXME: assumes only a single thread
@@ -6613,7 +6581,7 @@ LibraryManager.library = {
pthread_once: function(ptr, func) {
if (!_pthread_once.seen) _pthread_once.seen = {};
if (ptr in _pthread_once.seen) return;
- FUNCTION_TABLE[func]();
+ Runtime.dynCall('v', func);
_pthread_once.seen[ptr] = 1;
},
@@ -6631,7 +6599,7 @@ LibraryManager.library = {
},
pthread_cleanup_push: function(routine, arg) {
- __ATEXIT__.push({ func: function() { FUNCTION_TABLE[routine](arg) } })
+ __ATEXIT__.push({ func: function() { Runtime.dynCall('vi', routine, [arg]) } })
_pthread_cleanup_push.level = __ATEXIT__.length;
},
@@ -6777,8 +6745,12 @@ LibraryManager.library = {
$Sockets__deps: ['__setErrNo', '$ERRNO_CODES'],
$Sockets: {
+ BACKEND_WEBSOCKETS: 0,
+ BACKEND_WEBRTC: 1,
BUFFER_SIZE: 10*1024, // initial size
MAX_BUFFER_SIZE: 10*1024*1024, // maximum size we will grow the buffer
+
+ backend: 0, // default to websockets
nextFd: 1,
fds: {},
sockaddr_in_layout: Runtime.generateStructInfo([
@@ -6796,24 +6768,138 @@ LibraryManager.library = {
['i32', 'msg_controllen'],
['i32', 'msg_flags'],
]),
+
+ backends: {
+ 0: { // websockets
+ connect: function(info) {
+ console.log('opening ws://' + info.host + ':' + info.port);
+ info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']);
+ info.socket.binaryType = 'arraybuffer';
+
+ var i32Temp = new Uint32Array(1);
+ var i8Temp = new Uint8Array(i32Temp.buffer);
+
+ info.inQueue = [];
+ if (!info.stream) {
+ var partialBuffer = null; // inQueue contains full dgram messages; this buffers incomplete data. Must begin with the beginning of a message
+ }
+
+ info.socket.onmessage = function(event) {
+ assert(typeof event.data !== 'string' && event.data.byteLength); // must get binary data!
+ var data = new Uint8Array(event.data); // make a typed array view on the array buffer
+#if SOCKET_DEBUG
+ Module.print(['onmessage', data.length, '|', Array.prototype.slice.call(data)]);
+#endif
+ if (info.stream) {
+ info.inQueue.push(data);
+ } else {
+ // we added headers with message sizes, read those to find discrete messages
+ if (partialBuffer) {
+ // append to the partial buffer
+ var newBuffer = new Uint8Array(partialBuffer.length + data.length);
+ newBuffer.set(partialBuffer);
+ newBuffer.set(data, partialBuffer.length);
+ // forget the partial buffer and work on data
+ data = newBuffer;
+ partialBuffer = null;
+ }
+ var currPos = 0;
+ while (currPos+4 < data.length) {
+ i8Temp.set(data.subarray(currPos, currPos+4));
+ var currLen = i32Temp[0];
+ assert(currLen > 0);
+ if (currPos + 4 + currLen > data.length) {
+ break; // not enough data has arrived
+ }
+ currPos += 4;
+#if SOCKET_DEBUG
+ Module.print(['onmessage message', currLen, '|', Array.prototype.slice.call(data.subarray(currPos, currPos+currLen))]);
+#endif
+ info.inQueue.push(data.subarray(currPos, currPos+currLen));
+ currPos += currLen;
+ }
+ // If data remains, buffer it
+ if (currPos < data.length) {
+ partialBuffer = data.subarray(currPos);
+ }
+ }
+ }
+ function send(data) {
+ // TODO: if browser accepts views, can optimize this
+#if SOCKET_DEBUG
+ Module.print('sender actually sending ' + Array.prototype.slice.call(data));
+#endif
+ // ok to use the underlying buffer, we created data and know that the buffer starts at the beginning
+ info.socket.send(data.buffer);
+ }
+ var outQueue = [];
+ var intervalling = false, interval;
+ function trySend() {
+ if (info.socket.readyState != info.socket.OPEN) {
+ if (!intervalling) {
+ intervalling = true;
+ console.log('waiting for socket in order to send');
+ interval = setInterval(trySend, 100);
+ }
+ return;
+ }
+ for (var i = 0; i < outQueue.length; i++) {
+ send(outQueue[i]);
+ }
+ outQueue.length = 0;
+ if (intervalling) {
+ intervalling = false;
+ clearInterval(interval);
+ }
+ }
+ info.sender = function(data) {
+ if (!info.stream) {
+ // add a header with the message size
+ var header = new Uint8Array(4);
+ i32Temp[0] = data.length;
+ header.set(i8Temp);
+ outQueue.push(header);
+ }
+ outQueue.push(new Uint8Array(data));
+ trySend();
+ };
+ }
+ },
+ 1: { // webrtc
+ }
+ }
+ },
+
+ emscripten_set_network_backend__deps: ['$Sockets'],
+ emscripten_set_network_backend: function(backend) {
+ Sockets.backend = backend;
},
socket__deps: ['$Sockets'],
socket: function(family, type, protocol) {
var fd = Sockets.nextFd++;
+ assert(fd < 64); // select() assumes socket fd values are in 0..63
+ var stream = type == {{{ cDefine('SOCK_STREAM') }}};
+ if (protocol) {
+ assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if stream, must be tcp
+ }
+ if (Sockets.backend == Sockets.BACKEND_WEBRTC) {
+ assert(!stream); // If WebRTC, we can only support datagram, not stream
+ }
Sockets.fds[fd] = {
- connected: false
+ connected: false,
+ stream: stream
};
return fd;
},
- connect__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs', 'gethostbyname'],
+ connect__deps: ['$Sockets', '_inet_ntop_raw', 'htons', 'gethostbyname'],
connect: function(fd, addr, addrlen) {
var info = Sockets.fds[fd];
if (!info) return -1;
info.connected = true;
info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32');
- info.port = _ntohs(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16'));
+ info.port = _htons(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16'));
info.host = __inet_ntop_raw(info.addr);
// Support 'fake' ips from gethostbyname
var parts = info.host.split('.');
@@ -6823,69 +6909,7 @@ LibraryManager.library = {
info.host = _gethostbyname.table[low + 0xff*high];
assert(info.host, 'problem translating fake ip ' + parts);
}
- console.log('opening ws://' + info.host + ':' + info.port);
- info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']);
- info.socket.binaryType = 'arraybuffer';
- info.buffer = new Uint8Array(Sockets.BUFFER_SIZE);
- info.bufferWrite = info.bufferRead = 0;
- info.socket.onmessage = function (event) {
- assert(typeof event.data !== 'string' && event.data.byteLength); // must get binary data!
- var data = new Uint8Array(event.data); // make a typed array view on the array buffer
- var len = data.length;
-#if SOCKET_DEBUG
- Module.print(['onmessage', window.location, data, len, '|', Array.prototype.slice.call(data)]);
-#endif
- for (var i = 0; i < len; i++) { // TODO: typed array set, carefully with ranges, or other trick
- info.buffer[info.bufferWrite++] = data[i];
- if (info.bufferWrite == info.buffer.length) info.bufferWrite = 0;
- if (info.bufferWrite == info.bufferRead) {
- // grow the buffer
- var currLen = info.buffer.length;
- if (currLen > Sockets.MAX_BUFFER_SIZE) throw 'socket buffer overflow';
- var newBuffer = new Uint8Array(currLen*2);
- for (var j = 0; j < currLen; j++) {
- newBuffer[j] = info.buffer[(info.bufferRead + j)%currLen];
- }
- info.bufferRead = 0;
- info.bufferWrite = currLen;
- info.buffer = newBuffer;
- }
- }
- }
- info.sendQueue = new Uint8Array(1024);
- info.sendQueueUsed = 0;
- info.senderWaiting = false;
- info.sender = function(data, justQueue) {
- if (data) {
-#if SOCKET_DEBUG
- Module.print(['sender', data, data.length, '|', Array.prototype.slice.call(data)]);
-#endif
- if (info.sendQueueUsed + data.length >= info.sendQueue.length) {
- var newQueue = new Uint8Array(2*Math.max(info.sendQueue.length, data.length));
- newQueue.set(info.sendQueue);
- info.sendQueue = newQueue;
- }
- info.sendQueue.set(data, info.sendQueueUsed); // must copy, because while this waits memory can change!
- info.sendQueueUsed += data.length;
- } else {
- info.senderWaiting = false; // we are a setTimeout callback
- if (info.sendQueueUsed == 0) return;
- }
- if (info.socket.readyState != info.socket.OPEN) {
- if (!info.senderWaiting) {
- console.log('waiting for socket in order to send');
- setTimeout(info.sender, 100);
- info.senderWaiting = true;
- }
- return;
- }
- if (justQueue) return;
-#if SOCKET_DEBUG
- Module.print('sender actually sending ' + info.sendQueueUsed);
-#endif
- info.socket.send(new Uint8Array(info.sendQueue.subarray(0, info.sendQueueUsed)).buffer); // TODO: if browser accepts views, can optimize this
- info.sendQueueUsed = 0;
- };
+ Sockets.backends[Sockets.backend].connect(info);
return 0;
},
@@ -6893,25 +6917,19 @@ LibraryManager.library = {
recv: function(fd, buf, len, flags) {
var info = Sockets.fds[fd];
if (!info) return -1;
- if (info.bufferWrite == info.bufferRead) {
+ if (info.inQueue.length == 0) {
___setErrNo(ERRNO_CODES.EAGAIN); // no data, and all sockets are nonblocking, so this is the right behavior
return 0; // should this be -1 like the spec says?
}
- var ret = 0;
+ var buffer = info.inQueue.shift();
#if SOCKET_DEBUG
- Module.print('pre-recv: ' + [len, info.bufferWrite, info.bufferRead]);
+ Module.print('recv: ' + [Array.prototype.slice.call(buffer)]);
#endif
- while (info.bufferWrite != info.bufferRead && len > 0) {
- // write out a byte
- {{{ makeSetValue('buf++', '0', 'info.buffer[info.bufferRead++]', 'i8') }}};
- if (info.bufferRead == info.buffer.length) info.bufferRead = 0;
- len--;
- ret++;
+ if (len < buffer.length) {
+ buffer = buffer.subarray(0, len);
}
-#if SOCKET_DEBUG
- Module.print('recv: ' + [ret, len, buf] + ' : ' + Array.prototype.slice.call(HEAPU8.subarray(buf-ret, buf)));
-#endif
- return ret;
+ HEAPU8.set(buffer, buf);
+ return buffer.length;
},
send__deps: ['$Sockets'],
@@ -6934,10 +6952,15 @@ LibraryManager.library = {
}
var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
- var ret = 0;
#if SOCKET_DEBUG
Module.print('sendmsg vecs: ' + num);
#endif
+ var totalSize = 0;
+ for (var i = 0; i < num; i++) {
+ totalSize += {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
+ }
+ var buffer = new Uint8Array(totalSize);
+ var ret = 0;
for (var i = 0; i < num; i++) {
var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
#if SOCKET_DEBUG
@@ -6945,10 +6968,10 @@ LibraryManager.library = {
#endif
if (!currNum) continue;
var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}};
- info.sender(HEAPU8.subarray(currBuf, currBuf+currNum), true);
+ buffer.set(HEAPU8.subarray(currBuf, currBuf+currNum), ret);
ret += currNum;
}
- info.sender(null); // flush all of these together. Important they get sent as a single socket message
+ info.sender(buffer); // send all the iovs as a single message
return ret;
},
@@ -6965,12 +6988,12 @@ LibraryManager.library = {
assert(name, 'sendmsg on non-connected socket, and no name/address in the message');
_connect(fd, name, {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}});
}
- var bytes = info.bufferWrite - info.bufferRead;
- if (bytes < 0) bytes += info.buffer.length;
- if (bytes == 0) {
+ if (info.inQueue.length == 0) {
___setErrNo(ERRNO_CODES.EWOULDBLOCK);
return -1;
}
+ var buffer = info.inQueue.shift();
+ var bytes = buffer.length;
#if SOCKET_DEBUG
Module.print('recvmsg bytes: ' + bytes);
#endif
@@ -6982,7 +7005,7 @@ LibraryManager.library = {
var ret = bytes;
var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
- var data = '';
+ var bufferPos = 0;
for (var i = 0; i < num && bytes > 0; i++) {
var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
#if SOCKET_DEBUG
@@ -6995,7 +7018,14 @@ LibraryManager.library = {
#if SOCKET_DEBUG
Module.print('recvmsg call recv ' + currNum);
#endif
- assert(_recv(fd, currBuf, currNum, 0) == currNum);
+ HEAPU8.set(buffer.subarray(bufferPos, bufferPos + currNum), currBuf);
+ bufferPos += currNum;
+ }
+ if (info.stream) {
+ // This is tcp (reliable), so if not all was read, keep it
+ if (bufferPos < bytes) {
+ info.inQueue.unshift(buffer.subArray(bufferPos));
+ }
}
return ret;
},
@@ -7022,11 +7052,12 @@ LibraryManager.library = {
ioctl: function(fd, request, varargs) {
var info = Sockets.fds[fd];
if (!info) return -1;
- var start = info.bufferRead;
- var end = info.bufferWrite;
- if (end < start) end += info.buffer.length;
+ var bytes = 0;
+ if (info.inQueue.length > 0) {
+ bytes = info.inQueue[0].length;
+ }
var dest = {{{ makeGetValue('varargs', '0', 'i32') }}};
- {{{ makeSetValue('dest', '0', 'end - start', 'i32') }}};
+ {{{ makeSetValue('dest', '0', 'bytes', 'i32') }}};
return 0;
},
@@ -7058,6 +7089,26 @@ LibraryManager.library = {
return fd;
},
+ select: function(nfds, readfds, writefds, exceptfds, timeout) {
+ // only readfds are supported, not writefds or exceptfds
+ // timeout is always 0 - fully async
+ assert(!writefds && !exceptfds);
+ var ret = 0;
+ var l = {{{ makeGetValue('readfds', 0, 'i32') }}};
+ var h = {{{ makeGetValue('readfds', 4, 'i32') }}};
+ nfds = Math.min(64, nfds); // fd sets have 64 bits
+ for (var fd = 0; fd < nfds; fd++) {
+ var bit = fd % 32, int = fd < 32 ? l : h;
+ if (int & (1 << bit)) {
+ // index is in the set, check if it is ready for read
+ var info = Sockets.fds[fd];
+ if (!info) continue;
+ if (info.inQueue.length > 0) ret++;
+ }
+ }
+ return ret;
+ },
+
// ==========================================================================
// emscripten.h
// ==========================================================================
diff --git a/src/library_browser.js b/src/library_browser.js
index b14099f6..fd09f478 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -377,11 +377,11 @@ mergeInto(LibraryManager.library, {
_file.substr(index +1),
_url, true, true,
function() {
- if (onload) FUNCTION_TABLE[onload](file);
+ if (onload) Runtime.dynCall('vi', onload, [file]);
},
function() {
- if (onerror) FUNCTION_TABLE[onerror](file);
- }
+ if (onerror) Runtime.dynCall('vi', onerror, [file]);
+ }
);
},
@@ -396,6 +396,55 @@ mergeInto(LibraryManager.library, {
}, true /* no need for run dependency, this is async but will not do any prepare etc. step */ );
},
+ emscripten_async_wget2: function(url, file, request, param, arg, onload, onerror, onprogress) {
+ var _url = Pointer_stringify(url);
+ var _file = Pointer_stringify(file);
+ var _request = Pointer_stringify(request);
+ var _param = Pointer_stringify(param);
+ var index = _file.lastIndexOf('/');
+
+ var http = new XMLHttpRequest();
+ http.open(_request, _url, true);
+ http.responseType = 'arraybuffer';
+
+ // LOAD
+ http.onload = function(e) {
+ if (http.status == 200) {
+ FS.createDataFile( _file.substr(0, index), _file.substr(index + 1), new Uint8Array(http.response), true, true);
+ if (onload) FUNCTION_TABLE[onload](arg, file);
+ } else {
+ if (onerror) FUNCTION_TABLE[onerror](arg, http.status);
+ }
+ };
+
+ // ERROR
+ http.onerror = function(e) {
+ if (onerror) FUNCTION_TABLE[onerror](arg, http.status);
+ };
+
+ // PROGRESS
+ http.onprogress = function(e) {
+ var percentComplete = (e.position / e.totalSize)*100;
+ if (onprogress) FUNCTION_TABLE[onprogress](arg, percentComplete);
+ };
+
+ // Useful because the browser can limit the number of redirection
+ try {
+ if (http.channel instanceof Ci.nsIHttpChannel)
+ http.channel.redirectionLimit = 0;
+ } catch (ex) { /* whatever */ }
+
+ if (_request == "POST") {
+ //Send the proper header information along with the request
+ http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ http.setRequestHeader("Content-length", _param.length);
+ http.setRequestHeader("Connection", "close");
+ http.send(_param);
+ } else {
+ http.send(null);
+ }
+ },
+
emscripten_async_prepare: function(file, onload, onerror) {
var _file = Pointer_stringify(file);
var data = FS.analyzePath(_file);
@@ -406,10 +455,10 @@ mergeInto(LibraryManager.library, {
_file.substr(index +1),
new Uint8Array(data.object.contents), true, true,
function() {
- if (onload) FUNCTION_TABLE[onload](file);
+ if (onload) Runtime.dynCall('vi', onload, [file]);
},
function() {
- if (onerror) FUNCTION_TABLE[onerror](file);
+ if (onerror) Runtime.dynCall('vi', onerror, [file]);
},
true // don'tCreateFile - it's already there
);
@@ -428,10 +477,10 @@ mergeInto(LibraryManager.library, {
{{{ makeHEAPView('U8', 'data', 'data + size') }}},
true, true,
function() {
- if (onload) FUNCTION_TABLE[onload](arg, cname);
+ if (onload) Runtime.dynCall('vii', onload, [arg, cname]);
},
function() {
- if (onerror) FUNCTION_TABLE[onerror](arg);
+ if (onerror) Runtime.dynCall('vi', onerror, [arg]);
},
true // don'tCreateFile - it's already there
);
@@ -451,7 +500,6 @@ mergeInto(LibraryManager.library, {
emscripten_set_main_loop: function(func, fps, simulateInfiniteLoop) {
Module['noExitRuntime'] = true;
- var jsFunc = FUNCTION_TABLE[func];
Browser.mainLoop.runner = function() {
if (Browser.mainLoop.queue.length > 0) {
var start = Date.now();
@@ -484,7 +532,7 @@ mergeInto(LibraryManager.library, {
Module['preMainLoop']();
}
- jsFunc();
+ Runtime.dynCall('v', func);
if (Module['postMainLoop']) {
Module['postMainLoop']();
@@ -528,12 +576,16 @@ mergeInto(LibraryManager.library, {
},
_emscripten_push_main_loop_blocker: function(func, arg, name) {
- Browser.mainLoop.queue.push({ func: FUNCTION_TABLE[func], arg: arg, name: Pointer_stringify(name), counted: true });
+ Browser.mainLoop.queue.push({ func: function() {
+ Runtime.dynCall('vi', func, [arg]);
+ }, name: Pointer_stringify(name), counted: true });
Browser.mainLoop.updateStatus();
},
_emscripten_push_uncounted_main_loop_blocker: function(func, arg, name) {
- Browser.mainLoop.queue.push({ func: FUNCTION_TABLE[func], arg: arg, name: Pointer_stringify(name), counted: false });
+ Browser.mainLoop.queue.push({ func: function() {
+ Runtime.dynCall('vi', func, [arg]);
+ }, name: Pointer_stringify(name), counted: false });
Browser.mainLoop.updateStatus();
},
@@ -547,7 +599,7 @@ mergeInto(LibraryManager.library, {
Module['noExitRuntime'] = true;
function wrapper() {
- Runtime.getFuncWrapper(func)(arg);
+ Runtime.getFuncWrapper(func, 'vi')(arg);
}
if (millis >= 0) {
@@ -631,7 +683,7 @@ mergeInto(LibraryManager.library, {
if (callback) {
callbackId = info.callbacks.length;
info.callbacks.push({
- func: Runtime.getFuncWrapper(callback),
+ func: Runtime.getFuncWrapper(callback, 'viii'),
arg: arg
});
info.awaited++;
diff --git a/src/library_gc.js b/src/library_gc.js
index a06e2f01..fe4cbf63 100644
--- a/src/library_gc.js
+++ b/src/library_gc.js
@@ -1,5 +1,7 @@
if (GC_SUPPORT) {
+ EXPORTED_FUNCTIONS['_calloc'] = 1;
+
var LibraryGC = {
$GC__deps: ['sbrk'],
$GC: {
@@ -50,7 +52,7 @@ if (GC_SUPPORT) {
free: function(ptr) { // does not check if anything refers to it, this is a forced free
var finalizer = GC.finalizers[ptr];
if (finalizer) {
- Runtime.getFuncWrapper(finalizer)(ptr, GC.finalizerArgs[ptr]);
+ Runtime.getFuncWrapper(finalizer, 'vii')(ptr, GC.finalizerArgs[ptr]);
GC.finalizers[ptr] = 0;
}
_free(ptr);
diff --git a/src/library_gl.js b/src/library_gl.js
index 0f28a6a0..267a6185 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -1275,11 +1275,12 @@ var LibraryGL = {
getProcAddress: function(name) {
name = name.replace('EXT', '').replace('ARB', '');
// Do the translation carefully because of closure
+ var sig = '', func;
switch (name) {
- case 'glCreateShaderObject': case 'glCreateShader': func = _glCreateShader; break;
- case 'glCreateProgramObject': case 'glCreateProgram': func = _glCreateProgram; break;
- case 'glAttachObject': case 'glAttachShader': func = _glAttachShader; break;
- case 'glUseProgramObject': case 'glUseProgram': func = _glUseProgram; break;
+ case 'glCreateShaderObject': case 'glCreateShader': func = _glCreateShader; sig = 'ii'; break;
+ case 'glCreateProgramObject': case 'glCreateProgram': func = _glCreateProgram; sig = 'ii'; break;
+ case 'glAttachObject': case 'glAttachShader': func = _glAttachShader; sig = 'vi'; break;
+ case 'glUseProgramObject': case 'glUseProgram': func = _glUseProgram; sig = 'vi'; break;
case 'glDeleteObject': func = function(id) {
if (GL.programs[id]) {
_glDeleteProgram(id);
@@ -1288,7 +1289,7 @@ var LibraryGL = {
} else {
Module.printErr('WARNING: deleteObject received invalid id: ' + id);
}
- }; break;
+ }; sig = 'vi'; break;
case 'glGetObjectParameteriv': func = function(id, type, result) {
if (GL.programs[id]) {
if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB
@@ -1305,7 +1306,7 @@ var LibraryGL = {
} else {
Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id);
}
- }; break;
+ }; sig = 'viii'; break;
case 'glGetInfoLog': func = function(id, maxLength, length, infoLog) {
if (GL.programs[id]) {
_glGetProgramInfoLog(id, maxLength, length, infoLog);
@@ -1314,67 +1315,68 @@ var LibraryGL = {
} else {
Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id);
}
- }; break;
+ }; sig = 'viiii'; break;
case 'glBindProgram': func = function(type, id) {
assert(id == 0);
- }; break;
- case 'glDrawRangeElements': func = _glDrawRangeElements; break;
- case 'glShaderSource': func = _glShaderSource; break;
- case 'glCompileShader': func = _glCompileShader; break;
- case 'glLinkProgram': func = _glLinkProgram; break;
- case 'glGetUniformLocation': func = _glGetUniformLocation; break;
- case 'glUniform1f': func = _glUniform1f; break;
- case 'glUniform2f': func = _glUniform2f; break;
- case 'glUniform3f': func = _glUniform3f; break;
- case 'glUniform4f': func = _glUniform4f; break;
- case 'glUniform1fv': func = _glUniform1fv; break;
- case 'glUniform2fv': func = _glUniform2fv; break;
- case 'glUniform3fv': func = _glUniform3fv; break;
- case 'glUniform4fv': func = _glUniform4fv; break;
- case 'glUniform1i': func = _glUniform1i; break;
- case 'glUniform2i': func = _glUniform2i; break;
- case 'glUniform3i': func = _glUniform3i; break;
- case 'glUniform4i': func = _glUniform4i; break;
- case 'glUniform1iv': func = _glUniform1iv; break;
- case 'glUniform2iv': func = _glUniform2iv; break;
- case 'glUniform3iv': func = _glUniform3iv; break;
- case 'glUniform4iv': func = _glUniform4iv; break;
- case 'glBindAttribLocation': func = _glBindAttribLocation; break;
- case 'glGetActiveUniform': func = _glGetActiveUniform; break;
- case 'glGenBuffers': func = _glGenBuffers; break;
- case 'glBindBuffer': func = _glBindBuffer; break;
- case 'glBufferData': func = _glBufferData; break;
- case 'glBufferSubData': func = _glBufferSubData; break;
- case 'glDeleteBuffers': func = _glDeleteBuffers; break;
- case 'glActiveTexture': func = _glActiveTexture; break;
- case 'glClientActiveTexture': func = _glClientActiveTexture; break;
- case 'glGetProgramiv': func = _glGetProgramiv; break;
- case 'glEnableVertexAttribArray': func = _glEnableVertexAttribArray; break;
- case 'glDisableVertexAttribArray': func = _glDisableVertexAttribArray; break;
- case 'glVertexAttribPointer': func = _glVertexAttribPointer; break;
- case 'glBindRenderbuffer': func = _glBindRenderbuffer; break;
- case 'glDeleteRenderbuffers': func = _glDeleteRenderbuffers; break;
- case 'glGenRenderbuffers': func = _glGenRenderbuffers; break;
- case 'glCompressedTexImage2D': func = _glCompressedTexImage2D; break;
- case 'glCompressedTexSubImage2D': func = _glCompressedTexSubImage2D; break;
- case 'glBindFramebuffer': func = _glBindFramebuffer; break;
- case 'glGenFramebuffers': func = _glGenFramebuffers; break;
- case 'glDeleteFramebuffers': func = _glDeleteFramebuffers; break;
- case 'glFramebufferRenderbuffer': func = _glFramebufferRenderbuffer; break;
- case 'glFramebufferTexture2D': func = _glFramebufferTexture2D; break;
- case 'glGetFramebufferAttachmentParameteriv': func = _glGetFramebufferAttachmentParameteriv; break;
- case 'glIsFramebuffer': func = _glIsFramebuffer; break;
- case 'glCheckFramebufferStatus': func = _glCheckFramebufferStatus; break;
- case 'glRenderbufferStorage': func = _glRenderbufferStorage; break;
+ }; sig = 'vii'; break;
+ case 'glDrawRangeElements': func = _glDrawRangeElements; sig = 'viiiiii'; break;
+ case 'glShaderSource': func = _glShaderSource; sig = 'viiii'; break;
+ case 'glCompileShader': func = _glCompileShader; sig = 'vi'; break;
+ case 'glLinkProgram': func = _glLinkProgram; sig = 'vi'; break;
+ case 'glGetUniformLocation': func = _glGetUniformLocation; sig = 'iii'; break;
+ case 'glUniform1f': func = _glUniform1f; sig = 'vid'; break;
+ case 'glUniform2f': func = _glUniform2f; sig = 'vidd'; break;
+ case 'glUniform3f': func = _glUniform3f; sig = 'viddd'; break;
+ case 'glUniform4f': func = _glUniform4f; sig = 'vidddd'; break;
+ case 'glUniform1fv': func = _glUniform1fv; sig = 'viii'; break;
+ case 'glUniform2fv': func = _glUniform2fv; sig = 'viii'; break;
+ case 'glUniform3fv': func = _glUniform3fv; sig = 'viii'; break;
+ case 'glUniform4fv': func = _glUniform4fv; sig = 'viii'; break;
+ case 'glUniform1i': func = _glUniform1i; sig = 'vii'; break;
+ case 'glUniform2i': func = _glUniform2i; sig = 'viii'; break;
+ case 'glUniform3i': func = _glUniform3i; sig = 'viiii'; break;
+ case 'glUniform4i': func = _glUniform4i; sig = 'viiii'; break;
+ case 'glUniform1iv': func = _glUniform1iv; sig = 'viii'; break;
+ case 'glUniform2iv': func = _glUniform2iv; sig = 'viii'; break;
+ case 'glUniform3iv': func = _glUniform3iv; sig = 'viii'; break;
+ case 'glUniform4iv': func = _glUniform4iv; sig = 'viii'; break;
+ case 'glBindAttribLocation': func = _glBindAttribLocation; sig = 'viii'; break;
+ case 'glGetActiveUniform': func = _glGetActiveUniform; sig = 'viiiiiii'; break;
+ case 'glGenBuffers': func = _glGenBuffers; sig = 'iii'; break;
+ case 'glBindBuffer': func = _glBindBuffer; sig = 'vii'; break;
+ case 'glBufferData': func = _glBufferData; sig = 'viiii'; break;
+ case 'glBufferSubData': func = _glBufferSubData; sig = 'viiii'; break;
+ case 'glDeleteBuffers': func = _glDeleteBuffers; sig = 'vii'; break;
+ case 'glActiveTexture': func = _glActiveTexture; sig = 'vi'; break;
+ case 'glClientActiveTexture': func = _glClientActiveTexture; sig = 'vi'; break;
+ case 'glGetProgramiv': func = _glGetProgramiv; sig = 'viii'; break;
+ case 'glEnableVertexAttribArray': func = _glEnableVertexAttribArray; sig = 'vi'; break;
+ case 'glDisableVertexAttribArray': func = _glDisableVertexAttribArray; sig = 'vi'; break;
+ case 'glVertexAttribPointer': func = _glVertexAttribPointer; sig = 'viiiiii'; break;
+ case 'glBindRenderbuffer': func = _glBindRenderbuffer; sig = 'vii'; break;
+ case 'glDeleteRenderbuffers': func = _glDeleteRenderbuffers; sig = 'vii'; break;
+ case 'glGenRenderbuffers': func = _glGenRenderbuffers; sig = 'vii'; break;
+ case 'glCompressedTexImage2D': func = _glCompressedTexImage2D; sig = 'viiiiiiii'; break;
+ case 'glCompressedTexSubImage2D': func = _glCompressedTexSubImage2D; sig = 'viiiiiiiii'; break;
+ case 'glBindFramebuffer': func = _glBindFramebuffer; sig = 'vii'; break;
+ case 'glGenFramebuffers': func = _glGenFramebuffers; sig = 'vii'; break;
+ case 'glDeleteFramebuffers': func = _glDeleteFramebuffers; sig = 'vii'; break;
+ case 'glFramebufferRenderbuffer': func = _glFramebufferRenderbuffer; sig = 'viiii'; break;
+ case 'glFramebufferTexture2D': func = _glFramebufferTexture2D; sig = 'viiiii'; break;
+ case 'glGetFramebufferAttachmentParameteriv': func = _glGetFramebufferAttachmentParameteriv; sig = 'viiii'; break;
+ case 'glIsFramebuffer': func = _glIsFramebuffer; sig = 'ii'; break;
+ case 'glCheckFramebufferStatus': func = _glCheckFramebufferStatus; sig = 'ii'; break;
+ case 'glRenderbufferStorage': func = _glRenderbufferStorage; sig = 'viiii'; break;
default: {
Module.printErr('WARNING: getProcAddress failed for ' + name);
func = function() {
Module.printErr('WARNING: empty replacement for ' + name + ' called, no-op');
return 0;
};
+ sig = 'v';
}
}
- return Runtime.addFunction(func);
+ return Runtime.addFunction(func, sig);
}
},
@@ -2079,8 +2081,9 @@ var LibraryGL = {
glVertex3fv: function(p) {
_glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}});
},
+ glVertex2fv__deps: ['glVertex3f'],
glVertex2fv: function(p) {
- _glVertex2f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}});
+ _glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, 0);
},
glTexCoord2i: function(u, v) {
@@ -2093,9 +2096,9 @@ var LibraryGL = {
},
glTexCoord2f: 'glTexCoord2i',
- glTexCoord2fv__deps: ['glTexCoord2f'],
+ glTexCoord2fv__deps: ['glTexCoord2i'],
glTexCoord2fv: function(v) {
- _glTexCoord2f({{{ makeGetValue('v', '0', 'float') }}}, {{{ makeGetValue('v', '4', 'float') }}});
+ _glTexCoord2i({{{ makeGetValue('v', '0', 'float') }}}, {{{ makeGetValue('v', '4', 'float') }}});
},
glColor4f: function(r, g, b, a) {
diff --git a/src/library_glut.js b/src/library_glut.js
index 6069b484..2e662698 100644
--- a/src/library_glut.js
+++ b/src/library_glut.js
@@ -54,11 +54,11 @@ var LibraryGLUT = {
if (GLUT.buttons == 0 && event.target == Module["canvas"] && GLUT.passiveMotionFunc) {
event.preventDefault();
GLUT.saveModifiers(event);
- FUNCTION_TABLE[GLUT.passiveMotionFunc](GLUT.lastX, GLUT.lastY);
+ Runtime.dynCall('vii', GLUT.passiveMotionFunc, [GLUT.lastX, GLUT.lastY]);
} else if (GLUT.buttons != 0 && GLUT.motionFunc) {
event.preventDefault();
GLUT.saveModifiers(event);
- FUNCTION_TABLE[GLUT.motionFunc](GLUT.lastX, GLUT.lastY);
+ Runtime.dynCall('vii', GLUT.motionFunc, [GLUT.lastX, GLUT.lastY]);
}
},
@@ -159,7 +159,7 @@ var LibraryGLUT = {
if( GLUT.specialFunc ) {
event.preventDefault();
GLUT.saveModifiers(event);
- FUNCTION_TABLE[GLUT.specialFunc](key, GLUT.lastX, GLUT.lastY);
+ Runtime.dynCall('viii', GLUT.specialFunc, [key, GLUT.lastX, GLUT.lastY]);
}
}
else
@@ -168,7 +168,7 @@ var LibraryGLUT = {
if( key !== null && GLUT.keyboardFunc ) {
event.preventDefault();
GLUT.saveModifiers(event);
- FUNCTION_TABLE[GLUT.keyboardFunc](key, GLUT.lastX, GLUT.lastY);
+ Runtime.dynCall('viii', GLUT.keyboardFunc, [key, GLUT.lastX, GLUT.lastY]);
}
}
}
@@ -181,7 +181,7 @@ var LibraryGLUT = {
if(GLUT.specialUpFunc) {
event.preventDefault ();
GLUT.saveModifiers(event);
- FUNCTION_TABLE[GLUT.specialUpFunc](key, GLUT.lastX, GLUT.lastY);
+ Runtime.dynCall('viii', GLUT.specialUpFunc, [key, GLUT.lastX, GLUT.lastY]);
}
}
else
@@ -190,7 +190,7 @@ var LibraryGLUT = {
if( key !== null && GLUT.keyboardUpFunc ) {
event.preventDefault ();
GLUT.saveModifiers(event);
- FUNCTION_TABLE[GLUT.keyboardUpFunc](key, GLUT.lastX, GLUT.lastY);
+ Runtime.dynCall('viii', GLUT.keyboardUpFunc, [key, GLUT.lastX, GLUT.lastY]);
}
}
}
@@ -206,7 +206,7 @@ var LibraryGLUT = {
} catch (e) {}
event.preventDefault();
GLUT.saveModifiers(event);
- FUNCTION_TABLE[GLUT.mouseFunc](event['button'], 0/*GLUT_DOWN*/, GLUT.lastX, GLUT.lastY);
+ Runtime.dynCall('viiii', GLUT.mouseFunc, [event['button'], 0/*GLUT_DOWN*/, GLUT.lastX, GLUT.lastY]);
}
},
@@ -217,7 +217,7 @@ var LibraryGLUT = {
if(GLUT.mouseFunc) {
event.preventDefault();
GLUT.saveModifiers(event);
- FUNCTION_TABLE[GLUT.mouseFunc](event['button'], 1/*GLUT_UP*/, GLUT.lastX, GLUT.lastY);
+ Runtime.dynCall('viiii', GLUT.mouseFunc, [event['button'], 1/*GLUT_UP*/, GLUT.lastX, GLUT.lastY]);
}
},
@@ -241,7 +241,7 @@ var LibraryGLUT = {
/* Can't call _glutReshapeWindow as that requests cancelling fullscreen. */
if (GLUT.reshapeFunc) {
// console.log("GLUT.reshapeFunc (from FS): " + width + ", " + height);
- FUNCTION_TABLE[GLUT.reshapeFunc](width, height);
+ Runtime.dynCall('vii', GLUT.reshapeFunc, [width, height]);
}
_glutPostRedisplay();
},
@@ -326,7 +326,7 @@ var LibraryGLUT = {
glutIdleFunc: function(func) {
var callback = function() {
if (GLUT.idleFunc) {
- FUNCTION_TABLE[GLUT.idleFunc]();
+ Runtime.dynCall('v', GLUT.idleFunc);
window.setTimeout(callback, 0);
}
}
@@ -336,7 +336,7 @@ var LibraryGLUT = {
},
glutTimerFunc: function(msec, func, value) {
- window.setTimeout(function() { FUNCTION_TABLE[func](value); }, msec);
+ window.setTimeout(function() { Runtime.dynCall('vi', func, [value]); }, msec);
},
glutDisplayFunc: function(func) {
@@ -388,7 +388,7 @@ var LibraryGLUT = {
Browser.setCanvasSize(width, height);
if (GLUT.reshapeFunc) {
// console.log("GLUT.reshapeFunc: " + width + ", " + height);
- FUNCTION_TABLE[GLUT.reshapeFunc](width, height);
+ Runtime.dynCall('vii', GLUT.reshapeFunc, [width, height]);
}
_glutPostRedisplay();
},
@@ -417,7 +417,9 @@ var LibraryGLUT = {
glutPostRedisplay: function() {
if (GLUT.displayFunc) {
- Browser.requestAnimationFrame(FUNCTION_TABLE[GLUT.displayFunc]);
+ Browser.requestAnimationFrame(function() {
+ Runtime.dynCall('v', GLUT.displayFunc);
+ });
}
},
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 1cd99534..b2ca338b 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -707,7 +707,9 @@ var LibrarySDL = {
SDL_Quit: function() {
for (var i = 0; i < SDL.numChannels; ++i) {
- SDL.channels[i].audio.pause();
+ if (SDL.channels[i].audio) {
+ SDL.channels[i].audio.pause();
+ }
}
if (SDL.music.audio) {
SDL.music.audio.pause();
@@ -1116,7 +1118,7 @@ var LibrarySDL = {
SDL.audio.bufferSize = totalSamples*2; // hardcoded 16-bit audio
SDL.audio.buffer = _malloc(SDL.audio.bufferSize);
SDL.audio.caller = function() {
- FUNCTION_TABLE[SDL.audio.callback](SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize);
+ Runtime.dynCall('viii', SDL.audio.callback, [SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize]);
SDL.audio.pushAudio(SDL.audio.buffer, SDL.audio.bufferSize);
};
// Mozilla Audio API. TODO: Other audio APIs
@@ -1160,6 +1162,8 @@ var LibrarySDL = {
SDL_CreateMutex: function() { return 0 },
SDL_LockMutex: function() {},
SDL_UnlockMutex: function() {},
+ SDL_mutexP: function() { return 0 },
+ SDL_mutexV: function() { return 0 },
SDL_DestroyMutex: function() {},
SDL_CreateCond: function() { return 0 },
@@ -1278,7 +1282,7 @@ var LibrarySDL = {
channelInfo.audio = audio = audio.cloneNode(true);
if (SDL.channelFinished) {
audio['onended'] = function() { // TODO: cache these
- Runtime.getFuncWrapper(SDL.channelFinished)(channel);
+ Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
}
}
// Either play the element, or load the dynamic data into it
@@ -1349,7 +1353,7 @@ var LibrarySDL = {
info.audio = null;
}
if (SDL.channelFinished) {
- Runtime.getFuncWrapper(SDL.channelFinished)(channel);
+ Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
}
return 0;
},
@@ -1408,7 +1412,7 @@ var LibrarySDL = {
audio.pause();
SDL.music.audio = null;
if (SDL.hookMusicFinished) {
- FUNCTION_TABLE[SDL.hookMusicFinished]();
+ Runtime.dynCall('v', SDL.hookMusicFinished);
}
return 0;
},
@@ -1439,6 +1443,10 @@ var LibrarySDL = {
return id;
},
+ TTF_CloseFont: function(font) {
+ SDL.fonts[font] = null;
+ },
+
TTF_RenderText_Solid: function(font, text, color) {
// XXX the font and color are ignored
text = Pointer_stringify(text) || ' '; // if given an empty string, still return a valid surface
@@ -1557,7 +1565,7 @@ var LibrarySDL = {
SDL_AddTimer: function(interval, callback, param) {
return window.setTimeout(function() {
- FUNCTION_TABLE[callback](interval, param);
+ Runtime.dynCall('ii', callback, [interval, param]);
}, interval);
},
SDL_RemoveTimer: function(id) {
diff --git a/src/long.js b/src/long.js
index fc67733f..c3b0e605 100644
--- a/src/long.js
+++ b/src/long.js
@@ -1530,27 +1530,26 @@ var i64Math = (function() { // Emscripten wrapper
// Emscripten wrapper
var Wrapper = {
- result: [0, 0], // return result stored here
add: function(xl, xh, yl, yh) {
var x = new goog.math.Long(xl, xh);
var y = new goog.math.Long(yl, yh);
var ret = x.add(y);
- Wrapper.result[0] = ret.low_;
- Wrapper.result[1] = ret.high_;
+ HEAP32[tempDoublePtr>>2] = ret.low_;
+ HEAP32[tempDoublePtr+4>>2] = ret.high_;
},
subtract: function(xl, xh, yl, yh) {
var x = new goog.math.Long(xl, xh);
var y = new goog.math.Long(yl, yh);
var ret = x.subtract(y);
- Wrapper.result[0] = ret.low_;
- Wrapper.result[1] = ret.high_;
+ HEAP32[tempDoublePtr>>2] = ret.low_;
+ HEAP32[tempDoublePtr+4>>2] = ret.high_;
},
multiply: function(xl, xh, yl, yh) {
var x = new goog.math.Long(xl, xh);
var y = new goog.math.Long(yl, yh);
var ret = x.multiply(y);
- Wrapper.result[0] = ret.low_;
- Wrapper.result[1] = ret.high_;
+ HEAP32[tempDoublePtr>>2] = ret.low_;
+ HEAP32[tempDoublePtr+4>>2] = ret.high_;
},
ensureTemps: function() {
if (Wrapper.ensuredTemps) return;
@@ -1579,8 +1578,8 @@ var i64Math = (function() { // Emscripten wrapper
var x = new goog.math.Long(xl, xh);
var y = new goog.math.Long(yl, yh);
var ret = x.div(y);
- Wrapper.result[0] = ret.low_;
- Wrapper.result[1] = ret.high_;
+ HEAP32[tempDoublePtr>>2] = ret.low_;
+ HEAP32[tempDoublePtr+4>>2] = ret.high_;
} else {
// slow precise bignum division
var x = Wrapper.lh2bignum(xl >>> 0, xh >>> 0);
@@ -1590,8 +1589,8 @@ var i64Math = (function() { // Emscripten wrapper
var l = new BigInteger();
var h = new BigInteger();
z.divRemTo(Wrapper.two32, h, l);
- Wrapper.result[0] = parseInt(l.toString()) | 0;
- Wrapper.result[1] = parseInt(h.toString()) | 0;
+ HEAP32[tempDoublePtr>>2] = parseInt(l.toString()) | 0;
+ HEAP32[tempDoublePtr+4>>2] = parseInt(h.toString()) | 0;
}
},
modulo: function(xl, xh, yl, yh, unsigned) {
@@ -1600,8 +1599,8 @@ var i64Math = (function() { // Emscripten wrapper
var x = new goog.math.Long(xl, xh);
var y = new goog.math.Long(yl, yh);
var ret = x.modulo(y);
- Wrapper.result[0] = ret.low_;
- Wrapper.result[1] = ret.high_;
+ HEAP32[tempDoublePtr>>2] = ret.low_;
+ HEAP32[tempDoublePtr+4>>2] = ret.high_;
} else {
// slow precise bignum division
var x = Wrapper.lh2bignum(xl >>> 0, xh >>> 0);
@@ -1611,8 +1610,8 @@ var i64Math = (function() { // Emscripten wrapper
var l = new BigInteger();
var h = new BigInteger();
z.divRemTo(Wrapper.two32, h, l);
- Wrapper.result[0] = parseInt(l.toString()) | 0;
- Wrapper.result[1] = parseInt(h.toString()) | 0;
+ HEAP32[tempDoublePtr>>2] = parseInt(l.toString()) | 0;
+ HEAP32[tempDoublePtr+4>>2] = parseInt(h.toString()) | 0;
}
},
stringify: function(l, h, unsigned) {
@@ -1650,8 +1649,8 @@ var i64Math = (function() { // Emscripten wrapper
error = true;
}
var ret = goog.math.Long.fromString(bignum.toString()); // min-max checks should have clamped this to a range goog.math.Long can handle well
- Wrapper.result[0] = ret.low_;
- Wrapper.result[1] = ret.high_;
+ HEAP32[tempDoublePtr>>2] = ret.low_;
+ HEAP32[tempDoublePtr+4>>2] = ret.high_;
if (error) throw 'range error';
}
};
diff --git a/src/modules.js b/src/modules.js
index 64638be1..b5a30866 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -216,17 +216,29 @@ var Types = {
};
var Functions = {
- // All functions that will be implemented in this file
+ // All functions that will be implemented in this file. Maps id to signature
implementedFunctions: {},
+ libraryFunctions: {}, // functions added from the library
+ unimplementedFunctions: {}, // library etc. functions that we need to index, maps id to signature
indexedFunctions: {},
nextIndex: 2, // Start at a non-0 (even, see below) value
blockAddresses: {}, // maps functions to a map of block labels to label ids
+ getSignature: function(returnType, argTypes) {
+ var sig = returnType == 'void' ? 'v' : (isIntImplemented(returnType) ? 'i' : 'f');
+ for (var i = 0; i < argTypes.length; i++) {
+ var type = argTypes[i];
+ if (!type) break; // varargs
+ sig += isIntImplemented(type) ? (getBits(type) == 64 ? 'ii' : 'i') : 'f'; // legalized i64s will be i32s
+ }
+ return sig;
+ },
+
// Mark a function as needing indexing. Python will coordinate them all
getIndex: function(ident) {
- if (phase != 'post') {
+ if (phase != 'post' && singlePhase) {
this.indexedFunctions[ident] = 0; // tell python we need this indexized
return '{{{ FI_' + ident + ' }}}'; // something python will replace later
} else {
@@ -240,27 +252,60 @@ var Functions = {
}
},
+ getTable: function(sig) {
+ return ASM_JS ? 'FUNCTION_TABLE_' + sig : 'FUNCTION_TABLE';
+ },
+
// Generate code for function indexing
generateIndexing: function() {
- var vals = zeros(this.nextIndex);
+ var total = this.nextIndex;
+ if (ASM_JS) total = ceilPowerOfTwo(total); // must be power of 2 for mask
+ function emptyTable(sig) {
+ return zeros(total);
+ }
+ var tables = {};
+ if (ASM_JS) {
+ ['v', 'vi', 'ii', 'iii'].forEach(function(sig) { // add some default signatures that are used in the library
+ tables[sig] = emptyTable(sig); // TODO: make them compact
+ });
+ }
for (var ident in this.indexedFunctions) {
- vals[this.indexedFunctions[ident]] = ident;
+ var sig = ASM_JS ? Functions.implementedFunctions[ident] || Functions.unimplementedFunctions[ident] : 'x';
+ assert(sig, ident);
+ if (!tables[sig]) tables[sig] = emptyTable(sig); // TODO: make them compact
+ tables[sig][this.indexedFunctions[ident]] = ident;
}
- // Resolve multi-level aliases all the way down
- for (var i = 0; i < vals.length; i++) {
- while (1) {
- var varData = Variables.globals[vals[i]];
- if (!(varData && varData.resolvedAlias)) break;
- vals[i] = vals[+varData.resolvedAlias || eval(varData.resolvedAlias)]; // might need to eval to turn (6) into 6
+ var generated = false;
+ for (var t in tables) {
+ generated = true;
+ var table = tables[t];
+ for (var i = 0; i < table.length; i++) {
+ // Resolve multi-level aliases all the way down
+ while (1) {
+ var varData = Variables.globals[table[i]];
+ if (!(varData && varData.resolvedAlias)) break;
+ table[i] = table[+varData.resolvedAlias || eval(varData.resolvedAlias)]; // might need to eval to turn (6) into 6
+ }
+ // Resolve library aliases
+ if (table[i]) {
+ var libName = LibraryManager.getRootIdent(table[i].substr(1));
+ if (libName && typeof libName == 'string') {
+ table[i] = (libName.indexOf('.') < 0 ? '_' : '') + libName;
+ }
+ }
+ }
+ var indices = table.toString().replace('"', '');
+ if (BUILD_AS_SHARED_LIB) {
+ // Shared libraries reuse the parent's function table.
+ tables[t] = Functions.getTable(t) + '.push.apply(' + Functions.getTable(t) + ', [' + indices + ']);\n';
+ } else {
+ tables[t] = 'var ' + Functions.getTable(t) + ' = [' + indices + '];\n';
}
}
- var indices = vals.toString().replace('"', '');
- if (BUILD_AS_SHARED_LIB) {
- // Shared libraries reuse the parent's function table.
- return 'FUNCTION_TABLE.push.apply(FUNCTION_TABLE, [' + indices + ']);';
- } else {
- return 'FUNCTION_TABLE = [' + indices + ']; Module["FUNCTION_TABLE"] = FUNCTION_TABLE;';
+ if (!generated && !ASM_JS) {
+ tables['x'] = 'var FUNCTION_TABLE = [0, 0];\n'; // default empty table
}
+ Functions.tables = tables;
}
};
@@ -311,16 +356,23 @@ var PassManager = {
print('\n//FORWARDED_DATA:' + JSON.stringify({
Types: Types,
Variables: Variables,
- Functions: Functions
+ Functions: Functions,
+ EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS // needed for asm.js global constructors (ctors)
}));
} else if (phase == 'funcs') {
print('\n//FORWARDED_DATA:' + JSON.stringify({
Types: { preciseI64MathUsed: Types.preciseI64MathUsed },
Functions: {
blockAddresses: Functions.blockAddresses,
- indexedFunctions: Functions.indexedFunctions
+ indexedFunctions: Functions.indexedFunctions,
+ implementedFunctions: ASM_JS ? Functions.implementedFunctions : [],
+ unimplementedFunctions: Functions.unimplementedFunctions,
}
}));
+ } else if (phase == 'post') {
+ print('\n//FORWARDED_DATA:' + JSON.stringify({
+ Functions: { tables: Functions.tables }
+ }));
}
},
load: function(json) {
diff --git a/src/parseTools.js b/src/parseTools.js
index b2093da3..6cb3e776 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -128,16 +128,48 @@ function isStructType(type) {
return type[0] == '%';
}
+function isStructuralType(type) {
+ return /^{ ?[^}]* ?}$/.test(type); // { i32, i8 } etc. - anonymous struct types
+}
+
+function getStructuralTypeParts(type) { // split { i32, i8 } etc. into parts
+ return type.replace(/[ {}]/g, '').split(',');
+}
+
+function getStructureTypeParts(type) {
+ if (isStructuralType(type)) {
+ return type.replace(/[ {}]/g, '').split(',');
+ } else {
+ var typeData = Types.types[type];
+ assert(typeData, type);
+ return typeData.fields;
+ }
+}
+
+function getStructuralTypePartBits(part) {
+ return Math.ceil((getBits(part) || 32)/32)*32; // simple 32-bit alignment. || 32 is for pointers
+}
+
function isIntImplemented(type) {
return type[0] == 'i' || isPointerType(type);
}
-// Note: works for iX types, not pointers (even though they are implemented as ints)
+// Note: works for iX types and structure types, not pointers (even though they are implemented as ints)
function getBits(type) {
- if (!type || type[0] != 'i') return 0;
- var left = type.substr(1);
- if (!isNumber(left)) return 0;
- return parseInt(left);
+ if (!type) return 0;
+ if (type[0] == 'i') {
+ var left = type.substr(1);
+ if (!isNumber(left)) return 0;
+ return parseInt(left);
+ }
+ if (isStructuralType(type)) {
+ return sum(getStructuralTypeParts(type).map(getStructuralTypePartBits));
+ }
+ if (isStructType(type)) {
+ var typeData = Types.types[type];
+ return typeData.flatSize*8;
+ }
+ return 0;
}
function isIllegalType(type) {
@@ -163,11 +195,13 @@ function isFunctionDef(token, out) {
var subtext = segment[0].text;
fail = fail || segment.length > 1 || !(isType(subtext) || subtext == '...');
});
- if (out) out.numArgs = segments.length;
+ if (out) {
+ out.segments = segments;
+ out.numArgs = segments.length;
+ }
return !fail;
}
-
function isPossiblyFunctionType(type) {
// A quick but unreliable way to see if something is a function type. Yes is just 'maybe', no is definite.
var len = type.length;
@@ -189,6 +223,7 @@ function isFunctionType(type, out) {
if (pointingLevels(type) !== 1) return false;
var text = removeAllPointing(parts.slice(1).join(' '));
if (!text) return false;
+ if (out) out.returnType = parts[0];
return isType(parts[0]) && isFunctionDef({ text: text, item: tokenize(text.substr(1, text.length-2), true) }, out);
}
@@ -310,7 +345,9 @@ function parseParamTokens(params) {
if (segment.length == 1) {
if (segment[0].text == '...') {
ret.push({
- intertype: 'varargs'
+ intertype: 'varargs',
+ type: 'i8*',
+ ident: 'varrp' // the conventional name we have for this
});
} else {
// Clang sometimes has a parameter with just a type,
@@ -360,25 +397,34 @@ var UNINDEXABLE_GLOBALS = set(
'_llvm_global_ctors' // special-cased
);
-function noticePtr(ptr) {
- if (!NAMED_GLOBALS && !LibraryManager.loaded) UNINDEXABLE_GLOBALS[ptr] = 1; // we cannot index globals referred to in the library, since they are used there by name
-}
-
function isIndexableGlobal(ident) {
if (!(ident in Variables.globals)) return false;
- if (ident in UNINDEXABLE_GLOBALS) return false;
+ if (ident in UNINDEXABLE_GLOBALS) {
+ Variables.globals[ident].unIndexable = true;
+ return false;
+ }
var data = Variables.globals[ident];
- return !data.alias && !data.external;
+ // in asm.js, externals are just globals
+ return !data.alias && (ASM_JS || !data.external);
}
function makeGlobalDef(ident) {
if (!NAMED_GLOBALS && isIndexableGlobal(ident)) return '';
- return 'var ' + ident + ';'; // TODO: add option for namespacing or offsetting to allow reducing the number of globals
+ return 'var ' + ident + ';';
}
function makeGlobalUse(ident) {
- if (!NAMED_GLOBALS && isIndexableGlobal(ident)) return '(' + getFastValue('GLOBAL_BASE', '+', Variables.indexedGlobals[ident]) + ')';
- return ident; // TODO: add option for namespacing or offsetting to allow reducing the number of globals
+ if (!NAMED_GLOBALS && isIndexableGlobal(ident)) {
+ var index = Variables.indexedGlobals[ident];
+ if (index === undefined) {
+ // we are accessing this before we index globals, likely from the library. mark as unindexable
+ UNINDEXABLE_GLOBALS[ident] = 1;
+ return ident;
+ }
+ // We know and assert on TOTAL_STACK being equal to GLOBAL_BASE
+ return (TOTAL_STACK + index).toString();
+ }
+ return ident;
}
function sortGlobals(globals) {
@@ -447,7 +493,7 @@ function parseLLVMSegment(segment) {
return {
intertype: 'value',
ident: toNiceIdent(segment[1].text),
- type: segment[0].text
+ type: type
};
}
}
@@ -922,6 +968,12 @@ function checkSafeHeap() {
return SAFE_HEAP === 1 || checkSpecificSafeHeap();
}
+if (ASM_JS) {
+ var hexMemoryMask = '0x' + (TOTAL_MEMORY-1).toString(16);
+ var decMemoryMask = (TOTAL_MEMORY-1).toString();
+ var memoryMask = hexMemoryMask.length <= decMemoryMask.length ? hexMemoryMask : decMemoryMask;
+}
+
function getHeapOffset(offset, type) {
if (USE_TYPED_ARRAYS !== 2) {
return offset;
@@ -930,17 +982,64 @@ function getHeapOffset(offset, type) {
type = 'i32'; // XXX we emulate 64-bit values as 32
}
var shifts = Math.log(Runtime.getNativeTypeSize(type))/Math.LN2;
+ offset = '(' + offset + ')';
+ if (ASM_JS && phase == 'funcs') offset = '(' + offset + '&' + memoryMask + ')';
if (shifts != 0) {
- return '((' + offset + ')>>' + (shifts) + ')';
+ return '(' + offset + '>>' + shifts + ')';
} else {
- return '(' + offset + ')';
+ return offset;
}
}
}
+function makeVarDef(js) {
+ if (!ASM_JS) js = 'var ' + js;
+ return js;
+}
+
+function asmEnsureFloat(value, type) { // ensures that a float type has either 5.5 (clearly a float) or +5 (float due to asm coercion)
+ if (!ASM_JS) return value;
+ if (type in Runtime.FLOAT_TYPES && isNumber(value) && value.toString().indexOf('.') < 0) {
+ return '(+(' + value + '))';
+ } else {
+ return value;
+ }
+}
+
+function asmInitializer(type, impl) {
+ if (type in Runtime.FLOAT_TYPES) {
+ return '+0';
+ } else {
+ return '0';
+ }
+}
+
+function asmCoercion(value, type) {
+ if (!ASM_JS) return value;
+ if (type == 'void') {
+ return value;
+ } else if (type in Runtime.FLOAT_TYPES) {
+ return '(+(' + value + '))';
+ } else {
+ return '((' + value + ')|0)';
+ }
+}
+
+function asmMultiplyI32(a, b) {
+ // special-case: there is no integer multiply in asm, because there is no true integer
+ // multiply in JS. While we wait for Math.imul, do double multiply
+ if (USE_MATH_IMUL) {
+ return 'Math.imul(' + a + ',' + b + ')';
+ }
+ return '(~~(+' + a + ' * +' + b + '))';
+}
+
+function makeGetTempDouble(i) { // TODO: Support other than i32
+ return makeGetValue('tempDoublePtr', Runtime.getNativeTypeSize('i32')*i, 'i32');
+}
+
// See makeSetValue
function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSafe) {
- noticePtr(ptr);
if (UNALIGNED_MEMORY) align = 1;
if (isStructType(type)) {
var typeData = Types.types[type];
@@ -952,9 +1051,9 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
}
if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double') {
- return '(tempDoubleI32[0]=' + makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align) + ',' +
- 'tempDoubleI32[1]=' + makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, unsigned, ignore, align) + ',' +
- 'tempDoubleF64[0])';
+ return '(HEAP32[tempDoublePtr>>2]=' + makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align) + ',' +
+ 'HEAP32[tempDoublePtr+4>>2]=' + makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, unsigned, ignore, align) + ',' +
+ 'HEAPF64[tempDoublePtr>>3])';
}
if (USE_TYPED_ARRAYS == 2 && align) {
@@ -977,9 +1076,9 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
}
} else {
if (type == 'float') {
- ret += 'copyTempFloat(' + getFastValue(ptr, '+', pos) + '),tempDoubleF32[0]';
+ ret += 'copyTempFloat(' + getFastValue(ptr, '+', pos) + '),HEAPF32[tempDoublePtr>>2]';
} else {
- ret += 'copyTempDouble(' + getFastValue(ptr, '+', pos) + '),tempDoubleF64[0]';
+ ret += 'copyTempDouble(' + getFastValue(ptr, '+', pos) + '),HEAP64[tempDoublePtr>>3]';
}
}
ret += ')';
@@ -993,14 +1092,24 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
if (type[0] === '#') type = type.substr(1);
return 'SAFE_HEAP_LOAD(' + offset + ', ' + type + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')';
} else {
- return makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type) + ']';
+ var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type) + ']';
+ if (ASM_JS && phase == 'funcs') {
+ ret = asmCoercion(ret, type);
+ }
+ return ret;
}
}
function indexizeFunctions(value, type) {
assert((type && type !== '?') || (typeof value === 'string' && value.substr(0, 6) === 'CHECK_'), 'No type given for function indexizing');
assert(value !== type, 'Type set to value');
- if (type && isFunctionType(type) && value[0] === '_') { // checking for _ differentiates from $ (local vars)
+ var out = {};
+ if (type && isFunctionType(type, out) && value[0] === '_') { // checking for _ differentiates from $ (local vars)
+ // add signature to library functions that we now know need indexing
+ if (!(value in Functions.implementedFunctions) && !(value in Functions.unimplementedFunctions)) {
+ Functions.unimplementedFunctions[value] = Functions.getSignature(out.returnType, out.segments ? out.segments.map(function(segment) { return segment[0].text }) : []);
+ }
+
if (BUILD_AS_SHARED_LIB) {
return '(FUNCTION_TABLE_OFFSET + ' + Functions.getIndex(value) + ')';
} else {
@@ -1021,7 +1130,6 @@ function indexizeFunctions(value, type) {
//! which means we should write to all slabs, ignore type differences if any on reads, etc.
//! @param noNeedFirst Whether to ignore the offset in the pointer itself.
function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) {
- noticePtr(ptr);
if (UNALIGNED_MEMORY && !forcedAlign) align = 1;
sep = sep || ';';
if (isStructType(type)) {
@@ -1039,9 +1147,9 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
}
if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double') {
- return '(tempDoubleF64[0]=' + value + ',' +
- makeSetValue(ptr, pos, 'tempDoubleI32[0]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' +
- makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'tempDoubleI32[1]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ')';
+ return '(HEAPF64[tempDoublePtr>>3]=' + value + ',' +
+ makeSetValue(ptr, pos, 'HEAP32[tempDoublePtr>>2]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' +
+ makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'HEAP32[tempDoublePtr+4>>2]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ')';
} else if (USE_TYPED_ARRAYS == 2 && type == 'i64') {
return '(tempI64 = [' + splitI64(value) + '],' +
makeSetValue(ptr, pos, 'tempI64[0]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' +
@@ -1093,7 +1201,6 @@ var SEEK_OPTIMAL_ALIGN_MIN = 20;
var UNROLL_LOOP_MAX = 8;
function makeSetValues(ptr, pos, value, type, num, align) {
- noticePtr(ptr);
function unroll(type, num, jump, value$) {
jump = jump || 1;
value$ = value$ || value;
@@ -1125,7 +1232,7 @@ function makeSetValues(ptr, pos, value, type, num, align) {
[4, 2, 1].forEach(function(possibleAlign) {
if (num == 0) return;
if (align >= possibleAlign) {
- if (num <= UNROLL_LOOP_MAX*possibleAlign) {
+ if (num <= UNROLL_LOOP_MAX*possibleAlign || ASM_JS) { // XXX test asm performance
ret.push(unroll('i' + (possibleAlign*8), Math.floor(num/possibleAlign), possibleAlign, values[possibleAlign]));
} else {
ret.push('for (var $$dest = ' + getFastValue(ptr, '+', pos) + (possibleAlign > 1 ? '>>' + log2(possibleAlign) : '') + ', ' +
@@ -1143,8 +1250,6 @@ function makeSetValues(ptr, pos, value, type, num, align) {
var TYPED_ARRAY_SET_MIN = Infinity; // .set() as memcpy seems to just slow us down
function makeCopyValues(dest, src, num, type, modifier, align, sep) {
- noticePtr(dest);
- noticePtr(src);
sep = sep || ';';
function unroll(type, num, jump) {
jump = jump || 1;
@@ -1171,7 +1276,7 @@ function makeCopyValues(dest, src, num, type, modifier, align, sep) {
} else { // USE_TYPED_ARRAYS == 2
// If we don't know how to handle this at compile-time, or handling it is best done in a large amount of code, call memset
if (!isNumber(num) || (align < 4 && parseInt(num) >= SEEK_OPTIMAL_ALIGN_MIN)) {
- return '_memcpy(' + dest + ', ' + src + ', ' + num + ', ' + align + ')';
+ return '_memcpy(' + dest + ', ' + src + ', ' + num + ')';
}
num = parseInt(num);
var ret = [];
@@ -1179,7 +1284,7 @@ function makeCopyValues(dest, src, num, type, modifier, align, sep) {
if (num == 0) return;
if (align >= possibleAlign) {
// If we can unroll the loop, do so. Also do so if we must unroll it (we do not create real loops when inlined)
- if (num <= UNROLL_LOOP_MAX*possibleAlign || sep == ',') {
+ if (num <= UNROLL_LOOP_MAX*possibleAlign || sep == ',' || ASM_JS) { // XXX test asm performance
ret.push(unroll('i' + (possibleAlign*8), Math.floor(num/possibleAlign), possibleAlign));
} else {
assert(sep == ';');
@@ -1245,6 +1350,9 @@ function getFastValue(a, op, b, type) {
return '(' + a + '<<' + shifts + ')';
}
}
+ if (ASM_JS && !(type in Runtime.FLOAT_TYPES)) {
+ return asmMultiplyI32(a, b); // unoptimized multiply, do it using asm.js's special multiply operation
+ }
} else {
if (a == '0') {
return '0';
@@ -1509,8 +1617,35 @@ function handleOverflow(text, bits) {
}
}
-function makeLLVMStruct(values) { // TODO: Use this everywhere
- return '{ ' + values.map(function(value, i) { return 'f' + i + ': ' + value }).join(', ') + ' }'
+function makeLLVMStruct(values) {
+ if (USE_TYPED_ARRAYS == 2) {
+ return 'DEPRECATED' + (new Error().stack) + 'XXX';
+ } else {
+ return '{ ' + values.map(function(value, i) { return 'f' + i + ': ' + value }).join(', ') + ' }'
+ }
+}
+
+function makeStructuralReturn(values) {
+ if (USE_TYPED_ARRAYS == 2) {
+ var i = 0;
+ return 'return (' + values.slice(1).map(function(value) {
+ return ASM_JS ? 'asm.setTempRet' + (i++) + '(' + value + ')'
+ : 'tempRet' + (i++) + ' = ' + value;
+ }).concat([values[0]]).join(',') + ')';
+ } else {
+ var i = 0;
+ return 'return { ' + values.map(function(value) {
+ return 'f' + (i++) + ': ' + value;
+ }).join(', ') + ' }';
+ }
+}
+
+function makeStructuralAccess(ident, i) {
+ if (USE_TYPED_ARRAYS == 2) {
+ return ident + '$' + i;
+ } else {
+ return ident + '.f' + i;
+ }
}
// From parseLLVMSegment
@@ -1537,6 +1672,7 @@ function finalizeLLVMParameter(param, noIndexizeFunctions) {
ret = parseI64Constant(ret);
}
ret = parseNumerical(ret, param.type);
+ ret = asmEnsureFloat(ret, param.type);
} else if (param.intertype == 'structvalue') {
ret = makeLLVMStruct(param.params.map(function(value) { return finalizeLLVMParameter(value, noIndexizeFunctions) }));
} else if (param.intertype === 'blockaddress') {
@@ -1619,30 +1755,36 @@ function makeSignOp(value, type, op, force, ignore) {
function makeRounding(value, bits, signed, floatConversion) {
// TODO: handle roundings of i64s
assert(bits);
- // C rounds to 0 (-5.5 to -5, +5.5 to 5), while JS has no direct way to do that.
- if (bits <= 32 && signed) return '((' + value + ')&-1)'; // This is fast and even correct, for all cases. Note that it is the same
- // as |0, but &-1 hints to the js optimizer that this is a rounding correction
- // Do Math.floor, which is reasonably fast, if we either don't care, or if we can be sure
- // the value is non-negative
- if (!correctRoundings() || (!signed && !floatConversion)) return 'Math.floor(' + value + ')';
- // We are left with >32 bits signed, or a float conversion. Check and correct inline
- // Note that if converting a float, we may have the wrong sign at this point! But, we have
- // been rounded properly regardless, and we will be sign-corrected later when actually used, if
- // necessary.
- return makeInlineCalculation('VALUE >= 0 ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR');
-/* refactored version - needs perf testing TODO
- if (bits <= 32) {
- if (signed) {
- return '((' + value + ')&-1)'; // &-1 (instead of |0) hints to the js optimizer that this is a rounding correction
- } else {
- return '((' + value + ')>>>0)';
+ if (!ASM_JS) {
+ // C rounds to 0 (-5.5 to -5, +5.5 to 5), while JS has no direct way to do that.
+ if (bits <= 32 && signed) return '((' + value + ')&-1)'; // This is fast and even correct, for all cases. Note that it is the same
+ // as |0, but &-1 hints to the js optimizer that this is a rounding correction
+ // Do Math.floor, which is reasonably fast, if we either don't care, or if we can be sure
+ // the value is non-negative
+ if (!correctRoundings() || (!signed && !floatConversion)) return 'Math.floor(' + value + ')';
+ // We are left with >32 bits signed, or a float conversion. Check and correct inline
+ // Note that if converting a float, we may have the wrong sign at this point! But, we have
+ // been rounded properly regardless, and we will be sign-corrected later when actually used, if
+ // necessary.
+ return makeInlineCalculation('VALUE >= 0 ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR');
+ } else {
+ // asm.js mode, cleaner refactoring of this function as well. TODO: use in non-asm case, most of this
+ if (floatConversion && bits <= 32) {
+ return '(~~(' + value + '))'; // explicit float-to-int conversion
+ }
+
+ if (bits <= 32) {
+ if (signed) {
+ return '((' + value + ')&-1)'; // &-1 (instead of |0) hints to the js optimizer that this is a rounding correction
+ } else {
+ return '((' + value + ')>>>0)';
+ }
}
+ // Math.floor is reasonably fast if we don't care about corrections (and even correct if unsigned)
+ if (!correctRoundings() || !signed) return 'Math.floor(' + value + ')';
+ // We are left with >32 bits
+ return makeInlineCalculation('VALUE >= 0 ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR');
}
- // Math.floor is reasonably fast if we don't care about corrections (and even correct if unsigned)
- if (!correctRoundings() || !signed) return 'Math.floor(' + value + ')';
- // We are left with >32 bits
- return makeInlineCalculation('VALUE >= 0 ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR');
-*/
}
// fptoui and fptosi are not in these, because we need to be careful about what we do there. We can't
@@ -1716,8 +1858,8 @@ function processMathop(item) {
}
function i64PreciseOp(type, lastArg) {
Types.preciseI64MathUsed = true;
- return finish(['(i64Math.' + type + '(' + low1 + ',' + high1 + ',' + low2 + ',' + high2 +
- (lastArg ? ',' + lastArg : '') + '),i64Math.result[0])', 'i64Math.result[1]']);
+ return finish(['(i64Math' + (ASM_JS ? '_' : '.') + type + '(' + low1 + ',' + high1 + ',' + low2 + ',' + high2 +
+ (lastArg ? ',' + lastArg : '') + '),' + makeGetValue('tempDoublePtr', 0, 'i32') + ')', makeGetValue('tempDoublePtr', Runtime.getNativeTypeSize('i32'), 'i32')]);
}
switch (op) {
// basic integer ops
@@ -1734,7 +1876,8 @@ function processMathop(item) {
case 'ashr':
case 'lshr': {
if (!isNumber(idents[1])) {
- return 'Runtime.bitshift64(' + idents[0] + '[0], ' + idents[0] + '[1],"' + op + '",' + stripCorrections(idents[1]) + '[0]|0)';
+ return '(Runtime' + (ASM_JS ? '_' : '.') + 'bitshift64(' + idents[0] + '[0], ' + idents[0] + '[1],"' + op + '",' + stripCorrections(idents[1]) + '[0]|0),' +
+ '[' + makeGetTempDouble(0) + ',' + makeGetTempDouble(1) + '])';
}
bits = parseInt(idents[1]);
var ander = Math.pow(2, bits)-1;
@@ -1851,15 +1994,15 @@ function processMathop(item) {
var outType = item.type;
if (inType in Runtime.INT_TYPES && outType in Runtime.FLOAT_TYPES) {
if (legalizedI64s) {
- return '(tempDoubleI32[0]=' + idents[0] + '$0, tempDoubleI32[1]=' + idents[0] + '$1, tempDoubleF64[0])';
+ return '(HEAP32[tempDoublePtr>>2]=' + idents[0] + '$0, HEAP32[tempDoublePtr+4>>2]=' + idents[0] + '$1, HEAPF64[tempDoublePtr>>3])';
} else {
- return makeInlineCalculation('tempDoubleI32[0]=VALUE[0],tempDoubleI32[1]=VALUE[1],tempDoubleF64[0]', idents[0], 'tempI64');
+ return makeInlineCalculation('HEAP32[tempDoublePtr>>2]=VALUE[0],HEAP32[tempDoublePtr+4>>2]=VALUE[1],HEAPF64[tempDoublePtr>>>3]', idents[0], 'tempI64');
}
} else if (inType in Runtime.FLOAT_TYPES && outType in Runtime.INT_TYPES) {
if (legalizedI64s) {
- return 'tempDoubleF64[0]=' + idents[0] + '; ' + finish(['tempDoubleI32[0]','tempDoubleI32[1]']);
+ return 'HEAPF64[tempDoublePtr>>3]=' + idents[0] + '; ' + finish(['HEAP32[tempDoublePtr>>2]','HEAP32[tempDoublePtr+4>>2]']);
} else {
- return '(tempDoubleF64[0]=' + idents[0] + ',[tempDoubleI32[0],tempDoubleI32[1]])';
+ return '(HEAPF64[tempDoublePtr>>3]=' + idents[0] + ',[HEAP32[tempDoublePtr>>2],HEAP32[tempDoublePtr+4>>2]])';
}
} else {
throw 'Invalid USE_TYPED_ARRAYS == 2 bitcast: ' + dump(item) + ' : ' + item.params[0].type;
@@ -1877,7 +2020,7 @@ function processMathop(item) {
case 'mul': {
if (bits == 32 && PRECISE_I32_MUL) {
Types.preciseI64MathUsed = true;
- return '(i64Math.multiply(' + idents[0] + ',0,' + idents[1] + ',0),i64Math.result[0])';
+ return '(i64Math' + (ASM_JS ? '_' : '.') + 'multiply(' + idents[0] + ',0,' + idents[1] + ',0),' + makeGetValue('tempDoublePtr', 0, 'i32') + ')';
} else {
return handleOverflow(getFastValue(idents[0], '*', idents[1], item.type), bits);
}
@@ -1927,7 +2070,7 @@ function processMathop(item) {
case 'fdiv': return getFastValue(idents[0], '/', idents[1], item.type);
case 'fmul': return getFastValue(idents[0], '*', idents[1], item.type);
case 'frem': return getFastValue(idents[0], '%', idents[1], item.type);
- case 'uitofp': case 'sitofp': return idents[0];
+ case 'uitofp': case 'sitofp': return asmCoercion(idents[0], 'double');
case 'fptoui': case 'fptosi': return makeRounding(idents[0], bitsLeft, op === 'fptosi', true);
// TODO: We sometimes generate false instead of 0, etc., in the *cmps. It seemed slightly faster before, but worth rechecking
@@ -1970,7 +2113,7 @@ function processMathop(item) {
// then unsigning that i32... which would give something huge.
case 'zext': case 'fpext': case 'sext': return idents[0];
case 'fptrunc': return idents[0];
- case 'select': return idents[0] + ' ? ' + idents[1] + ' : ' + idents[2];
+ case 'select': return idents[0] + ' ? ' + asmEnsureFloat(idents[1], item.type) + ' : ' + asmEnsureFloat(idents[2], item.type);
case 'ptrtoint': case 'inttoptr': {
var ret = '';
if (QUANTUM_SIZE == 1) {
@@ -1998,9 +2141,9 @@ function processMathop(item) {
(inType in Runtime.FLOAT_TYPES && outType in Runtime.INT_TYPES)) {
assert(USE_TYPED_ARRAYS == 2, 'Can only bitcast ints <-> floats with typed arrays mode 2');
if (inType in Runtime.INT_TYPES) {
- return '(tempDoubleI32[0] = ' + idents[0] + ',tempDoubleF32[0])';
+ return '(HEAP32[tempDoublePtr>>2] = ' + idents[0] + ',HEAPF32[tempDoublePtr>>2])';
} else {
- return '(tempDoubleF32[0] = ' + idents[0] + ',tempDoubleI32[0])';
+ return '(HEAPF32[tempDoublePtr>>2] = ' + idents[0] + ',HEAP32[tempDoublePtr>>2])';
}
}
return idents[0];
diff --git a/src/postamble.js b/src/postamble.js
index 144b5af9..5f541733 100644
--- a/src/postamble.js
+++ b/src/postamble.js
@@ -18,12 +18,16 @@ Module.callMain = function callMain(args) {
argv = allocate(argv, 'i32', ALLOC_STATIC);
#if CATCH_EXIT_CODE
+ var initialStackTop = STACKTOP;
try {
- return _main(argc, argv, 0);
+ return Module['_main'](argc, argv, 0);
}
catch(e) { if (e.name == "ExitStatus") return e.status; throw e; }
+ finally {
+ STACKTOP = initialStackTop;
+ }
#else
- return _main(argc, argv, 0);
+ return Module['_main'](argc, argv, 0);
#endif
}
diff --git a/src/preamble.js b/src/preamble.js
index 0917f977..cb01994f 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -275,7 +275,7 @@ Module['printProfiling'] = printProfiling;
// Runtime essentials
//========================================
-var __THREW__ = false; // Used in checking for thrown exceptions.
+var __THREW__ = 0; // Used in checking for thrown exceptions.
var setjmpId = 1; // Used in setjmp/longjmp
var setjmpLabels = {};
@@ -287,6 +287,7 @@ var undef = 0;
var tempValue, tempInt, tempBigInt, tempInt2, tempBigInt2, tempPair, tempBigIntI, tempBigIntR, tempBigIntS, tempBigIntP, tempBigIntD;
#if USE_TYPED_ARRAYS == 2
var tempI64, tempI64b;
+var tempRet0, tempRet1, tempRet2, tempRet3, tempRet4, tempRet5, tempRet6, tempRet7, tempRet8, tempRet9;
#endif
function abort(text) {
@@ -307,18 +308,10 @@ var globalScope = this;
// defined with extern "C").
//
// Note: LLVM optimizations can inline and remove functions, after which you will not be
-// able to call them. Adding
+// able to call them. Closure can also do so. To avoid that, add your function to
+// the exports using something like
//
-// __attribute__((used))
-//
-// to the function definition will prevent that.
-//
-// Note: Closure optimizations will minify function names, making
-// functions no longer callable. If you run closure (on by default
-// in -O2 and above), you should export the functions you will call
-// by calling emcc with something like
-//
-// -s EXPORTED_FUNCTIONS='["_func1","_func2"]'
+// -s EXPORTED_FUNCTIONS='["_main", "_myfunc"]'
//
// @param ident The name of the C function (note that C++ functions will be name-mangled - use extern "C")
// @param returnType The return type of the function, one of the JS types 'number', 'string' or 'array' (use 'number' for any C pointer, and
@@ -577,9 +570,6 @@ Module['Array_stringify'] = Array_stringify;
// Memory management
-var FUNCTION_TABLE; // XXX: In theory the indexes here can be equal to pointers to stacked or malloced memory. Such comparisons should
- // be false, but can turn out true. We should probably set the top bit to prevent such issues.
-
var PAGE_SIZE = 4096;
function alignMemoryPage(x) {
return ((x+4095)>>12)<<12;
@@ -601,7 +591,7 @@ var STATICTOP;
#if USE_TYPED_ARRAYS
function enlargeMemory() {
#if ALLOW_MEMORY_GROWTH == 0
- abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ( ' + TOTAL_MEMORY + '), (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.');
+ abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value, (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.');
#else
// TOTAL_MEMORY is the current size of the actual array, and STATICTOP is the new top.
#if ASSERTIONS
@@ -699,47 +689,47 @@ Module['HEAPF64'] = HEAPF64;
#endif
STACK_ROOT = STACKTOP = Runtime.alignMemory(1);
-STACK_MAX = STACK_ROOT + TOTAL_STACK;
+STACK_MAX = TOTAL_STACK; // we lose a little stack here, but TOTAL_STACK is nice and round so use that as the max
#if USE_TYPED_ARRAYS == 2
-var tempDoublePtr = Runtime.alignMemory(STACK_MAX, 8);
-var tempDoubleI8 = HEAP8.subarray(tempDoublePtr);
-var tempDoubleI32 = HEAP32.subarray(tempDoublePtr >> 2);
-var tempDoubleF32 = HEAPF32.subarray(tempDoublePtr >> 2);
-var tempDoubleF64 = HEAPF64.subarray(tempDoublePtr >> 3);
+var tempDoublePtr = Runtime.alignMemory(allocate(12, 'i8', ALLOC_STACK), 8);
+assert(tempDoublePtr % 8 == 0);
function copyTempFloat(ptr) { // functions, because inlining this code is increases code size too much
- tempDoubleI8[0] = HEAP8[ptr];
- tempDoubleI8[1] = HEAP8[ptr+1];
- tempDoubleI8[2] = HEAP8[ptr+2];
- tempDoubleI8[3] = HEAP8[ptr+3];
+ HEAP8[tempDoublePtr] = HEAP8[ptr];
+ HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];
+ HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];
+ HEAP8[tempDoublePtr+3] = HEAP8[ptr+3];
}
function copyTempDouble(ptr) {
- tempDoubleI8[0] = HEAP8[ptr];
- tempDoubleI8[1] = HEAP8[ptr+1];
- tempDoubleI8[2] = HEAP8[ptr+2];
- tempDoubleI8[3] = HEAP8[ptr+3];
- tempDoubleI8[4] = HEAP8[ptr+4];
- tempDoubleI8[5] = HEAP8[ptr+5];
- tempDoubleI8[6] = HEAP8[ptr+6];
- tempDoubleI8[7] = HEAP8[ptr+7];
+ HEAP8[tempDoublePtr] = HEAP8[ptr];
+ HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];
+ HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];
+ HEAP8[tempDoublePtr+3] = HEAP8[ptr+3];
+ HEAP8[tempDoublePtr+4] = HEAP8[ptr+4];
+ HEAP8[tempDoublePtr+5] = HEAP8[ptr+5];
+ HEAP8[tempDoublePtr+6] = HEAP8[ptr+6];
+ HEAP8[tempDoublePtr+7] = HEAP8[ptr+7];
}
-STACK_MAX = tempDoublePtr + 8;
#endif
-STATICTOP = alignMemoryPage(STACK_MAX);
-
+STATICTOP = STACK_MAX;
assert(STATICTOP < TOTAL_MEMORY); // Stack must fit in TOTAL_MEMORY; allocations from here on may enlarge TOTAL_MEMORY
-var nullString = allocate(intArrayFromString('(null)'), 'i8', ALLOC_STATIC);
+var nullString = allocate(intArrayFromString('(null)'), 'i8', ALLOC_STACK);
function callRuntimeCallbacks(callbacks) {
while(callbacks.length > 0) {
var callback = callbacks.shift();
var func = callback.func;
if (typeof func === 'number') {
- func = FUNCTION_TABLE[func];
+ if (callback.arg === undefined) {
+ Runtime.dynCall('v', func);
+ } else {
+ Runtime.dynCall('vi', func, [callback.arg]);
+ }
+ } else {
+ func(callback.arg === undefined ? null : callback.arg);
}
- func(callback.arg === undefined ? null : callback.arg);
}
}
@@ -881,7 +871,8 @@ function removeRunDependency(id) {
clearInterval(runDependencyWatcher);
runDependencyWatcher = null;
}
- if (!calledRun) run();
+ // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false)
+ if (!calledRun && shouldRunNow) run();
}
}
Module['removeRunDependency'] = removeRunDependency;
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index 933fda96..ae8577b1 100644
--- a/src/relooper/Relooper.cpp
+++ b/src/relooper/Relooper.cpp
@@ -58,6 +58,8 @@ void PutIndented(const char *String) {
*OutputBuffer = 0;
}
+static int AsmJS = 0;
+
// Indenter
#if EMSCRIPTEN
@@ -271,7 +273,11 @@ void MultipleShape::Render(bool InLoop) {
RenderLoopPrefix();
bool First = true;
for (BlockShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) {
- PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->first->Id);
+ if (AsmJS) {
+ PrintIndented("%sif ((label|0) == %d) {\n", First ? "" : "else ", iter->first->Id);
+ } else {
+ PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->first->Id);
+ }
First = false;
Indenter::Indent();
iter->second->Render(InLoop);
@@ -977,6 +983,10 @@ void Relooper::MakeOutputBuffer(int Size) {
OutputBufferSize = Size;
}
+void Relooper::SetAsmJSMode(int On) {
+ AsmJS = On;
+}
+
#if DEBUG
// Debugging
@@ -1018,6 +1028,10 @@ void rl_make_output_buffer(int size) {
Relooper::SetOutputBuffer((char*)malloc(size), size);
}
+void rl_set_asm_js_mode(int on) {
+ Relooper::SetAsmJSMode(on);
+}
+
void *rl_new_block(const char *text) {
Block *ret = new Block(text);
#if DEBUG
diff --git a/src/relooper/Relooper.h b/src/relooper/Relooper.h
index 08ac8e40..eac03738 100644
--- a/src/relooper/Relooper.h
+++ b/src/relooper/Relooper.h
@@ -200,6 +200,9 @@ struct Relooper {
// Creates an output buffer. Must call this or SetOutputBuffer.
static void MakeOutputBuffer(int Size);
+
+ // Sets asm.js mode on or off (default is off)
+ static void SetAsmJSMode(int On);
};
typedef std::set<Block*> BlockSet;
@@ -231,6 +234,7 @@ extern "C" {
RELOOPERDLL_API void rl_set_output_buffer(char *buffer, int size);
RELOOPERDLL_API void rl_make_output_buffer(int size);
+RELOOPERDLL_API void rl_set_asm_js_mode(int on);
RELOOPERDLL_API void *rl_new_block(const char *text);
RELOOPERDLL_API void rl_delete_block(void *block);
RELOOPERDLL_API void rl_block_add_branch_to(void *from, void *to, const char *condition, const char *code);
diff --git a/src/relooper/emscripten/Makefile b/src/relooper/emscripten/Makefile
deleted file mode 100644
index 277dd538..00000000
--- a/src/relooper/emscripten/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-EMSCRIPTEN = ~/Dev/emscripten
-EMCC = $(EMSCRIPTEN)/emcc
-BINDINGS_GENERATOR = $(EMSCRIPTEN)/tools/bindings_generator.py
-NATIVIZER = $(EMSCRIPTEN)/tools/nativize_llvm.py
-
-all: relooper.js
-
-relooper.js:
- $(EMCC) -O2 ../Relooper.cpp -I.. --post-js glue.js -o relooper.raw.js -s TOTAL_MEMORY=52428800 -s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE='["memcpy", "memset", "malloc", "free", "puts"]'
- echo "// Relooper, (C) 2012 Alon Zakai, MIT license, https://github.com/kripken/Relooper" > relooper.js
- echo "var Relooper = (function() {" >> relooper.js
- cat relooper.raw.js >> relooper.js
- echo " return Module.Relooper;" >> relooper.js
- echo "})();" >> relooper.js
-
-clean:
- rm relooper.js
-
diff --git a/src/relooper/emscripten/glue.js b/src/relooper/emscripten/glue.js
index 15998acf..36922185 100644
--- a/src/relooper/emscripten/glue.js
+++ b/src/relooper/emscripten/glue.js
@@ -49,6 +49,9 @@
RelooperGlue['setDebug'] = function(debug) {
_rl_set_debugging(+!!debug);
};
+ RelooperGlue['setAsmJSMode'] = function(on) {
+ _rl_set_asm_js_mode(on);
+ };
Module['Relooper'] = RelooperGlue;
diff --git a/src/relooper/test.txt b/src/relooper/test.txt
index 12d0ef39..b7c8794d 100644
--- a/src/relooper/test.txt
+++ b/src/relooper/test.txt
@@ -54,7 +54,7 @@ while(1) {
// code 2
if (!($2)) {
var $x_1 = $x_0;
- label = 18;
+ label = 19;
break;
}
// code 3
@@ -64,7 +64,7 @@ while(1) {
var $i_0 = $7;var $x_0 = $5;
}
}
-if (label == 18) {
+if (label == 19) {
// code 7
}
// code 4
diff --git a/src/runtime.js b/src/runtime.js
index dd14e779..9d5e5e1f 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -13,7 +13,7 @@ var RuntimeGenerator = {
if (init) {
ret += sep + '_memset(' + type + 'TOP, 0, ' + size + ')';
}
- ret += sep + type + 'TOP += ' + size;
+ ret += sep + type + 'TOP = (' + type + 'TOP + ' + size + ')|0';
if ({{{ QUANTUM_SIZE }}} > 1 && !ignoreAlign) {
ret += sep + RuntimeGenerator.alignMemory(type + 'TOP', {{{ QUANTUM_SIZE }}});
}
@@ -23,27 +23,27 @@ var RuntimeGenerator = {
// An allocation that lives as long as the current function call
stackAlloc: function(size, sep) {
sep = sep || ';';
- if (USE_TYPED_ARRAYS === 2) 'STACKTOP += STACKTOP % ' + ({{{ QUANTUM_SIZE }}} - (isNumber(size) ? Math.min(size, {{{ QUANTUM_SIZE }}}) : {{{ QUANTUM_SIZE }}})) + sep;
+ if (USE_TYPED_ARRAYS === 2) 'STACKTOP = (STACKTOP + STACKTOP|0 % ' + ({{{ QUANTUM_SIZE }}} - (isNumber(size) ? Math.min(size, {{{ QUANTUM_SIZE }}}) : {{{ QUANTUM_SIZE }}})) + ')' + sep;
// The stack is always QUANTUM SIZE aligned, so we may not need to force alignment here
var ret = RuntimeGenerator.alloc(size, 'STACK', INIT_STACK, sep, USE_TYPED_ARRAYS != 2 || (isNumber(size) && parseInt(size) % {{{ QUANTUM_SIZE }}} == 0));
if (ASSERTIONS) {
- ret += sep + 'assert(STACKTOP < STACK_ROOT + STACK_MAX, "Ran out of stack")';
+ ret += sep + 'assert(STACKTOP|0 < STACK_MAX|0)';
}
return ret;
},
stackEnter: function(initial, force) {
if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return '';
- var ret = 'var __stackBase__ = STACKTOP';
- if (initial > 0) ret += '; STACKTOP += ' + initial;
+ var ret = 'var __stackBase__ = ' + (ASM_JS ? '0; __stackBase__ = ' : '') + 'STACKTOP';
+ if (initial > 0) ret += '; STACKTOP = (STACKTOP + ' + initial + ')|0';
if (USE_TYPED_ARRAYS == 2) {
assert(initial % QUANTUM_SIZE == 0);
if (ASSERTIONS) {
- ret += '; assert(STACKTOP % {{{ QUANTUM_SIZE }}} == 0, "Stack is unaligned")';
+ ret += '; assert(STACKTOP|0 % {{{ QUANTUM_SIZE }}} == 0)';
}
}
if (ASSERTIONS) {
- ret += '; assert(STACKTOP < STACK_MAX, "Ran out of stack")';
+ ret += '; assert(STACKTOP < STACK_MAX)';
}
if (INIT_STACK) {
ret += '; _memset(__stackBase__, 0, ' + initial + ')';
@@ -124,36 +124,50 @@ var Runtime = {
// Mirrors processMathop's treatment of constants (which we optimize directly)
bitshift64: function(low, high, op, bits) {
+ var ret;
var ander = Math.pow(2, bits)-1;
if (bits < 32) {
switch (op) {
case 'shl':
- return [low << bits, (high << bits) | ((low&(ander << (32 - bits))) >>> (32 - bits))];
+ ret = [low << bits, (high << bits) | ((low&(ander << (32 - bits))) >>> (32 - bits))];
+ break;
case 'ashr':
- return [(((low >>> bits ) | ((high&ander) << (32 - bits))) >> 0) >>> 0, (high >> bits) >>> 0];
+ ret = [(((low >>> bits ) | ((high&ander) << (32 - bits))) >> 0) >>> 0, (high >> bits) >>> 0];
+ break;
case 'lshr':
- return [((low >>> bits) | ((high&ander) << (32 - bits))) >>> 0, high >>> bits];
+ ret = [((low >>> bits) | ((high&ander) << (32 - bits))) >>> 0, high >>> bits];
+ break;
}
} else if (bits == 32) {
switch (op) {
case 'shl':
- return [0, low];
+ ret = [0, low];
+ break;
case 'ashr':
- return [high, (high|0) < 0 ? ander : 0];
+ ret = [high, (high|0) < 0 ? ander : 0];
+ break;
case 'lshr':
- return [high, 0];
+ ret = [high, 0];
+ break;
}
} else { // bits > 32
switch (op) {
case 'shl':
- return [0, low << (bits - 32)];
+ ret = [0, low << (bits - 32)];
+ break;
case 'ashr':
- return [(high >> (bits - 32)) >>> 0, (high|0) < 0 ? ander : 0];
+ ret = [(high >> (bits - 32)) >>> 0, (high|0) < 0 ? ander : 0];
+ break;
case 'lshr':
- return [high >>> (bits - 32) , 0];
+ ret = [high >>> (bits - 32) , 0];
+ break;
}
}
- abort('unknown bitshift64 op: ' + [value, op, bits]);
+#if ASSERTIONS
+ assert(ret);
+#endif
+ HEAP32[tempDoublePtr>>2] = ret[0]; // cannot use utility functions since we are in runtime itself
+ HEAP32[tempDoublePtr+4>>2] = ret[1];
},
// Imprecise bitops utilities
@@ -311,10 +325,35 @@ var Runtime = {
return ret;
},
- addFunction: function(func) {
- var ret = FUNCTION_TABLE.length;
- FUNCTION_TABLE.push(func);
- FUNCTION_TABLE.push(0);
+ dynCall: function(sig, ptr, args) {
+ if (args && args.length) {
+#if ASSERTIONS
+ assert(args.length == sig.length-1);
+#endif
+#if ASM_JS
+ args.splice(0, 0, ptr);
+ return Module['dynCall_' + sig].apply(null, args);
+#else
+ return FUNCTION_TABLE[ptr].apply(null, args);
+#endif
+ } else {
+#if ASSERTIONS
+ assert(sig.length == 1);
+#endif
+#if ASM_JS
+ return Module['dynCall_' + sig].call(null, ptr);
+#else
+ return FUNCTION_TABLE[ptr]();
+#endif
+ }
+ },
+
+ addFunction: function(func, sig) {
+ assert(sig);
+ var table = FUNCTION_TABLE; // TODO: support asm
+ var ret = table.length;
+ table.push(func);
+ table.push(0);
return ret;
},
@@ -328,10 +367,11 @@ var Runtime = {
funcWrappers: {},
- getFuncWrapper: function(func) {
+ getFuncWrapper: function(func, sig) {
+ assert(sig);
if (!Runtime.funcWrappers[func]) {
Runtime.funcWrappers[func] = function() {
- FUNCTION_TABLE[func].apply(null, arguments);
+ Runtime.dynCall(sig, func, arguments);
};
}
return Runtime.funcWrappers[func];
diff --git a/src/settings.js b/src/settings.js
index c4d1df48..8ae287f9 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -43,7 +43,7 @@ var TOTAL_STACK = 5*1024*1024; // The total stack size. There is no way to enlar
// value must be large enough for the program's requirements. If
// assertions are on, we will assert on not exceeding this, otherwise,
// it will fail silently.
-var TOTAL_MEMORY = 10*1024*1024; // The total amount of memory to use. Using more memory than this will
+var TOTAL_MEMORY = 16777216; // The total amount of memory to use. Using more memory than this will
// cause us to expand the heap, which can be costly with typed arrays:
// we need to copy the old heap into a new one in that case.
var FAST_MEMORY = 2*1024*1024; // The amount of memory to initialize to 0. This ensures it will be
@@ -118,7 +118,9 @@ var INLINING_LIMIT = 50; // A limit on inlining. If 0, we will inline normally i
var CATCH_EXIT_CODE = 0; // If set, causes exit() to throw an exception object which is caught
// in a try..catch block and results in the exit status being
// returned from run(). If zero (the default), the program is just
- // terminated with an error message.
+ // terminated with an error message, that is, the exception thrown
+ // by exit() is not handled in any way (in particular, the stack
+ // position will not be reset).
// Generated code debugging options
var SAFE_HEAP = 0; // Check each write to the heap, for example, this will give a clear
@@ -138,7 +140,7 @@ var LABEL_FUNCTION_FILTERS = []; // Filters for function label debug.
// labels of functions that is equaled to
// one of the filters are printed out
// When the array is empty, the filter is disabled.
-var EXCEPTION_DEBUG = 1; // Print out exceptions in emscriptened code
+var EXCEPTION_DEBUG = 0; // Print out exceptions in emscriptened code
var LIBRARY_DEBUG = 0; // Print out when we enter a library call (library*.js). You can also unset
// Runtime.debug at runtime for logging to cease, and can set it when you
@@ -200,7 +202,7 @@ var PGO = 0; // Profile-guided optimization.
// All CORRECT_* options default to 1 with PGO builds.
// See https://github.com/kripken/emscripten/wiki/Optimizing-Code for more info
-var NAMED_GLOBALS = 1; // If 1, we use global variables for globals. Otherwise
+var NAMED_GLOBALS = 0; // If 1, we use global variables for globals. Otherwise
// they are referred to by a base plus an offset (called an indexed global),
// saving global variables but adding runtime overhead.
@@ -220,6 +222,12 @@ var DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = ['memcpy', 'memset', 'malloc', 'free', '$
// add it here (and in EXPORTED FUNCTIONS with prefix
// "_", for closure).
+var LIBRARY_DEPS_TO_AUTOEXPORT = ['memcpy']; // This list is also used to determine
+ // auto-exporting of library dependencies (i.e., functions that
+ // might be dependencies of JS library functions, that if
+ // so we must export so that if they are implemented in C
+ // they will be accessible, in ASM_JS mode).
+
var IGNORED_FUNCTIONS = []; // Functions that we should not generate, neither a stub nor a complete function.
// This is useful if your project code includes a function, and you want to replace
// that in the compiled code with your own handwritten JS. (Of course even without
@@ -301,6 +309,11 @@ var HEADLESS = 0; // If 1, will include shim code that tries to 'fake' a browser
// very partial - it is hard to fake a whole browser! - so
// keep your expectations low for this to work.
+var ASM_JS = 0; // If 1, generate code in asm.js format. XXX This is highly experimental,
+ // and will not work on most codebases yet. It is NOT recommended that you
+ // try this yet.
+var USE_MATH_IMUL = 0; // If 1, use Math.imul when useful
+
var NECESSARY_BLOCKADDRS = []; // List of (function, block) for all block addresses that are taken.
// Compiler debugging options
@@ -1186,6 +1199,8 @@ var C_DEFINES = {'SI_MESGQ': '5',
'_SC_TTY_NAME_MAX': '41',
'AF_INET': '1',
'AF_INET6': '6',
- 'FIONREAD': '1'
+ 'FIONREAD': '1',
+ 'SOCK_STREAM': '200',
+ 'IPPROTO_TCP': 1
};
diff --git a/src/utility.js b/src/utility.js
index 84b50ce9..63582ae8 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -321,6 +321,12 @@ function isPowerOfTwo(x) {
return x > 0 && ((x & (x-1)) == 0);
}
+function ceilPowerOfTwo(x) {
+ var ret = 1;
+ while (ret < x) ret <<= 1;
+ return ret;
+}
+
function Benchmarker() {
var starts = {}, times = {}, counts = {};
this.start = function(id) {