blob: 31e6203a5c9e803a15833c58819266ce3ecc6cab [file] [log] [blame] [edit]
//"use strict";
// Implementation details for the 'runtime environment' we generate in
// JavaScript. The Runtime object itself is used both during compilation,
// and is available at runtime (dynamic compilation). The RuntimeGenerator
// helps to create the Runtime object (written so that the Runtime object
// itself is as optimized as possible - no unneeded runtime checks).
var RuntimeGenerator = {
alloc: function(size, type, sep, ignoreAlign) {
sep = sep || ';';
var ret = type + 'TOP';
ret += sep + type + 'TOP = (' + type + 'TOP + ' + size + ')|0';
if ({{{ STACK_ALIGN }}} > 1 && !ignoreAlign) {
ret += sep + RuntimeGenerator.alignMemory(type + 'TOP', {{{ STACK_ALIGN }}});
}
return ret;
},
// An allocation that lives as long as the current function call
stackAlloc: function(size, sep) {
sep = sep || ';';
var ret = RuntimeGenerator.alloc(size, 'STACK', sep, (isNumber(size) && parseInt(size) % {{{ STACK_ALIGN }}} == 0));
if (ASSERTIONS) {
ret += sep + '(assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')|0)';
}
return ret;
},
stackEnter: function(initial, force) {
if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return '';
var ret = 'var sp=0;sp=STACKTOP';
if (initial > 0) ret += ';STACKTOP=(STACKTOP+' + initial + ')|0';
assert(initial % Runtime.STACK_ALIGN == 0);
if (ASSERTIONS && Runtime.STACK_ALIGN == 4) {
ret += '; (assert(' + asmCoercion('!(STACKTOP&3)', 'i32') + ')|0)';
}
if (ASSERTIONS) {
ret += '; (assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')|0)';
}
return ret;
},
stackExit: function(initial, force) {
if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return '';
return 'STACKTOP=sp';
},
// An allocation that cannot normally be free'd (except through sbrk, which once
// called, takes control of STATICTOP)
staticAlloc: function(size) {
#if USE_PTHREADS
if (typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD) throw 'Runtime.staticAlloc is not available in pthreads!'; // This is because each worker has its own copy of STATICTOP, of which main thread is authoritative.
#endif
// After we seal static memory
return 'STATICTOP; if (staticSealed) return Runtime.stackAlloc(' + size + '); STATICTOP = (STATICTOP + size)|0;STATICTOP = (((STATICTOP)+15)&-16)';
},
forceAlign: function(target, quantum) {
quantum = quantum || {{{ QUANTUM_SIZE }}};
if (quantum == 1) return target;
if (isNumber(target) && isNumber(quantum)) {
return Math.ceil(target/quantum)*quantum;
} else if (isNumber(quantum) && isPowerOfTwo(quantum)) {
return '(((' +target + ')+' + (quantum-1) + ')&' + -quantum + ')';
}
return 'Math.ceil((' + target + ')/' + quantum + ')*' + quantum;
},
alignMemory: function(target, quantum) {
if (typeof quantum !== 'number') {
quantum = '(quantum ? quantum : {{{ STACK_ALIGN }}})';
}
return target + ' = ' + RuntimeGenerator.forceAlign(target, quantum);
},
// Given two 32-bit unsigned parts of an emulated 64-bit number, combine them into a JS number (double).
// Rounding is inevitable if the number is large. This is a particular problem for small negative numbers
// (-1 will be rounded!), so handle negatives separately and carefully
makeBigInt: function(low, high, unsigned) {
var unsignedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'double') + '+(' + asmCoercion(makeSignOp(high, 'i32', 'un', 1, 1), 'double') + '*' + asmEnsureFloat(4294967296, 'double') + '))';
var signedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'double') + '+(' + asmCoercion(makeSignOp(high, 'i32', 're', 1, 1), 'double') + '*' + asmEnsureFloat(4294967296, 'double') + '))';
if (typeof unsigned === 'string') return '(' + unsigned + ' ? ' + unsignedRet + ' : ' + signedRet + ')';
return unsigned ? unsignedRet : signedRet;
}
};
function unInline(name_, params) {
var src = '(function(' + params + ') { var ret = ' + RuntimeGenerator[name_].apply(null, params) + '; return ret; })';
var ret = eval(src);
return ret;
}
var Compiletime = {
isPointerType: isPointerType,
isStructType: isStructType,
isNumberType: function(type) {
return type in Compiletime.INT_TYPES || type in Compiletime.FLOAT_TYPES;
},
INT_TYPES: set('i1', 'i8', 'i16', 'i32', 'i64'),
FLOAT_TYPES: set('float', 'double'),
};
var Runtime = {
// When a 64 bit long is returned from a compiled function the least significant
// 32 bit word is passed in the return value, but the most significant 32 bit
// word is placed in tempRet0. This provides an accessor for that value.
setTempRet0: function(value) {
tempRet0 = value;
},
getTempRet0: function() {
return tempRet0;
},
stackSave: function() {
return STACKTOP;
},
stackRestore: function(stackTop) {
STACKTOP = stackTop;
},
//! Returns the size of a type, as C/C++ would have it (in 32-bit), in bytes.
//! @param type The type, by name.
getNativeTypeSize: function(type) {
switch (type) {
case 'i1': case 'i8': return 1;
case 'i16': return 2;
case 'i32': return 4;
case 'i64': return 8;
case 'float': return 4;
case 'double': return 8;
default: {
if (type[type.length-1] === '*') {
return Runtime.QUANTUM_SIZE; // A pointer
} else if (type[0] === 'i') {
var bits = parseInt(type.substr(1));
assert(bits % 8 === 0);
return bits/8;
} else {
return 0;
}
}
}
},
//! Returns the size of a structure field, as C/C++ would have it (in 32-bit,
//! for now).
//! @param type The type, by name.
getNativeFieldSize: function(type) {
return Math.max(Runtime.getNativeTypeSize(type), Runtime.QUANTUM_SIZE);
},
STACK_ALIGN: {{{ STACK_ALIGN }}},
// This must be called before reading a double or i64 vararg. It will bump the pointer properly.
// It also does an assert on i32 values, so it's nice to call it before all varargs calls.
prepVararg: function(ptr, type) {
if (type === 'double' || type === 'i64') {
// move so the load is aligned
if (ptr & 7) {
assert((ptr & 7) === 4);
ptr += 4;
}
} else {
assert((ptr & 3) === 0);
}
return ptr;
},
// type can be a native type or a struct (or null, for structs we only look at size here)
getAlignSize: function(type, size, vararg) {
// we align i64s and doubles on 64-bit boundaries, unlike x86
if (!vararg && (type == 'i64' || type == 'double')) return 8;
if (!type) return Math.min(size, 8); // align structures internally to 64 bits
return Math.min(size || (type ? Runtime.getNativeFieldSize(type) : 0), Runtime.QUANTUM_SIZE);
},
dynCall: function(sig, ptr, args) {
if (args && args.length) {
#if ASSERTIONS
assert(args.length == sig.length-1);
#endif
if (!args.splice) args = Array.prototype.slice.call(args);
args.splice(0, 0, ptr);
#if ASSERTIONS
assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\'');
#endif
return Module['dynCall_' + sig].apply(null, args);
} else {
#if ASSERTIONS
assert(sig.length == 1);
#endif
#if ASSERTIONS
assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\'');
#endif
return Module['dynCall_' + sig].call(null, ptr);
}
},
#if EMULATED_FUNCTION_POINTERS
getFunctionTables: function(module) {
if (!module) module = Module;
var tables = {};
for (var t in module) {
if (/^FUNCTION_TABLE_.*/.test(t)) {
var table = module[t];
if (typeof table === 'object') tables[t.substr('FUNCTION_TABLE_'.length)] = table;
}
}
return tables;
},
alignFunctionTables: function(module) {
var tables = Runtime.getFunctionTables(module);
var maxx = 0;
for (var sig in tables) {
maxx = Math.max(maxx, tables[sig].length);
}
assert(maxx >= 0);
for (var sig in tables) {
var table = tables[sig];
while (table.length < maxx) table.push(0);
}
return maxx;
},
registerFunctions: function(sigs, newModule) {
sigs.forEach(function(sig) {
if (!Module['FUNCTION_TABLE_' + sig]) {
Module['FUNCTION_TABLE_' + sig] = [];
}
});
var oldMaxx = Runtime.alignFunctionTables(); // align the new tables we may have just added
var newMaxx = Runtime.alignFunctionTables(newModule);
var maxx = oldMaxx + newMaxx;
sigs.forEach(function(sig) {
var newTable = newModule['FUNCTION_TABLE_' + sig];
var oldTable = Module['FUNCTION_TABLE_' + sig];
assert(newTable !== oldTable);
assert(oldTable.length === oldMaxx);
for (var i = 0; i < newTable.length; i++) {
oldTable.push(newTable[i]);
}
assert(oldTable.length === maxx);
});
assert(maxx === Runtime.alignFunctionTables()); // align the ones we didn't touch
},
#endif
functionPointers: new Array(RESERVED_FUNCTION_POINTERS),
addFunction: function(func) {
#if EMULATED_FUNCTION_POINTERS == 0
for (var i = 0; i < Runtime.functionPointers.length; i++) {
if (!Runtime.functionPointers[i]) {
Runtime.functionPointers[i] = func;
return {{{ FUNCTION_POINTER_ALIGNMENT }}}*(1 + i);
}
}
throw 'Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.';
#else
Runtime.alignFunctionTables(); // XXX we should rely on this being an invariant
var tables = Runtime.getFunctionTables();
var ret = -1;
for (var sig in tables) {
var table = tables[sig];
if (ret < 0) ret = table.length;
else assert(ret === table.length);
table.push(func);
}
return ret;
#endif
},
removeFunction: function(index) {
#if EMULATED_FUNCTION_POINTERS == 0
Runtime.functionPointers[(index-{{{ FUNCTION_POINTER_ALIGNMENT }}})/{{{ FUNCTION_POINTER_ALIGNMENT }}}] = null;
#else
Runtime.alignFunctionTables(); // XXX we should rely on this being an invariant
var tables = Runtime.getFunctionTables();
for (var sig in tables) {
tables[sig][index] = null;
}
#endif
},
#if RELOCATABLE
loadedDynamicLibraries: [],
loadDynamicLibrary: function(lib) {
// TODO: addRunDep etc., do asynchronously when in the browser. for now we assume we can do a sync xhr, no mem init files in libs, and we ignore the sync xhr lag
var src = Module['read'](lib);
var libModule = eval(src)(
Runtime.alignFunctionTables(),
Module
);
// add symbols into global namespace TODO: weak linking etc.
for (var sym in libModule) {
if (!Module.hasOwnProperty(sym)) {
Module[sym] = libModule[sym];
}
#if ASSERTIONS == 2
else if (sym[0] === '_') {
var curr = Module[sym], next = libModule[sym];
// don't warn on functions - might be odr, linkonce_odr, etc.
if (!(typeof curr === 'function' && typeof next === 'function')) {
Module.printErr("warning: trying to dynamically load symbol '" + sym + "' (from '" + lib + "') that already exists (duplicate symbol? or weak linking, which isn't supported yet?)"); // + [curr, ' vs ', next]);
}
}
#endif
}
Runtime.loadedDynamicLibraries.push(libModule);
},
#endif
warnOnce: function(text) {
if (!Runtime.warnOnce.shown) Runtime.warnOnce.shown = {};
if (!Runtime.warnOnce.shown[text]) {
Runtime.warnOnce.shown[text] = 1;
Module.printErr(text);
}
},
funcWrappers: {},
getFuncWrapper: function(func, sig) {
assert(sig);
if (!Runtime.funcWrappers[sig]) {
Runtime.funcWrappers[sig] = {};
}
var sigCache = Runtime.funcWrappers[sig];
if (!sigCache[func]) {
sigCache[func] = function dynCall_wrapper() {
return Runtime.dynCall(sig, func, arguments);
};
}
return sigCache[func];
},
#if RETAIN_COMPILER_SETTINGS
compilerSettings: {},
#endif
getCompilerSetting: function(name) {
#if RETAIN_COMPILER_SETTINGS == 0
throw 'You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work';
#else
if (!(name in Runtime.compilerSettings)) return 'invalid compiler setting: ' + name;
return Runtime.compilerSettings[name];
#endif
},
#if RUNTIME_DEBUG
debug: true, // Switch to false at runtime to disable logging at the right times
printObjectList: [],
prettyPrint: function(arg) {
if (typeof arg == 'undefined') return '!UNDEFINED!';
if (typeof arg == 'boolean') arg = arg + 0;
if (!arg) return arg;
var index = Runtime.printObjectList.indexOf(arg);
if (index >= 0) return '<' + arg + '|' + index + '>';
if (arg.toString() == '[object HTMLImageElement]') {
return arg + '\n\n';
}
if (arg.byteLength) {
return '{' + Array.prototype.slice.call(arg, 0, Math.min(arg.length, 400)) + '}'; // Useful for correct arrays, less so for compiled arrays, see the code below for that
var buf = new ArrayBuffer(32);
var i8buf = new Int8Array(buf);
var i16buf = new Int16Array(buf);
var f32buf = new Float32Array(buf);
switch(arg.toString()) {
case '[object Uint8Array]':
i8buf.set(arg.subarray(0, 32));
break;
case '[object Float32Array]':
f32buf.set(arg.subarray(0, 5));
break;
case '[object Uint16Array]':
i16buf.set(arg.subarray(0, 16));
break;
default:
alert('unknown array for debugging: ' + arg);
throw 'see alert';
}
var ret = '{' + arg.byteLength + ':\n';
var arr = Array.prototype.slice.call(i8buf);
ret += 'i8:' + arr.toString().replace(/,/g, ',') + '\n';
arr = Array.prototype.slice.call(f32buf, 0, 8);
ret += 'f32:' + arr.toString().replace(/,/g, ',') + '}';
return ret;
}
if (typeof arg == 'object') {
Runtime.printObjectList.push(arg);
return '<' + arg + '|' + (Runtime.printObjectList.length-1) + '>';
}
if (typeof arg == 'number') {
if (arg > 0) return '0x' + arg.toString(16) + ' (' + arg + ')';
}
return arg;
}
#endif
};
Runtime.stackAlloc = unInline('stackAlloc', ['size']);
Runtime.staticAlloc = unInline('staticAlloc', ['size']);
Runtime.alignMemory = unInline('alignMemory', ['size', 'quantum']);
Runtime.makeBigInt = unInline('makeBigInt', ['low', 'high', 'unsigned']);
if (MAIN_MODULE || SIDE_MODULE) {
Runtime.tempRet0 = 0;
Runtime.getTempRet0 = function() {
return Runtime.tempRet0;
};
Runtime.setTempRet0 = function(x) {
Runtime.tempRet0 = x;
};
}
function getRuntime() {
var ret = 'var Runtime = {\n';
for (i in Runtime) {
var item = Runtime[i];
ret += ' ' + i + ': ';
if (typeof item === 'function') {
ret += item.toString();
} else {
ret += JSON.stringify(item);
}
ret += ',\n';
}
return ret + ' __dummy__: 0\n}\n';
}
// Additional runtime elements, that need preprocessing
// Converts a value we have as signed, into an unsigned value. For
// example, -1 in int32 would be a very large number as unsigned.
function unSign(value, bits, ignore) {
if (value >= 0) {
return value;
}
return bits <= 32 ? 2*Math.abs(1 << (bits-1)) + value // Need some trickery, since if bits == 32, we are right at the limit of the bits JS uses in bitshifts
: Math.pow(2, bits) + value;
}
// Converts a value we have as unsigned, into a signed value. For
// example, 200 in a uint8 would be a negative number.
function reSign(value, bits, ignore) {
if (value <= 0) {
return value;
}
var half = bits <= 32 ? Math.abs(1 << (bits-1)) // abs is needed if bits == 32
: Math.pow(2, bits-1);
if (value >= half && (bits <= 32 || value > half)) { // for huge values, we can hit the precision limit and always get true here. so don't do that
// but, in general there is no perfect solution here. With 64-bit ints, we get rounding and errors
// TODO: In i64 mode 1, resign the two parts separately and safely
value = -2*half + value; // Cannot bitshift half, as it may be at the limit of the bits JS uses in bitshifts
}
return value;
}
// The address globals begin at. Very low in memory, for code size and optimization opportunities.
// Above 0 is static memory, starting with globals.
// Then the stack.
// Then 'dynamic' memory for sbrk.
Runtime.GLOBAL_BASE = {{{ GLOBAL_BASE }}} < 0 ? 8 : {{{ GLOBAL_BASE }}};
if (RETAIN_COMPILER_SETTINGS) {
var blacklist = set('STRUCT_INFO');
for (var x in this) {
try {
if (x[0] !== '_' && !(x in blacklist) && x == x.toUpperCase() && (typeof this[x] === 'number' || typeof this[x] === 'string' || this.isArray())) Runtime.compilerSettings[x] = this[x];
} catch(e){}
}
}