summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js29
-rw-r--r--src/compiler.js15
-rw-r--r--src/corruptionCheck.js98
-rw-r--r--src/experimental/allow_loopvars_from_memsetcpy_inasm.diff97
-rw-r--r--src/intertyper.js4
-rw-r--r--src/jsifier.js63
-rw-r--r--src/library.js481
-rw-r--r--src/library_browser.js93
-rw-r--r--src/library_egl.js41
-rw-r--r--src/library_gc.js2
-rw-r--r--src/library_gl.js761
-rw-r--r--src/library_glut.js6
-rw-r--r--src/library_sdl.js143
-rw-r--r--src/long.js11
-rw-r--r--src/modules.js22
-rw-r--r--src/parseTools.js128
-rw-r--r--src/preamble.js221
-rw-r--r--src/relooper/Relooper.cpp156
-rw-r--r--src/relooper/fuzzer.py4
-rw-r--r--src/relooper/test2.txt15
-rw-r--r--src/relooper/test3.txt38
-rw-r--r--src/relooper/test4.txt21
-rw-r--r--src/relooper/test6.txt15
-rw-r--r--src/relooper/test_debug.txt15
-rw-r--r--src/relooper/test_fuzz1.txt13
-rw-r--r--src/relooper/test_fuzz5.txt27
-rw-r--r--src/relooper/test_inf.txt651
-rw-r--r--src/runtime.js40
-rw-r--r--src/settings.js57
-rw-r--r--src/shell.html11
30 files changed, 2088 insertions, 1190 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 1c53b76c..209e3140 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -18,6 +18,7 @@ function recomputeLines(func) {
// Handy sets
var BRANCH_INVOKE = set('branch', 'invoke');
+var LABEL_ENDERS = set('branch', 'return');
var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic');
var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam');
@@ -88,7 +89,7 @@ function analyzer(data, sidePass) {
// Internal line
if (!currLabelFinished) {
item.functions.slice(-1)[0].labels.slice(-1)[0].lines.push(subItem); // If this line fails, perhaps missing a label?
- if (subItem.intertype === 'branch') {
+ if (subItem.intertype in LABEL_ENDERS) {
currLabelFinished = true;
}
} else {
@@ -121,7 +122,8 @@ function analyzer(data, sidePass) {
// Legalization
if (USE_TYPED_ARRAYS == 2) {
function getLegalVars(base, bits, allowLegal) {
- if (allowLegal && bits <= 32) return [{ ident: base, bits: bits }];
+ bits = bits || 32; // things like pointers are all i32, but show up as 0 bits from getBits
+ if (allowLegal && bits <= 32) return [{ ident: base + ('i' + bits in Runtime.INT_TYPES ? '' : '$0'), bits: bits }];
if (isNumber(base)) return getLegalLiterals(base, bits);
var ret = new Array(Math.ceil(bits/32));
var i = 0;
@@ -223,6 +225,9 @@ function analyzer(data, sidePass) {
for (var i = 0; i < item.params.length; i++) {
if (item.params[i].type == 'i64') item.params[i].type = 'i32';
}
+ } else if (item.intertype == 'inttoptr') {
+ var input = item.params[0];
+ if (input.type == 'i64') input.type = 'i32'; // inttoptr can only care about 32 bits anyhow since pointers are 32-bit
}
if (isIllegalType(item.valueType) || isIllegalType(item.type)) {
isIllegal = true;
@@ -285,7 +290,7 @@ function analyzer(data, sidePass) {
var elements = getLegalParams([item.value], bits)[0];
var j = 0;
elements.forEach(function(element) {
- var tempVar = '$st$' + i + '$' + j;
+ var tempVar = '$st$' + (tempId++) + '$' + j;
toAdd.push({
intertype: 'getelementptr',
assignTo: tempVar,
@@ -396,7 +401,7 @@ function analyzer(data, sidePass) {
var j = 0;
var toAdd = [];
elements.forEach(function(element) {
- var tempVar = '$st$' + i + '$' + j;
+ var tempVar = '$ld$' + (tempId++) + '$' + j;
toAdd.push({
intertype: 'getelementptr',
assignTo: tempVar,
@@ -643,13 +648,7 @@ function analyzer(data, sidePass) {
default: throw 'Invalid mathop for legalization: ' + [value.op, item.lineNum, dump(item)];
}
// Do the legalization
- var sourceElements;
- if (sourceBits <= 32) {
- // The input is a legal type
- sourceElements = [{ ident: value.params[0].ident, bits: sourceBits }];
- } else {
- sourceElements = getLegalVars(value.params[0].ident, sourceBits);
- }
+ var sourceElements = getLegalVars(value.params[0].ident, sourceBits, true);
if (!isNumber(shifts)) {
// We can't statically legalize this, do the operation at runtime TODO: optimize
assert(sourceBits == 64, 'TODO: handle nonconstant shifts on != 64 bits');
@@ -681,9 +680,9 @@ function analyzer(data, sidePass) {
params: [(signed && j + whole > sourceElements.length) ? signedKeepAlive : null],
type: 'i32',
};
- if (j == 0 && isUnsignedOp(value.op) && sourceBits < 32) {
+ if (j == 0 && sourceBits < 32) {
// zext sign correction
- result.ident = makeSignOp(result.ident, 'i' + sourceBits, 'un', 1, 1);
+ result.ident = makeSignOp(result.ident, 'i' + sourceBits, isUnsignedOp(value.op) ? 'un' : 're', 1, 1);
}
if (fraction != 0) {
var other = {
@@ -953,6 +952,7 @@ function analyzer(data, sidePass) {
// Function parameters
func.params.forEach(function(param) {
if (param.intertype !== 'varargs') {
+ if (func.variables[param.ident]) warn('cannot have duplicate variable names: ' + param.ident); // toNiceIdent collisions?
func.variables[param.ident] = {
ident: param.ident,
type: param.type,
@@ -966,6 +966,7 @@ function analyzer(data, sidePass) {
// Normal variables
func.lines.forEach(function(item, i) {
if (item.assignTo) {
+ if (func.variables[item.assignTo]) warn('cannot have duplicate variable names: ' + item.assignTo); // toNiceIdent collisions?
var variable = func.variables[item.assignTo] = {
ident: item.assignTo,
type: item.type,
@@ -1381,7 +1382,7 @@ function analyzer(data, sidePass) {
var label = func.labels[i];
for (var j = 0; j < label.lines.length; j++) {
var line = label.lines[j];
- if (line.intertype == 'call' && line.ident == setjmp) {
+ if ((line.intertype == 'call' || line.intertype == 'invoke') && line.ident == setjmp) {
// Add a new label
var oldIdent = label.ident;
var newIdent = func.labelIdCounter++;
diff --git a/src/compiler.js b/src/compiler.js
index 25c306cf..3047daf1 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -160,12 +160,6 @@ if (SAFE_HEAP >= 2) {
SAFE_HEAP_LINES = set(SAFE_HEAP_LINES); // for fast checking
}
-if (PGO) { // by default, correct everything during PGO
- CORRECT_SIGNS = CORRECT_SIGNS || 1;
- CORRECT_OVERFLOWS = CORRECT_OVERFLOWS || 1;
- CORRECT_ROUNDINGS = CORRECT_ROUNDINGS || 1;
-}
-
EXPORTED_FUNCTIONS = set(EXPORTED_FUNCTIONS);
EXPORTED_GLOBALS = set(EXPORTED_GLOBALS);
EXCEPTION_CATCHING_WHITELIST = set(EXCEPTION_CATCHING_WHITELIST);
@@ -178,20 +172,21 @@ assert(!(USE_TYPED_ARRAYS === 2 && QUANTUM_SIZE !== 4), 'For USE_TYPED_ARRAYS ==
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(DISABLE_EXCEPTION_CATCHING == 1, 'asm.js does not support C++ exceptions yet');
}
assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB)); // shared libraries must have named globals
// Output some info and warnings based on settings
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) {
+ if (!MICRO_OPTS || !RELOOP || ASSERTIONS || CHECK_SIGNS || CHECK_OVERFLOWS || INIT_HEAP ||
+ !SKIP_STACK_IN_SMALL || SAFE_HEAP || !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) {
+ if (DOUBLE_MODE || CORRECT_SIGNS || CORRECT_OVERFLOWS || CORRECT_ROUNDINGS || CHECK_HEAP_ALIGN) {
print('// Note: Some Emscripten settings may limit the speed of the generated code.');
}
}
@@ -204,7 +199,7 @@ load('parseTools.js');
load('intertyper.js');
load('analyzer.js');
load('jsifier.js');
-if (RELOOP) load('relooper.js')
+if (RELOOP) load(RELOOPER)
globalEval(processMacros(preprocess(read('runtime.js'))));
Runtime.QUANTUM_SIZE = QUANTUM_SIZE;
diff --git a/src/corruptionCheck.js b/src/corruptionCheck.js
new file mode 100644
index 00000000..315f5cf0
--- /dev/null
+++ b/src/corruptionCheck.js
@@ -0,0 +1,98 @@
+
+// See settings.js, CORRUPTION_CHECK
+
+var CorruptionChecker = {
+ BUFFER_FACTOR: Math.round({{{ CORRUPTION_CHECK }}}),
+
+ ptrs: {},
+ checks: 0,
+ checkFrequency: 1,
+
+ init: function() {
+ this.realMalloc = _malloc;
+ _malloc = Module['_malloc'] = this.malloc;
+
+ this.realFree = _free;
+ _free = Module['_free'] = this.free;
+
+ if (typeof _realloc != 'undefined') {
+ this.realRealloc = _realloc;
+ _realloc = Module['_realloc'] = this.realloc;
+ }
+
+ __ATEXIT__.push({ func: function() {
+ Module.printErr('No corruption detected, ran ' + CorruptionChecker.checks + ' checks.');
+ } });
+ },
+ malloc: function(size) {
+ if (size <= 0) size = 1; // malloc(0) sometimes happens - just allocate a larger area, no harm
+ CorruptionChecker.checkAll();
+ size = (size+7)&(~7);
+ var allocation = CorruptionChecker.realMalloc(size*(1+2*CorruptionChecker.BUFFER_FACTOR));
+ var ptr = allocation + size*CorruptionChecker.BUFFER_FACTOR;
+ assert(!CorruptionChecker.ptrs[ptr]);
+ CorruptionChecker.ptrs[ptr] = size;
+ CorruptionChecker.fillBuffer(allocation, size*CorruptionChecker.BUFFER_FACTOR);
+ CorruptionChecker.fillBuffer(allocation + size*(1+CorruptionChecker.BUFFER_FACTOR), size*CorruptionChecker.BUFFER_FACTOR);
+ //Module.printErr('malloc ' + size + ' ==> ' + [ptr, allocation]);
+ return ptr;
+ },
+ free: function(ptr) {
+ if (!ptr) return; // ok to free(NULL), does nothing
+ CorruptionChecker.checkAll();
+ var size = CorruptionChecker.ptrs[ptr];
+ //Module.printErr('free ' + ptr + ' of size ' + size);
+ assert(size);
+ var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR;
+ //Module.printErr('free ' + ptr + ' of size ' + size + ' and allocation ' + allocation);
+ delete CorruptionChecker.ptrs[ptr];
+ CorruptionChecker.realFree(allocation);
+ },
+ realloc: function(ptr, newSize) {
+ //Module.printErr('realloc ' + ptr + ' to size ' + newSize);
+ if (newSize <= 0) newSize = 1; // like in malloc
+ if (!ptr) return CorruptionChecker.malloc(newSize); // realloc(NULL, size) forwards to malloc according to the spec
+ var size = CorruptionChecker.ptrs[ptr];
+ assert(size);
+ var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR;
+ var newPtr = CorruptionChecker.malloc(newSize);
+ //Module.printErr('realloc ' + ptr + ' to size ' + newSize + ' is now ' + newPtr);
+ var newAllocation = newPtr + newSize*CorruptionChecker.BUFFER_FACTOR;
+ HEAPU8.set(HEAPU8.subarray(ptr, ptr + Math.min(size, newSize)), newPtr);
+ CorruptionChecker.free(ptr);
+ return newPtr;
+ },
+ canary: function(x) {
+ return (x&127) + 10;
+ },
+ fillBuffer: function(buffer, size) {
+ for (var x = buffer; x < buffer + size; x++) {
+ {{{ makeSetValue('x', 0, 'CorruptionChecker.canary(x)', 'i8') }}};
+ }
+ },
+ checkBuffer: function(buffer, size) {
+ for (var x = buffer; x < buffer + size; x++) {
+ if (({{{ makeGetValue('x', 0, 'i8') }}}&255) != CorruptionChecker.canary(x)) {
+ assert(0, 'Heap corruption detected!' + [x, buffer, size, {{{ makeGetValue('x', 0, 'i8') }}}&255, CorruptionChecker.canary(x)]);
+ }
+ }
+ },
+ checkPtr: function(ptr) {
+ var size = CorruptionChecker.ptrs[ptr];
+ assert(size);
+ var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR;
+ CorruptionChecker.checkBuffer(allocation, size*CorruptionChecker.BUFFER_FACTOR);
+ CorruptionChecker.checkBuffer(allocation + size*(1+CorruptionChecker.BUFFER_FACTOR), size*CorruptionChecker.BUFFER_FACTOR);
+ },
+ checkAll: function(force) {
+ CorruptionChecker.checks++;
+ if (!force && CorruptionChecker.checks % CorruptionChecker.checkFrequency != 0) return;
+ //Module.printErr('checking for corruption ' + (CorruptionChecker.checks/CorruptionChecker.checkFrequency));
+ for (var ptr in CorruptionChecker.ptrs) {
+ CorruptionChecker.checkPtr(ptr, false);
+ }
+ },
+};
+
+CorruptionChecker.init();
+
diff --git a/src/experimental/allow_loopvars_from_memsetcpy_inasm.diff b/src/experimental/allow_loopvars_from_memsetcpy_inasm.diff
new file mode 100644
index 00000000..a2dde7da
--- /dev/null
+++ b/src/experimental/allow_loopvars_from_memsetcpy_inasm.diff
@@ -0,0 +1,97 @@
+commit a61ef3dbbaf7333ad67fca29c0aad5bcc99b653a
+Author: Alon Zakai <alonzakai@gmail.com>
+Date: Wed Mar 6 18:18:03 2013 -0800
+
+ handle new vars in asm code, such as the loop vars from memset/memcpy loops
+
+diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
+index f2dc516..65059e8 100644
+--- a/tools/js-optimizer.js
++++ b/tools/js-optimizer.js
+@@ -1321,7 +1321,7 @@ function normalizeAsm(func) {
+ var name = v[0];
+ var value = v[1];
+ if (!(name in data.vars)) {
+- assert(value[0] == 'num' || (value[0] == 'unary-prefix' && value[2][0] == 'num')); // must be valid coercion no-op
++ if (!(value[0] == 'num' || (value[0] == 'unary-prefix' && value[2][0] == 'num'))) break outer; // must be valid coercion no-op
+ data.vars[name] = detectAsmCoercion(value);
+ v.length = 1; // make an un-assigning var
+ } else {
+@@ -1331,6 +1331,7 @@ function normalizeAsm(func) {
+ i++;
+ }
+ // finally, look for other var definitions and collect them
++ var extra = [];
+ while (i < stats.length) {
+ traverse(stats[i], function(node, type) {
+ if (type == 'var') {
+@@ -1340,6 +1341,7 @@ function normalizeAsm(func) {
+ var value = v[1];
+ if (!(name in data.vars)) {
+ data.vars[name] = detectAsmCoercion(value);
++ extra.push(['var', [[name]]]); // add a 'var' for normal JS
+ }
+ }
+ unVarify(node[1], node);
+@@ -1353,6 +1355,7 @@ function normalizeAsm(func) {
+ });
+ i++;
+ }
++ if (extra.length > 0) stats.splice.apply(stats, [0, 0].concat(extra));
+ //printErr('normalized \n\n' + astToSrc(func) + '\n\nwith: ' + JSON.stringify(data));
+ return data;
+ }
+diff --git a/tools/test-js-optimizer-asm-regs-output.js b/tools/test-js-optimizer-asm-regs-output.js
+index 99bccd2..f84b8d5 100644
+--- a/tools/test-js-optimizer-asm-regs-output.js
++++ b/tools/test-js-optimizer-asm-regs-output.js
+@@ -9,6 +9,18 @@ function asm(d1, i2) {
+ d4 = d1 * 5;
+ return d4;
+ }
++function asm2(d1, i2) {
++ d1 = +d1;
++ i2 = i2 | 0;
++ var i3 = 0, d4 = +0;
++ i3 = i2;
++ i2 = d1 + i3 | 0;
++ d1 = d(Math_max(10, Math_min(5, f())));
++ i3 = i2 + 2 | 0;
++ print(i3);
++ d4 = d1 * 5;
++ return d4;
++}
+ function _doit(i1, i2, i3) {
+ i1 = i1 | 0;
+ i2 = i2 | 0;
+diff --git a/tools/test-js-optimizer-asm-regs.js b/tools/test-js-optimizer-asm-regs.js
+index 0afced2..fbaa7c4 100644
+--- a/tools/test-js-optimizer-asm-regs.js
++++ b/tools/test-js-optimizer-asm-regs.js
+@@ -10,6 +10,19 @@ function asm(x, y) {
+ double2 = double1*5;
+ return double2;
+ }
++function asm2(x, y) {
++ x = +x;
++ y = y | 0;
++ var int1 = 0, int2 = 0; // do not mix the types!
++ var double1 = +0, double2 = +0;
++ var tempy = y;
++ int1 = (x+tempy)|0;
++ double1 = d(Math.max(10, Math_min(5, f())));
++ int2 = (int1+2)|0;
++ print(int2);
++ double2 = double1*5;
++ return double2;
++}
+ function _doit($x, $y$0, $y$1) {
+ $x = $x | 0;
+ $y$0 = $y$0 | 0;
+@@ -41,5 +54,5 @@ function retf() {
+ }
+ // missing final return, need it as a float
+ }
+-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "_doit", "rett", "ret2t", "retf"]
++// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "asm2", "_doit", "rett", "ret2t", "retf"]
+
diff --git a/src/intertyper.js b/src/intertyper.js
index c1a98354..2103ecfa 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -677,7 +677,7 @@ function intertyper(data, sidePass, baseLineNums) {
item.type = item.tokens[1].text;
Types.needAnalysis[item.type] = 0;
while (['@', '%'].indexOf(item.tokens[2].text[0]) == -1 && !(item.tokens[2].text in PARSABLE_LLVM_FUNCTIONS) &&
- item.tokens[2].text != 'null' && item.tokens[2].text != 'asm') {
+ item.tokens[2].text != 'null' && item.tokens[2].text != 'asm' && item.tokens[2].text != 'undef') {
assert(item.tokens[2].text != 'asm', 'Inline assembly cannot be compiled to JavaScript!');
item.tokens.splice(2, 1);
}
@@ -741,10 +741,12 @@ function intertyper(data, sidePass, baseLineNums) {
processItem: function(item) {
item.intertype = 'atomic';
if (item.tokens[0].text == 'atomicrmw') {
+ if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1);
item.op = item.tokens[1].text;
item.tokens.splice(1, 1);
} else {
assert(item.tokens[0].text == 'cmpxchg')
+ if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1);
item.op = 'cmpxchg';
}
var last = getTokenIndexByText(item.tokens, ';');
diff --git a/src/jsifier.js b/src/jsifier.js
index 761a5fec..ff58ece2 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -375,6 +375,7 @@ function JSify(data, functionsOnly, givenFunctions) {
var ret = [item];
item.JS = 'var ' + item.ident + ';';
// Set the actual value in a postset, since it may be a global variable. We also order by dependencies there
+ Variables.globals[item.ident].targetIdent = item.value.ident;
var value = Variables.globals[item.ident].resolvedAlias = finalizeLLVMParameter(item.value);
var fix = '';
if (BUILD_AS_SHARED_LIB == 2 && !item.private_) {
@@ -478,8 +479,7 @@ function JSify(data, functionsOnly, givenFunctions) {
ident = '_' + ident;
}
var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : '');
- // redirected idents just need a var, but no value assigned to them - it would be unused
- var contentText = isFunction ? snippet : ('var ' + ident + (redirectedIdent ? '' : '=' + snippet) + ';');
+ var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';');
if (ASM_JS) {
var sig = LibraryManager.library[ident.substr(1) + '__sig'];
if (isFunction && sig && LibraryManager.library[ident.substr(1) + '__asm']) {
@@ -506,9 +506,9 @@ function JSify(data, functionsOnly, givenFunctions) {
item.JS = '';
} else if (LibraryManager.library.hasOwnProperty(shortident)) {
item.JS = addFromLibrary(shortident);
- } else {
+ } else if (!LibraryManager.library.hasOwnProperty(shortident + '__inline')) {
item.JS = 'var ' + item.ident + '; // stub for ' + item.ident;
- if (WARN_ON_UNDEFINED_SYMBOLS) {
+ if (WARN_ON_UNDEFINED_SYMBOLS || ASM_JS) { // always warn on undefs in asm, since it breaks validation
warn('Unresolved symbol: ' + item.ident);
}
}
@@ -631,15 +631,6 @@ function JSify(data, functionsOnly, givenFunctions) {
}
}
- if (PROFILE) {
- func.JS += ' if (PROFILING) { '
- + 'var __parentProfilingNode__ = PROFILING_NODE; PROFILING_NODE = PROFILING_NODE.children["' + func.ident + '"]; '
- + 'if (!PROFILING_NODE) __parentProfilingNode__.children["' + func.ident + '"] = PROFILING_NODE = { time: 0, children: {}, calls: 0 };'
- + 'PROFILING_NODE.calls++; '
- + 'var __profilingStartTime__ = Date.now() '
- + '}\n';
- }
-
if (true) { // TODO: optimize away when not needed
if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */';
func.JS += ' var label = 0;\n';
@@ -923,7 +914,11 @@ function JSify(data, functionsOnly, givenFunctions) {
case VAR_NATIVIZED:
if (isNumber(item.ident)) {
// Direct write to a memory address; this may be an intentional segfault, if not, it is a bug in the source
- return 'throw "fault on write to ' + item.ident + '";';
+ if (ASM_JS) {
+ return 'abort(' + item.ident + ')';
+ } else {
+ return 'throw "fault on write to ' + item.ident + '";';
+ }
}
return item.ident + '=' + value + ';'; // We have the actual value here
break;
@@ -1145,12 +1140,6 @@ function JSify(data, functionsOnly, givenFunctions) {
});
makeFuncLineActor('return', function(item) {
var ret = RuntimeGenerator.stackExit(item.funcData.initialStack, item.funcData.otherStackAllocations) + ';\n';
- if (PROFILE) {
- ret += 'if (PROFILING) { '
- + 'PROFILING_NODE.time += Date.now() - __profilingStartTime__; '
- + 'PROFILING_NODE = __parentProfilingNode__ '
- + '}\n';
- }
if (LABEL_DEBUG && functionNameFilterTest(item.funcData.ident)) {
ret += "Module.print(INDENT + 'Exiting: " + item.funcData.ident + "');\n"
+ "INDENT = INDENT.substr(0, INDENT.length-2);\n";
@@ -1215,10 +1204,13 @@ function JSify(data, functionsOnly, givenFunctions) {
switch (item.op) {
case 'add': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue+' + param2, type, null, null, null, null, ',') + ',tempValue)';
case 'sub': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue-' + param2, type, null, null, null, null, ',') + ',tempValue)';
+ case 'or': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue|' + param2, type, null, null, null, null, ',') + ',tempValue)';
+ case 'and': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue&' + param2, type, null, null, null, null, ',') + ',tempValue)';
+ case 'xor': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue^' + param2, type, null, null, null, null, ',') + ',tempValue)';
case 'xchg': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, param2, type, null, null, null, null, ',') + ',tempValue)';
case 'cmpxchg': {
var param3 = finalizeLLVMParameter(item.params[2]);
- return '(tempValue=' + makeGetValue(param1, 0, type) + ',(' + makeGetValue(param1, 0, type) + '==' + param2 + ' ? ' + makeSetValue(param1, 0, param3, type, null, null, null, null, ',') + ' : 0),tempValue)';
+ return '(tempValue=' + makeGetValue(param1, 0, type) + ',(' + makeGetValue(param1, 0, type) + '==(' + param2 + '|0) ? ' + makeSetValue(param1, 0, param3, type, null, null, null, null, ',') + ' : 0),tempValue)';
}
default: throw 'unhandled atomic op: ' + item.op;
}
@@ -1237,6 +1229,15 @@ function JSify(data, functionsOnly, givenFunctions) {
var impl = item.ident ? getVarImpl(item.funcData, item.ident) : VAR_EMULATED;
switch (impl) {
case VAR_NATIVIZED: {
+ if (isNumber(item.ident)) {
+ item.assignTo = null;
+ // Direct read from a memory address; this may be an intentional segfault, if not, it is a bug in the source
+ if (ASM_JS) {
+ return 'abort(' + item.ident + ')';
+ } else {
+ return 'throw "fault on read from ' + item.ident + '";';
+ }
+ }
return value; // We have the actual value here
}
case VAR_EMULATED: return makeGetValue(value, 0, item.type, 0, item.unsigned, 0, item.align);
@@ -1260,7 +1261,7 @@ function JSify(data, functionsOnly, givenFunctions) {
makeFuncLineActor('insertvalue', function(item) {
assert(item.indexes.length == 1); // TODO: see extractvalue
var ret = '(', ident;
- if (item.ident === 'undef') {
+ if (item.ident === '0') {
item.ident = 'tempValue';
ret += item.ident + ' = [' + makeEmptyStruct(item.type) + '], ';
}
@@ -1300,6 +1301,7 @@ 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!');
+ ident = Variables.resolveAliasToIdent(ident);
var shortident = ident.slice(1);
var callIdent = LibraryManager.getRootIdent(shortident);
if (callIdent) {
@@ -1417,6 +1419,9 @@ function JSify(data, functionsOnly, givenFunctions) {
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
+ } else if (SAFE_DYNCALLS) {
+ assert(!ASM_JS, 'cannot emit safe dyncalls in asm');
+ callIdent = '(tempInt=' + callIdent + ',tempInt < 0 || tempInt >= FUNCTION_TABLE.length-1 || !FUNCTION_TABLE[tempInt] ? abort("dyncall error: ' + sig + ' " + FUNCTION_TABLE_NAMES[tempInt]) : tempInt)';
}
callIdent = Functions.getTable(sig) + '[' + callIdent + ']';
}
@@ -1527,7 +1532,7 @@ function JSify(data, functionsOnly, givenFunctions) {
print('// ASM_LIBRARY FUNCTIONS');
function fix(f) { // fix indenting to not confuse js optimizer
f = f.substr(f.indexOf('f')); // remove initial spaces before 'function'
- f = f.substr(0, f.lastIndexOf('\n')+1); // remove spaces and last }
+ f = f.substr(0, f.lastIndexOf('\n')+1); // remove spaces and last } XXX assumes function has multiple lines
return f + '}'; // add unindented } to match function
}
print(asmLibraryFunctions.map(fix).join('\n'));
@@ -1547,6 +1552,10 @@ function JSify(data, functionsOnly, givenFunctions) {
// This is the main 'post' pass. Print out the generated code that we have here, together with the
// rest of the output that we started to print out earlier (see comment on the
// "Final shape that will be created").
+ if (CORRUPTION_CHECK) {
+ assert(!ASM_JS); // cannot monkeypatch asm!
+ print(processMacros(read('corruptionCheck.js')));
+ }
if (PRECISE_I64_MATH && Types.preciseI64MathUsed) {
print(read('long.js'));
} else {
@@ -1579,9 +1588,11 @@ function JSify(data, functionsOnly, givenFunctions) {
var shellParts = read(shellFile).split('{{BODY}}');
print(shellParts[1]);
// Print out some useful metadata (for additional optimizations later, like the eliminator)
- print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) {
- return IGNORED_FUNCTIONS.indexOf(func.ident) < 0;
- })) + '\n');
+ if (EMIT_GENERATED_FUNCTIONS) {
+ print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) {
+ return IGNORED_FUNCTIONS.indexOf(func.ident) < 0;
+ })) + '\n');
+ }
PassManager.serialize();
diff --git a/src/library.js b/src/library.js
index 1e026937..d787e30c 100644
--- a/src/library.js
+++ b/src/library.js
@@ -624,7 +624,12 @@ LibraryManager.library = {
// dirent.h
// ==========================================================================
- __dirent_struct_layout: Runtime.generateStructInfo(['d_ino', 'd_name', 'd_off', 'd_reclen', 'd_type'], '%struct.dirent'),
+ __dirent_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'd_ino'],
+ ['b1024', 'd_name'],
+ ['i32', 'd_off'],
+ ['i32', 'd_reclen'],
+ ['i32', 'd_type']]),
opendir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'],
opendir: function(dirname) {
// DIR *opendir(const char *dirname);
@@ -786,7 +791,9 @@ LibraryManager.library = {
// utime.h
// ==========================================================================
- __utimbuf_struct_layout: Runtime.generateStructInfo(['actime', 'modtime'], '%struct.utimbuf'),
+ __utimbuf_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'actime'],
+ ['i32', 'modtime']]),
utime__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__utimbuf_struct_layout'],
utime: function(path, times) {
// int utime(const char *path, const struct utimbuf *times);
@@ -877,23 +884,23 @@ LibraryManager.library = {
// ==========================================================================
__stat_struct_layout: Runtime.generateStructInfo([
- 'st_dev',
- 'st_ino',
- 'st_mode',
- 'st_nlink',
- 'st_uid',
- 'st_gid',
- 'st_rdev',
- 'st_size',
- 'st_atime',
- 'st_spare1',
- 'st_mtime',
- 'st_spare2',
- 'st_ctime',
- 'st_spare3',
- 'st_blksize',
- 'st_blocks',
- 'st_spare4'], '%struct.stat'),
+ ['i32', 'st_dev'],
+ ['i32', 'st_ino'],
+ ['i32', 'st_mode'],
+ ['i32', 'st_nlink'],
+ ['i32', 'st_uid'],
+ ['i32', 'st_gid'],
+ ['i32', 'st_rdev'],
+ ['i32', 'st_size'],
+ ['i32', 'st_atime'],
+ ['i32', 'st_spare1'],
+ ['i32', 'st_mtime'],
+ ['i32', 'st_spare2'],
+ ['i32', 'st_ctime'],
+ ['i32', 'st_spare3'],
+ ['i32', 'st_blksize'],
+ ['i32', 'st_blocks'],
+ ['i32', 'st_spare4']]),
stat__deps: ['$FS', '__stat_struct_layout'],
stat: function(path, buf, dontResolveLastLink) {
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html
@@ -1065,17 +1072,17 @@ LibraryManager.library = {
// ==========================================================================
__statvfs_struct_layout: Runtime.generateStructInfo([
- 'f_bsize',
- 'f_frsize',
- 'f_blocks',
- 'f_bfree',
- 'f_bavail',
- 'f_files',
- 'f_ffree',
- 'f_favail',
- 'f_fsid',
- 'f_flag',
- 'f_namemax'], '%struct.statvfs'),
+ ['i32', 'f_bsize'],
+ ['i32', 'f_frsize'],
+ ['i32', 'f_blocks'],
+ ['i32', 'f_bfree'],
+ ['i32', 'f_bavail'],
+ ['i32', 'f_files'],
+ ['i32', 'f_ffree'],
+ ['i32', 'f_favail'],
+ ['i32', 'f_fsid'],
+ ['i32', 'f_flag'],
+ ['i32', 'f_namemax']]),
statvfs__deps: ['$FS', '__statvfs_struct_layout'],
statvfs: function(path, buf) {
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html
@@ -1110,12 +1117,12 @@ LibraryManager.library = {
// ==========================================================================
__flock_struct_layout: Runtime.generateStructInfo([
- 'l_type',
- 'l_whence',
- 'l_start',
- 'l_len',
- 'l_pid',
- 'l_xxx'], '%struct.flock'),
+ ['i16', 'l_type'],
+ ['i16', 'l_whence'],
+ ['i32', 'l_start'],
+ ['i32', 'l_len'],
+ ['i16', 'l_pid'],
+ ['i16', 'l_xxx']]),
open__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'],
open: function(path, oflag, varargs) {
// int open(const char *path, int oflag, ...);
@@ -1336,7 +1343,10 @@ LibraryManager.library = {
// poll.h
// ==========================================================================
- __pollfd_struct_layout: Runtime.generateStructInfo(['fd', 'events', 'revents'], '%struct.pollfd'),
+ __pollfd_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'fd'],
+ ['i16', 'events'],
+ ['i16', 'revents']]),
poll__deps: ['$FS', '__pollfd_struct_layout'],
poll: function(fds, nfds, timeout) {
// int poll(struct pollfd fds[], nfds_t nfds, int timeout);
@@ -2393,6 +2403,7 @@ LibraryManager.library = {
case {{{ cDefine('_SC_STREAM_MAX') }}}: return 16;
case {{{ cDefine('_SC_TZNAME_MAX') }}}: return 6;
case {{{ cDefine('_SC_THREAD_DESTRUCTOR_ITERATIONS') }}}: return 4;
+ case {{{ cDefine('_SC_NPROCESSORS_ONLN') }}}: return 1;
}
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
@@ -2481,6 +2492,17 @@ LibraryManager.library = {
continue;
}
+ // TODO: Support strings like "%5c" etc.
+ if (format[formatIndex] === '%' && format[formatIndex+1] == 'c') {
+ var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}};
+ argIndex += Runtime.getNativeFieldSize('void*');
+ fields++;
+ next = get();
+ {{{ makeSetValue('argPtr', 0, 'next', 'i8') }}}
+ formatIndex += 2;
+ continue;
+ }
+
// remove whitespace
while (1) {
next = get();
@@ -3342,14 +3364,15 @@ LibraryManager.library = {
___setErrNo(ERRNO_CODES.ECHILD);
return -1;
},
- perror__deps: ['puts', 'putc', 'strerror', '__errno_location'],
+ perror__deps: ['puts', 'fputs', 'fputc', 'strerror', '__errno_location'],
perror: function(s) {
// void perror(const char *s);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/perror.html
+ var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}};
if (s) {
- _puts(s);
- _putc(':'.charCodeAt(0));
- _putc(' '.charCodeAt(0));
+ _fputs(s, stdout);
+ _fputc(':'.charCodeAt(0), stdout);
+ _fputc(' '.charCodeAt(0), stdout);
}
var errnum = {{{ makeGetValue('___errno_location()', '0', 'i32') }}};
_puts(_strerror(errnum));
@@ -3647,6 +3670,17 @@ LibraryManager.library = {
abs: 'Math.abs',
labs: 'Math.abs',
+#if USE_TYPED_ARRAYS == 2
+ llabs__deps: [function() { Types.preciseI64MathUsed = 1 }],
+ llabs: function(lo, hi) {
+ i64Math.abs(lo, hi);
+ {{{ makeStructuralReturn([makeGetTempDouble(0, 'i32'), makeGetTempDouble(1, 'i32')]) }}};
+ },
+#else
+ llabs: function(lo, hi) {
+ throw 'unsupported llabs';
+ },
+#endif
exit__deps: ['_exit'],
exit: function(status) {
@@ -3704,93 +3738,6 @@ LibraryManager.library = {
return ret;
},
- strtod__deps: ['isspace', 'isdigit'],
- strtod: function(str, endptr) {
- var origin = str;
-
- // Skip space.
- while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++;
-
- // Check for a plus/minus sign.
- var multiplier = 1;
- if ({{{ makeGetValue('str', 0, 'i8') }}} == '-'.charCodeAt(0)) {
- multiplier = -1;
- str++;
- } else if ({{{ makeGetValue('str', 0, 'i8') }}} == '+'.charCodeAt(0)) {
- str++;
- }
-
- var chr;
- var ret = 0;
-
- // Get whole part.
- var whole = false;
- while(1) {
- chr = {{{ makeGetValue('str', 0, 'i8') }}};
- if (!_isdigit(chr)) break;
- whole = true;
- ret = ret*10 + chr - '0'.charCodeAt(0);
- str++;
- }
-
- // Get fractional part.
- var fraction = false;
- if ({{{ makeGetValue('str', 0, 'i8') }}} == '.'.charCodeAt(0)) {
- str++;
- var mul = 1/10;
- while(1) {
- chr = {{{ makeGetValue('str', 0, 'i8') }}};
- if (!_isdigit(chr)) break;
- fraction = true;
- ret += mul*(chr - '0'.charCodeAt(0));
- mul /= 10;
- str++;
- }
- }
-
- if (!whole && !fraction) {
- if (endptr) {
- {{{ makeSetValue('endptr', 0, 'origin', '*') }}}
- }
- return 0;
- }
-
- // Get exponent part.
- chr = {{{ makeGetValue('str', 0, 'i8') }}};
- if (chr == 'e'.charCodeAt(0) || chr == 'E'.charCodeAt(0)) {
- str++;
- var exponent = 0;
- var expNegative = false;
- chr = {{{ makeGetValue('str', 0, 'i8') }}};
- if (chr == '-'.charCodeAt(0)) {
- expNegative = true;
- str++;
- } else if (chr == '+'.charCodeAt(0)) {
- str++;
- }
- chr = {{{ makeGetValue('str', 0, 'i8') }}};
- while(1) {
- if (!_isdigit(chr)) break;
- exponent = exponent*10 + chr - '0'.charCodeAt(0);
- str++;
- chr = {{{ makeGetValue('str', 0, 'i8') }}};
- }
- if (expNegative) exponent = -exponent;
- ret *= Math.pow(10, exponent);
- }
-
- // Set end pointer.
- if (endptr) {
- {{{ makeSetValue('endptr', 0, 'str', '*') }}}
- }
-
- return ret * multiplier;
- },
- strtod_l: 'strtod', // no locale support yet
- strtold: 'strtod', // XXX add real support for long double
- strtold_l: 'strtold', // no locale support yet
- strtof: 'strtod', // use stdtod to handle strtof
-
_parseInt__deps: ['isspace', '__setErrNo', '$ERRNO_CODES'],
_parseInt: function(str, endptr, base, min, max, bits, unsign) {
// Skip space.
@@ -3949,11 +3896,6 @@ LibraryManager.library = {
},
strtoull_l: 'strtoull', // no locale support yet
- atof__deps: ['strtod'],
- atof: function(ptr) {
- return _strtod(ptr, null);
- },
-
atoi__deps: ['strtol'],
atoi: function(ptr) {
return _strtol(ptr, null, 10);
@@ -4202,6 +4144,8 @@ LibraryManager.library = {
return 1;
},
+ arc4random: 'rand',
+
// ==========================================================================
// string.h
// ==========================================================================
@@ -4290,8 +4234,9 @@ LibraryManager.library = {
ptr = ptr|0; value = value|0; num = num|0;
var stop = 0, value4 = 0, stop4 = 0, unaligned = 0;
stop = (ptr + num)|0;
- if (num|0 >= {{{ SEEK_OPTIMAL_ALIGN_MIN }}}) {
+ if ((num|0) >= {{{ Math.round(2.5*UNROLL_LOOP_MAX) }}}) {
// This is unaligned, but quite large, so work hard to get to aligned settings
+ value = value & 0xff;
unaligned = ptr & 3;
value4 = value | (value << 8) | (value << 16) | (value << 24);
stop4 = stop & ~3;
@@ -4502,11 +4447,16 @@ LibraryManager.library = {
return 0;
},
+ memcmp__asm: 'true',
+ memcmp__sig: 'iiii',
memcmp: function(p1, p2, num) {
- for (var i = 0; i < num; i++) {
- var v1 = {{{ makeGetValue('p1', 'i', 'i8', 0, 1) }}};
- var v2 = {{{ makeGetValue('p2', 'i', 'i8', 0, 1) }}};
- if (v1 != v2) return v1 > v2 ? 1 : -1;
+ p1 = p1|0; p2 = p2|0; num = num|0;
+ var i = 0, v1 = 0, v2 = 0;
+ while ((i|0) < (num|0)) {
+ var v1 = {{{ makeGetValueAsm('p1', 'i', 'i8', true) }}};
+ var v2 = {{{ makeGetValueAsm('p2', 'i', 'i8', true) }}};
+ if ((v1|0) != (v2|0)) return ((v1|0) > (v2|0) ? 1 : -1)|0;
+ i = (i+1)|0;
}
return 0;
},
@@ -4607,10 +4557,8 @@ LibraryManager.library = {
__strtok_state: 0,
strtok__deps: ['__strtok_state', 'strtok_r'],
+ strtok__postset: '___strtok_state = Runtime.staticAlloc(4);',
strtok: function(s, delim) {
- if (!___strtok_state) {
- ___strtok_state = _malloc(4);
- }
return _strtok_r(s, delim, ___strtok_state);
},
@@ -4948,6 +4896,20 @@ LibraryManager.library = {
#endif
},
+ llvm_ctpop_i32: function(x) {
+ var ret = 0;
+ while (x) {
+ if (x&1) ret++;
+ x >>= 1;
+ }
+ return ret;
+ },
+
+ llvm_ctpop_i64__deps: ['llvm_ctpop_i32'],
+ llvm_ctpop_i64: function(l, h) {
+ return _llvm_ctpop_i32(l) + _llvm_ctpop_i32(h);
+ },
+
llvm_trap: function() {
throw 'trap! ' + new Error().stack;
},
@@ -5070,6 +5032,7 @@ LibraryManager.library = {
},
__cxa_call_unexpected: function(exception) {
+ Module.printErr('Unexpected exception thrown, this is not properly supported - aborting');
ABORT = true;
throw exception;
},
@@ -5477,6 +5440,14 @@ LibraryManager.library = {
return -a;
},
copysignf: 'copysign',
+ __signbit__deps: ['copysign'],
+ __signbit: function(x) {
+ // We implement using copysign so that we get support
+ // for negative zero (once copysign supports that).
+ return _copysign(1.0, x) < 0;
+ },
+ __signbitf: '__signbit',
+ __signbitd: '__signbit',
hypot: function(a, b) {
return Math.sqrt(a*a + b*b);
},
@@ -5619,11 +5590,11 @@ LibraryManager.library = {
// ==========================================================================
__utsname_struct_layout: Runtime.generateStructInfo([
- 'sysname',
- 'nodename',
- 'release',
- 'version',
- 'machine'], '%struct.utsname'),
+ ['b32', 'sysname'],
+ ['b32', 'nodename'],
+ ['b32', 'release'],
+ ['b32', 'version'],
+ ['b32', 'machine']]),
uname__deps: ['__utsname_struct_layout'],
uname: function(name) {
// int uname(struct utsname *name);
@@ -5823,17 +5794,17 @@ LibraryManager.library = {
},
__tm_struct_layout: Runtime.generateStructInfo([
- 'tm_sec',
- 'tm_min',
- 'tm_hour',
- 'tm_mday',
- 'tm_mon',
- 'tm_year',
- 'tm_wday',
- 'tm_yday',
- 'tm_isdst',
- 'tm_gmtoff',
- 'tm_zone'], '%struct.tm'),
+ ['i32', 'tm_sec'],
+ ['i32', 'tm_min'],
+ ['i32', 'tm_hour'],
+ ['i32', 'tm_mday'],
+ ['i32', 'tm_mon'],
+ ['i32', 'tm_year'],
+ ['i32', 'tm_wday'],
+ ['i32', 'tm_yday'],
+ ['i32', 'tm_isdst'],
+ ['i32', 'tm_gmtoff'],
+ ['i32', 'tm_zone']]),
// Statically allocated time struct.
__tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STACK)',
// Statically allocated timezone strings.
@@ -6032,7 +6003,18 @@ LibraryManager.library = {
// sys/time.h
// ==========================================================================
- __timespec_struct_layout: Runtime.generateStructInfo(['tv_sec', 'tv_nsec'], '%struct.timespec'),
+ __timespec_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'tv_sec'],
+ ['i32', 'tv_nsec']]),
+ nanosleep__deps: ['usleep', '__timespec_struct_layout'],
+ nanosleep: function(rqtp, rmtp) {
+ // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
+ var seconds = {{{ makeGetValue('rqtp', '___timespec_struct_layout.tv_sec', 'i32') }}};
+ var nanoseconds = {{{ makeGetValue('rqtp', '___timespec_struct_layout.tv_nsec', 'i32') }}};
+ {{{ makeSetValue('rmtp', '___timespec_struct_layout.tv_sec', '0', 'i32') }}}
+ {{{ makeSetValue('rmtp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}
+ return _usleep((seconds * 1e6) + (nanoseconds / 1000));
+ },
// TODO: Implement these for real.
clock_gettime__deps: ['__timespec_struct_layout'],
clock_gettime: function(clk_id, tp) {
@@ -6089,10 +6071,10 @@ LibraryManager.library = {
// ==========================================================================
__tms_struct_layout: Runtime.generateStructInfo([
- 'tms_utime',
- 'tms_stime',
- 'tms_cutime',
- 'tms_cstime'], '%struct.tms'),
+ ['i32', 'tms_utime'],
+ ['i32', 'tms_stime'],
+ ['i32', 'tms_cutime'],
+ ['i32', 'tms_cstime']]),
times__deps: ['__tms_struct_layout', 'memset'],
times: function(buffer) {
// clock_t times(struct tms *buffer);
@@ -6139,7 +6121,7 @@ LibraryManager.library = {
setjmp__inline: function(env) {
// Save the label
- return '(tempInt = setjmpId++, mySetjmpIds[tempInt] = 1, setjmpLabels[tempInt] = label,' + makeSetValue(env, '0', 'tempInt', 'i32') + ', 0)';
+ return '(tempInt = setjmpId++, mySetjmpIds[tempInt] = 1, setjmpLabels[tempInt] = label,' + makeSetValue(env, '0', 'tempInt', 'i32', undefined, undefined, undefined, undefined, ',') + ', 0)';
},
longjmp: function(env, value) {
@@ -6614,7 +6596,9 @@ LibraryManager.library = {
// ==========================================================================
// TODO: Implement for real.
- __rlimit_struct_layout: Runtime.generateStructInfo(['rlim_cur', 'rlim_max'], '%struct.rlimit'),
+ __rlimit_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'rlim_cur'],
+ ['i32', 'rlim_max']]),
getrlimit__deps: ['__rlimit_struct_layout'],
getrlimit: function(resource, rlp) {
// int getrlimit(int resource, struct rlimit *rlp);
@@ -6630,22 +6614,22 @@ LibraryManager.library = {
// TODO: Implement for real. We just do time used, and no useful data
__rusage_struct_layout: Runtime.generateStructInfo([
- 'ru_utime',
- 'ru_stime',
- 'ru_maxrss',
- 'ru_ixrss',
- 'ru_idrss',
- 'ru_isrss',
- 'ru_minflt',
- 'ru_majflt',
- 'ru_nswap',
- 'ru_inblock',
- 'ru_oublock',
- 'ru_msgsnd',
- 'ru_msgrcv',
- 'ru_nsignals',
- 'ru_nvcsw',
- 'ru_nivcsw'], '%struct.rusage'),
+ ['i64', 'ru_utime'],
+ ['i64', 'ru_stime'],
+ ['i32', 'ru_maxrss'],
+ ['i32', 'ru_ixrss'],
+ ['i32', 'ru_idrss'],
+ ['i32', 'ru_isrss'],
+ ['i32', 'ru_minflt'],
+ ['i32', 'ru_majflt'],
+ ['i32', 'ru_nswap'],
+ ['i32', 'ru_inblock'],
+ ['i32', 'ru_oublock'],
+ ['i32', 'ru_msgsnd'],
+ ['i32', 'ru_msgrcv'],
+ ['i32', 'ru_nsignals'],
+ ['i32', 'ru_nvcsw'],
+ ['i32', 'ru_nivcsw']]),
getrusage__deps: ['__rusage_struct_layout'],
getrusage: function(resource, rlp) {
// %struct.timeval = type { i32, i32 }
@@ -6660,6 +6644,13 @@ LibraryManager.library = {
},
// ==========================================================================
+ // sched.h (stubs only - no thread support yet!)
+ // ==========================================================================
+ sched_yield: function() {
+ return 0;
+ },
+
+ // ==========================================================================
// pthread.h (stubs for mutexes only - no thread support yet!)
// ==========================================================================
@@ -6670,10 +6661,20 @@ LibraryManager.library = {
pthread_mutexattr_destroy: function() {},
pthread_mutex_lock: function() {},
pthread_mutex_unlock: function() {},
+ pthread_mutex_trylock: function() {
+ return 0;
+ },
pthread_cond_init: function() {},
pthread_cond_destroy: function() {},
- pthread_cond_broadcast: function() {},
- pthread_cond_wait: function() {},
+ pthread_cond_broadcast: function() {
+ return 0;
+ },
+ pthread_cond_wait: function() {
+ return 0;
+ },
+ pthread_cond_timedwait: function() {
+ return 0;
+ },
pthread_self: function() {
//FIXME: assumes only a single thread
return 0;
@@ -6712,17 +6713,27 @@ LibraryManager.library = {
pthread_key_create: function(key, destructor) {
if (!_pthread_key_create.keys) _pthread_key_create.keys = {};
- _pthread_key_create.keys[key] = null;
+ // values start at 0
+ _pthread_key_create.keys[key] = 0;
},
pthread_getspecific: function(key) {
- return _pthread_key_create.keys[key];
+ return _pthread_key_create.keys[key] || 0;
},
pthread_setspecific: function(key, value) {
_pthread_key_create.keys[key] = value;
},
+ pthread_key_delete: ['$ERRNO_CODES'],
+ pthread_key_delete: function(key) {
+ if (_pthread_key_create.keys[key]) {
+ delete _pthread_key_create.keys[key];
+ return 0;
+ }
+ return ERRNO_CODES.EINVAL;
+ },
+
pthread_cleanup_push: function(routine, arg) {
__ATEXIT__.push({ func: function() { Runtime.dynCall('vi', routine, [arg]) } })
_pthread_cleanup_push.level = __ATEXIT__.length;
@@ -7227,23 +7238,56 @@ LibraryManager.library = {
},
select: function(nfds, readfds, writefds, exceptfds, timeout) {
- // only readfds are supported, not writefds or exceptfds
+ // readfds are supported,
+ // writefds checks socket open status
+ // exceptfds not supported
// 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.hasData()) ret++;
+ assert(!exceptfds);
+
+ function canRead(info) {
+ // make sure hasData exists.
+ // we do create it when the socket is connected,
+ // but other implementations may create it lazily
+ return info.hasData && info.hasData();
+ }
+
+ function canWrite(info) {
+ // make sure socket exists.
+ // we do create it when the socket is connected,
+ // but other implementations may create it lazily
+ return info.socket && (info.socket.readyState == info.socket.OPEN);
+ }
+
+ function checkfds(nfds, fds, can) {
+ if (!fds) return 0;
+
+ var bitsSet = 0;
+ var dstLow = 0;
+ var dstHigh = 0;
+ var srcLow = {{{ makeGetValue('fds', 0, 'i32') }}};
+ var srcHigh = {{{ makeGetValue('fds', 4, 'i32') }}};
+ nfds = Math.min(64, nfds); // fd sets have 64 bits
+
+ for (var fd = 0; fd < nfds; fd++) {
+ var mask = 1 << (fd % 32), int = fd < 32 ? srcLow : srcHigh;
+ if (int & mask) {
+ // index is in the set, check if it is ready for read
+ var info = Sockets.fds[fd];
+ if (info && can(info)) {
+ // set bit
+ fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask);
+ bitsSet++;
+ }
+ }
}
+
+ {{{ makeSetValue('fds', 0, 'dstLow', 'i32') }}};
+ {{{ makeSetValue('fds', 4, 'dstHigh', 'i32') }}};
+ return bitsSet;
}
- return ret;
+
+ return checkfds(nfds, readfds, canRead)
+ + checkfds(nfds, writefds, canWrite);
},
// pty.h
@@ -7290,35 +7334,22 @@ LibraryManager.library = {
return Math.random();
},
- $Profiling: {
- max_: 0,
- times: null,
- invalid: 0,
- dump: function() {
- if (Profiling.invalid) {
- Module.printErr('Invalid # of calls to Profiling begin and end!');
- return;
- }
- Module.printErr('Profiling data:')
- for (var i = 0; i < Profiling.max_; i++) {
- Module.printErr('Block ' + i + ': ' + Profiling.times[i]);
- }
+ emscripten_jcache_printf___deps: ['_formatString'],
+ emscripten_jcache_printf_: function(varargs) {
+ var MAX = 10240;
+ if (!_emscripten_jcache_printf_.buffer) {
+ _emscripten_jcache_printf_.buffer = _malloc(MAX);
}
+ var i = 0;
+ do {
+ var curr = {{{ makeGetValue('varargs', 'i*4', 'i8') }}};
+ {{{ makeSetValue('_emscripten_jcache_printf_.buffer', 'i', 'curr', 'i8') }}};
+ i++;
+ assert(i*4 < MAX);
+ } while (curr != 0);
+ Module.print(intArrayToString(__formatString(_emscripten_jcache_printf_.buffer, varargs + i*4)).replace('\\n', ''));
+ Runtime.stackAlloc(-4*i); // free up the stack space we know is ok to free
},
- EMSCRIPTEN_PROFILE_INIT__deps: ['$Profiling'],
- EMSCRIPTEN_PROFILE_INIT: function(max_) {
- Profiling.max_ = max_;
- Profiling.times = new Array(max_);
- for (var i = 0; i < max_; i++) Profiling.times[i] = 0;
- },
- EMSCRIPTEN_PROFILE_BEGIN__inline: function(id) {
- return 'Profiling.times[' + id + '] -= Date.now();'
- + 'Profiling.invalid++;'
- },
- EMSCRIPTEN_PROFILE_END__inline: function(id) {
- return 'Profiling.times[' + id + '] += Date.now();'
- + 'Profiling.invalid--;'
- }
};
function autoAddDeps(object, name) {
diff --git a/src/library_browser.js b/src/library_browser.js
index d16fbc0b..bdd94bac 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -3,7 +3,7 @@
// Utilities for browser environments
mergeInto(LibraryManager.library, {
- $Browser__postset: 'Module["requestFullScreen"] = function() { Browser.requestFullScreen() };\n' + // exports
+ $Browser__postset: 'Module["requestFullScreen"] = function(lockPointer, resizeCanvas) { Browser.requestFullScreen(lockPointer, resizeCanvas) };\n' + // exports
'Module["requestAnimationFrame"] = function(func) { Browser.requestAnimationFrame(func) };\n' +
'Module["pauseMainLoop"] = function() { Browser.mainLoop.pause() };\n' +
'Module["resumeMainLoop"] = function() { Browser.mainLoop.resume() };\n',
@@ -40,6 +40,7 @@ mergeInto(LibraryManager.library, {
}
}
},
+ isFullScreen: false,
pointerLock: false,
moduleContextCreatedCallbacks: [],
workers: [],
@@ -68,6 +69,7 @@ mergeInto(LibraryManager.library, {
function getMimetype(name) {
return {
'jpg': 'image/jpeg',
+ 'jpeg': 'image/jpeg',
'png': 'image/png',
'bmp': 'image/bmp',
'ogg': 'audio/ogg',
@@ -81,7 +83,7 @@ mergeInto(LibraryManager.library, {
var imagePlugin = {};
imagePlugin['canHandle'] = function(name) {
- return name.substr(-4) in { '.jpg': 1, '.png': 1, '.bmp': 1 };
+ return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/.exec(name);
};
imagePlugin['handle'] = function(byteArray, name, onload, onerror) {
var b = null;
@@ -123,7 +125,7 @@ mergeInto(LibraryManager.library, {
var audioPlugin = {};
audioPlugin['canHandle'] = function(name) {
- return name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 };
+ return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 };
};
audioPlugin['handle'] = function(byteArray, name, onload, onerror) {
var done = false;
@@ -200,8 +202,18 @@ mergeInto(LibraryManager.library, {
return null;
}
#endif
+ var ctx;
try {
- var ctx = canvas.getContext(useWebGL ? 'experimental-webgl' : '2d');
+ if (useWebGL) {
+ ctx = canvas.getContext('experimental-webgl', {
+#if GL_TESTING
+ preserveDrawingBuffer: true,
+#endif
+ alpha: false
+ });
+ } else {
+ ctx = canvas.getContext('2d');
+ }
if (!ctx) throw ':(';
} catch (e) {
Module.print('Could not create canvas - ' + e);
@@ -263,35 +275,59 @@ mergeInto(LibraryManager.library, {
return ctx;
},
- requestFullScreen: function() {
+ destroyContext: function(canvas, useWebGL, setInModule) {},
+
+ fullScreenHandlersInstalled: false,
+ lockPointer: undefined,
+ resizeCanvas: undefined,
+ requestFullScreen: function(lockPointer, resizeCanvas) {
+ this.lockPointer = lockPointer;
+ this.resizeCanvas = resizeCanvas;
+ if (typeof this.lockPointer === 'undefined') this.lockPointer = true;
+ if (typeof this.resizeCanvas === 'undefined') this.resizeCanvas = false;
+
var canvas = Module['canvas'];
function fullScreenChange() {
- var isFullScreen = false;
+ Browser.isFullScreen = false;
if ((document['webkitFullScreenElement'] || document['webkitFullscreenElement'] ||
document['mozFullScreenElement'] || document['mozFullscreenElement'] ||
document['fullScreenElement'] || document['fullscreenElement']) === canvas) {
canvas.requestPointerLock = canvas['requestPointerLock'] ||
canvas['mozRequestPointerLock'] ||
canvas['webkitRequestPointerLock'];
- canvas.requestPointerLock();
- isFullScreen = true;
+ canvas.exitPointerLock = document['exitPointerLock'] ||
+ document['mozExitPointerLock'] ||
+ document['webkitExitPointerLock'];
+ canvas.exitPointerLock = canvas.exitPointerLock.bind(document);
+ canvas.cancelFullScreen = document['cancelFullScreen'] ||
+ document['mozCancelFullScreen'] ||
+ document['webkitCancelFullScreen'];
+ canvas.cancelFullScreen = canvas.cancelFullScreen.bind(document);
+ if (Browser.lockPointer) canvas.requestPointerLock();
+ Browser.isFullScreen = true;
+ if (Browser.resizeCanvas) Browser.setFullScreenCanvasSize();
+ } else if (Browser.resizeCanvas){
+ Browser.setWindowedCanvasSize();
}
- if (Module['onFullScreen']) Module['onFullScreen'](isFullScreen);
+ if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullScreen);
}
- document.addEventListener('fullscreenchange', fullScreenChange, false);
- document.addEventListener('mozfullscreenchange', fullScreenChange, false);
- document.addEventListener('webkitfullscreenchange', fullScreenChange, false);
-
function pointerLockChange() {
Browser.pointerLock = document['pointerLockElement'] === canvas ||
document['mozPointerLockElement'] === canvas ||
document['webkitPointerLockElement'] === canvas;
}
- document.addEventListener('pointerlockchange', pointerLockChange, false);
- document.addEventListener('mozpointerlockchange', pointerLockChange, false);
- document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
+ if (!this.fullScreenHandlersInstalled) {
+ this.fullScreenHandlersInstalled = true;
+ document.addEventListener('fullscreenchange', fullScreenChange, false);
+ document.addEventListener('mozfullscreenchange', fullScreenChange, false);
+ document.addEventListener('webkitfullscreenchange', fullScreenChange, false);
+
+ document.addEventListener('pointerlockchange', pointerLockChange, false);
+ document.addEventListener('mozpointerlockchange', pointerLockChange, false);
+ document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
+ }
canvas.requestFullScreen = canvas['requestFullScreen'] ||
canvas['mozRequestFullScreen'] ||
@@ -369,7 +405,32 @@ mergeInto(LibraryManager.library, {
canvas.width = width;
canvas.height = height;
if (!noUpdates) Browser.updateResizeListeners();
+ },
+
+ windowedWidth: 0,
+ windowedHeight: 0,
+ setFullScreenCanvasSize: function() {
+ var canvas = Module['canvas'];
+ this.windowedWidth = canvas.width;
+ this.windowedHeight = canvas.height;
+ canvas.width = screen.width;
+ canvas.height = screen.height;
+ var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}};
+ flags = flags | 0x00800000; // set SDL_FULLSCREEN flag
+ {{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}}
+ Browser.updateResizeListeners();
+ },
+
+ setWindowedCanvasSize: function() {
+ var canvas = Module['canvas'];
+ canvas.width = this.windowedWidth;
+ canvas.height = this.windowedHeight;
+ var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}};
+ flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag
+ {{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}}
+ Browser.updateResizeListeners();
}
+
},
emscripten_async_wget: function(url, file, onload, onerror) {
diff --git a/src/library_egl.js b/src/library_egl.js
index a9eb37dd..271ea29e 100644
--- a/src/library_egl.js
+++ b/src/library_egl.js
@@ -83,6 +83,17 @@ var LibraryEGL = {
return 1;
},
+// EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay dpy);
+ eglTerminate: function(display) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ // TODO: Tear down EGL here. Currently a no-op since we don't need to actually do anything here for the browser.
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 1;
+ },
+
// EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);
eglGetConfigs: function(display, configs, config_size, numConfigs) {
return EGL.chooseConfig(display, 0, configs, config_size, numConfigs);
@@ -225,6 +236,20 @@ var LibraryEGL = {
return 62006; /* Magic ID for Emscripten 'default surface' */
},
+ // EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface(EGLDisplay display, EGLSurface surface);
+ eglDestroySurface: function(display, surface) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ if (surface != 62006 /* Magic ID for the only EGLSurface supported by Emscripten */) {
+ EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */);
+ return 1;
+ }
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 1; /* Magic ID for Emscripten 'default surface' */
+ },
+
eglCreateContext__deps: ['glutCreateWindow', '$GL'],
// EGLAPI EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
@@ -234,7 +259,21 @@ var LibraryEGL = {
return 0;
}
- _glutCreateWindow();
+ EGL.windowID = _glutCreateWindow();
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 62004; // Magic ID for Emscripten EGLContext
+ },
+
+ eglDestroyContext__deps: ['glutDestroyWindow', '$GL'],
+
+ // EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext(EGLDisplay dpy, EGLContext context);
+ eglDestroyContext: function(display, context) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+
+ _glutDestroyWindow(EGL.windowID);
EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
return 62004; // Magic ID for Emscripten EGLContext
},
diff --git a/src/library_gc.js b/src/library_gc.js
index 083019ca..f6db74d8 100644
--- a/src/library_gc.js
+++ b/src/library_gc.js
@@ -4,7 +4,7 @@ if (GC_SUPPORT) {
EXPORTED_FUNCTIONS['_realloc'] = 1;
var LibraryGC = {
- $GC__deps: ['sbrk', 'realloc'],
+ $GC__deps: ['sbrk', 'realloc', 'calloc'],
$GC: {
ALLOCATIONS_TO_GC: 1*1024*1024,
diff --git a/src/library_gl.js b/src/library_gl.js
index f7966dab..c6007809 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -19,6 +19,27 @@ var LibraryGL = {
uniforms: [],
shaders: [],
+#if FULL_ES2
+ clientBuffers: [],
+#endif
+ currArrayBuffer: 0,
+ currElementArrayBuffer: 0,
+
+ byteSizeByTypeRoot: 0x1400, // GL_BYTE
+ byteSizeByType: [
+ 1, // GL_BYTE
+ 1, // GL_UNSIGNED_BYTE
+ 2, // GL_SHORT
+ 2, // GL_UNSIGNED_SHORT
+ 4, // GL_INT
+ 4, // GL_UNSIGNED_INT
+ 4, // GL_FLOAT
+ 2, // GL_2_BYTES
+ 3, // GL_3_BYTES
+ 4, // GL_4_BYTES
+ 8 // GL_DOUBLE
+ ],
+
uniformTable: {}, // name => uniform ID. the uID must be identical until relinking, cannot create a new uID each call to glGetUniformLocation
packAlignment: 4, // default alignment is 4 bytes
@@ -37,6 +58,65 @@ var LibraryGL = {
return ret;
},
+ // Temporary buffers
+ MAX_TEMP_BUFFER_SIZE: {{{ GL_MAX_TEMP_BUFFER_SIZE }}},
+ tempBufferIndexLookup: null,
+ tempVertexBuffers: null,
+ tempIndexBuffers: null,
+ tempQuadIndexBuffer: null,
+
+ generateTempBuffers: function(quads) {
+ this.tempBufferIndexLookup = new Uint8Array(this.MAX_TEMP_BUFFER_SIZE+1);
+ this.tempVertexBuffers = [];
+ this.tempIndexBuffers = [];
+ var last = -1, curr = -1;
+ var size = 1;
+ for (var i = 0; i <= this.MAX_TEMP_BUFFER_SIZE; i++) {
+ if (i > size) {
+ size <<= 1;
+ }
+ if (size != last) {
+ curr++;
+ this.tempVertexBuffers[curr] = Module.ctx.createBuffer();
+ Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, this.tempVertexBuffers[curr]);
+ Module.ctx.bufferData(Module.ctx.ARRAY_BUFFER, size, Module.ctx.DYNAMIC_DRAW);
+ Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, null);
+ this.tempIndexBuffers[curr] = Module.ctx.createBuffer();
+ Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.tempIndexBuffers[curr]);
+ Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER, size, Module.ctx.DYNAMIC_DRAW);
+ Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null);
+ last = size;
+ }
+ this.tempBufferIndexLookup[i] = curr;
+ }
+
+ if (quads) {
+ // GL_QUAD indexes can be precalculated
+ this.tempQuadIndexBuffer = Module.ctx.createBuffer();
+ Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.tempQuadIndexBuffer);
+ var numIndexes = this.MAX_TEMP_BUFFER_SIZE >> 1;
+ var quadIndexes = new Uint16Array(numIndexes);
+ var i = 0, v = 0;
+ while (1) {
+ quadIndexes[i++] = v;
+ if (i >= numIndexes) break;
+ quadIndexes[i++] = v+1;
+ if (i >= numIndexes) break;
+ quadIndexes[i++] = v+2;
+ if (i >= numIndexes) break;
+ quadIndexes[i++] = v;
+ if (i >= numIndexes) break;
+ quadIndexes[i++] = v+2;
+ if (i >= numIndexes) break;
+ quadIndexes[i++] = v+3;
+ if (i >= numIndexes) break;
+ v += 4;
+ }
+ Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER, quadIndexes, Module.ctx.STATIC_DRAW);
+ Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null);
+ }
+ },
+
// Linear lookup in one of the tables (buffers, programs, etc.). TODO: consider using a weakmap to make this faster, if it matters
scan: function(table, object) {
for (var item in table) {
@@ -176,12 +256,70 @@ var LibraryGL = {
}
},
+#if FULL_ES2
+ calcBufLength: function(size, type, stride, count) {
+ if (stride > 0) {
+ return count * stride; // XXXvlad this is not exactly correct I don't think
+ }
+ var typeSize = GL.byteSizeByType[type - GL.byteSizeByTypeRoot];
+ return size * typeSize * count;
+ },
+
+ usedTempBuffers: [],
+
+ preDrawHandleClientVertexAttribBindings: function(count) {
+ GL.resetBufferBinding = false;
+
+ var used = GL.usedTempBuffers;
+ used.length = 0;
+
+ // TODO: initial pass to detect ranges we need to upload, might not need an upload per attrib
+ for (var i = 0; i < GL.maxVertexAttribs; ++i) {
+ var cb = GL.clientBuffers[i];
+ if (!cb.enabled) continue;
+
+ GL.resetBufferBinding = true;
+
+ var size = GL.calcBufLength(cb.size, cb.type, cb.stride, count);
+ var index = GL.tempBufferIndexLookup[size];
+ var buf;
+ do {
+#if ASSERTIONS
+ assert(index < GL.tempVertexBuffers.length);
+#endif
+ buf = GL.tempVertexBuffers[index++];
+ } while (used.indexOf(buf) >= 0);
+ used.push(buf);
+ Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, buf);
+ Module.ctx.bufferData(Module.ctx.ARRAY_BUFFER,
+ HEAPU8.subarray(cb.ptr, cb.ptr + size),
+ Module.ctx.DYNAMIC_DRAW);
+ Module.ctx.vertexAttribPointer(i, cb.size, cb.type, cb.normalized, cb.stride, 0);
+ }
+ },
+
+ postDrawHandleClientVertexAttribBindings: function() {
+ if (GL.resetBufferBinding) {
+ Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, GL.buffers[GL.currArrayBuffer]);
+ }
+ },
+#endif
+
initExtensions: function() {
if (GL.initExtensions.done) return;
GL.initExtensions.done = true;
if (!Module.useWebGL) return; // an app might link both gl and 2d backends
+ GL.maxVertexAttribs = Module.ctx.getParameter(Module.ctx.MAX_VERTEX_ATTRIBS);
+#if FULL_ES2
+ for (var i = 0; i < GL.maxVertexAttribs; i++) {
+ GL.clientBuffers[i] = { enabled: false, size: 0, type: 0, normalized: 0, stride: 0, ptr: 0 };
+ }
+
+ GL.generateTempBuffers();
+#endif
+
GL.compressionExt = Module.ctx.getExtension('WEBGL_compressed_texture_s3tc') ||
Module.ctx.getExtension('MOZ_WEBGL_compressed_texture_s3tc') ||
Module.ctx.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc');
@@ -408,7 +546,19 @@ var LibraryGL = {
},
glReadPixels: function(x, y, width, height, format, type, pixels) {
- Module.ctx.readPixels(x, y, width, height, format, type, HEAPU8.subarray(pixels));
+ assert(type == 0x1401 /* GL_UNSIGNED_BYTE */);
+ var sizePerPixel;
+ switch (format) {
+ case 0x1907 /* GL_RGB */:
+ sizePerPixel = 3;
+ break;
+ case 0x1908 /* GL_RGBA */:
+ sizePerPixel = 4;
+ break;
+ default: throw 'unsupported glReadPixels format';
+ }
+ var totalSize = width*height*sizePerPixel;
+ Module.ctx.readPixels(x, y, width, height, format, type, HEAPU8.subarray(pixels, pixels + totalSize));
},
glBindTexture: function(target, texture) {
@@ -444,6 +594,9 @@ var LibraryGL = {
var id = {{{ makeGetValue('buffers', 'i*4', 'i32') }}};
Module.ctx.deleteBuffer(GL.buffers[id]);
GL.buffers[id] = null;
+
+ if (id == GL.currArrayBuffer) GL.currArrayBuffer = 0;
+ if (id == GL.currElementArrayBuffer) GL.currElementArrayBuffer = 0;
}
},
@@ -462,11 +615,9 @@ var LibraryGL = {
},
glIsBuffer: function(buffer) {
- var fb = GL.buffers[buffer];
- if (typeof(fb) == 'undefined') {
- return 0;
- }
- return Module.ctx.isBuffer(fb);
+ var b = GL.buffers[buffer];
+ if (!b) return 0;
+ return Module.ctx.isBuffer(b);
},
glGenRenderbuffers__sig: 'vii',
@@ -497,11 +648,9 @@ var LibraryGL = {
},
glIsRenderbuffer: function(renderbuffer) {
- var fb = GL.renderbuffers[renderbuffer];
- if (typeof(fb) == 'undefined') {
- return 0;
- }
- return Module.ctx.isRenderbuffer(fb);
+ var rb = GL.renderbuffers[renderbuffer];
+ if (!rb) return 0;
+ return Module.ctx.isRenderbuffer(rb);
},
glGetUniformfv: function(program, location, params) {
@@ -542,6 +691,11 @@ var LibraryGL = {
},
glGetVertexAttribfv: function(index, pname, params) {
+#if FULL_ES2
+ if (GL.clientBuffers[index].enabled) {
+ Module.printErr("glGetVertexAttribfv on client-side array: not supported, bad data returned");
+ }
+#endif
var data = Module.ctx.getVertexAttrib(index, pname);
if (typeof data == 'number') {
{{{ makeSetValue('params', '0', 'data', 'float') }}};
@@ -553,6 +707,11 @@ var LibraryGL = {
},
glGetVertexAttribiv: function(index, pname, params) {
+#if FULL_ES2
+ if (GL.clientBuffers[index].enabled) {
+ Module.printErr("glGetVertexAttribiv on client-side array: not supported, bad data returned");
+ }
+#endif
var data = Module.ctx.getVertexAttrib(index, pname);
if (typeof data == 'number' || typeof data == 'boolean') {
{{{ makeSetValue('params', '0', 'data', 'i32') }}};
@@ -564,6 +723,11 @@ var LibraryGL = {
},
glGetVertexAttribPointerv: function(index, pname, pointer) {
+#if FULL_ES2
+ if (GL.clientBuffers[index].enabled) {
+ Module.printErr("glGetVertexAttribPointer on client-side array: not supported, bad data returned");
+ }
+#endif
{{{ makeSetValue('pointer', '0', 'Module.ctx.getVertexAttribOffset(index, pname)', 'i32') }}};
},
@@ -719,6 +883,12 @@ var LibraryGL = {
glBindBuffer__sig: 'vii',
glBindBuffer: function(target, buffer) {
+ if (target == Module.ctx.ARRAY_BUFFER) {
+ GL.currArrayBuffer = buffer;
+ } else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) {
+ GL.currElementArrayBuffer = buffer;
+ }
+
Module.ctx.bindBuffer(target, buffer ? GL.buffers[buffer] : null);
},
@@ -846,11 +1016,9 @@ var LibraryGL = {
},
glIsShader: function(shader) {
- var fb = GL.shaders[shader];
- if (typeof(fb) == 'undefined') {
- return 0;
- }
- return Module.ctx.isShader(fb);
+ var s = GL.shaders[shader];
+ if (!s) return 0;
+ return Module.ctx.isShader(s);
},
glCreateProgram__sig: 'i',
@@ -908,11 +1076,9 @@ var LibraryGL = {
},
glIsProgram: function(program) {
- var fb = GL.programs[program];
- if (typeof(fb) == 'undefined') {
- return 0;
- }
- return Module.ctx.isProgram(fb);
+ var program = GL.programs[program];
+ if (!program) return 0;
+ return Module.ctx.isProgram(program);
},
glBindAttribLocation__sig: 'viii',
@@ -965,9 +1131,7 @@ var LibraryGL = {
glIsFramebuffer__sig: 'ii',
glIsFramebuffer: function(framebuffer) {
var fb = GL.framebuffers[framebuffer];
- if (typeof(fb) == 'undefined') {
- return 0;
- }
+ if (!fb) return 0;
return Module.ctx.isFramebuffer(fb);
},
@@ -983,6 +1147,11 @@ var LibraryGL = {
fogMode: 0x0800, // GL_EXP
fogEnabled: false,
+ // VAO support
+ vaos: [],
+ currentVao: null,
+ enabledVertexAttribArrays: {}, // helps with vao cleanups
+
init: function() {
GLEmulation.fogColor = new Float32Array(4);
@@ -1001,6 +1170,7 @@ var LibraryGL = {
0x809E: 1, // GL_SAMPLE_ALPHA_TO_COVERAGE
0x80A0: 1 // GL_SAMPLE_COVERAGE
};
+
_glEnable = function(cap) {
// Clean up the renderer on any change to the rendering state. The optimization of
// skipping renderer setup is aimed at the case of multiple glDraw* right after each other
@@ -1008,6 +1178,11 @@ var LibraryGL = {
if (cap == 0x0B60 /* GL_FOG */) {
GLEmulation.fogEnabled = true;
return;
+ } else if (cap == 0x0de1 /* GL_TEXTURE_2D */) {
+ // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support
+ // it by forwarding to glEnableClientState
+ _glEnableClientState(cap);
+ return;
} else if (!(cap in validCapabilities)) {
return;
}
@@ -1018,6 +1193,11 @@ var LibraryGL = {
if (cap == 0x0B60 /* GL_FOG */) {
GLEmulation.fogEnabled = false;
return;
+ } else if (cap == 0x0de1 /* GL_TEXTURE_2D */) {
+ // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support
+ // it by forwarding to glDisableClientState
+ _glDisableClientState(cap);
+ return;
} else if (!(cap in validCapabilities)) {
return;
}
@@ -1032,6 +1212,17 @@ var LibraryGL = {
return Module.ctx.isEnabled(cap);
};
+ var glGetBooleanv = _glGetBooleanv;
+ _glGetBooleanv = function(pname, p) {
+ var attrib = GLEmulation.getAttributeFromCapability(pname);
+ if (attrib !== null) {
+ var result = GL.immediate.enabledClientAttributes[attrib];
+ {{{ makeSetValue('p', '0', 'result === true ? 1 : 0', 'i8') }}};
+ return;
+ }
+ glGetBooleanv(pname, p);
+ };
+
var glGetIntegerv = _glGetIntegerv;
_glGetIntegerv = function(pname, params) {
switch (pname) {
@@ -1052,6 +1243,51 @@ var LibraryGL = {
return;
}
case 0x8871: pname = Module.ctx.MAX_COMBINED_TEXTURE_IMAGE_UNITS /* close enough */; break; // GL_MAX_TEXTURE_COORDS
+ case 0x807A: { // GL_VERTEX_ARRAY_SIZE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
+ return;
+ }
+ case 0x807B: { // GL_VERTEX_ARRAY_TYPE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
+ return;
+ }
+ case 0x807C: { // GL_VERTEX_ARRAY_STRIDE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
+ return;
+ }
+ case 0x8081: { // GL_COLOR_ARRAY_SIZE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
+ return;
+ }
+ case 0x8082: { // GL_COLOR_ARRAY_TYPE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
+ return;
+ }
+ case 0x8083: { // GL_COLOR_ARRAY_STRIDE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
+ return;
+ }
+ case 0x8088: { // GL_TEXTURE_COORD_ARRAY_SIZE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
+ return;
+ }
+ case 0x8089: { // GL_TEXTURE_COORD_ARRAY_TYPE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
+ return;
+ }
+ case 0x808A: { // GL_TEXTURE_COORD_ARRAY_STRIDE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
+ return;
+ }
}
glGetIntegerv(pname, params);
};
@@ -1266,19 +1502,12 @@ var LibraryGL = {
_glBindBuffer = function(target, buffer) {
glBindBuffer(target, buffer);
if (target == Module.ctx.ARRAY_BUFFER) {
- GL.currArrayBuffer = buffer;
+ if (GLEmulation.currentVao) {
+ assert(GLEmulation.currentVao.arrayBuffer == buffer || GLEmulation.currentVao.arrayBuffer == 0 || buffer == 0, 'TODO: support for multiple array buffers in vao');
+ GLEmulation.currentVao.arrayBuffer = buffer;
+ }
} else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) {
- GL.currElementArrayBuffer = buffer;
- }
- };
-
- var glDeleteBuffers = _glDeleteBuffers;
- _glDeleteBuffers = function(n, buffers) {
- glDeleteBuffers(n, buffers);
- for (var i = 0; i < n; i++) {
- var buffer = {{{ makeGetValue('buffers', 'i*4', 'i32') }}};
- if (buffer == GL.currArrayBuffer) GL.currArrayBuffer = 0;
- if (buffer == GL.currElementArrayBuffer) GL.currElementArrayBuffer = 0;
+ if (GLEmulation.currentVao) GLEmulation.currentVao.elementArrayBuffer = buffer;
}
};
@@ -1312,6 +1541,44 @@ var LibraryGL = {
}
glHint(target, mode);
};
+
+ var glEnableVertexAttribArray = _glEnableVertexAttribArray;
+ _glEnableVertexAttribArray = function(index) {
+ glEnableVertexAttribArray(index);
+ GLEmulation.enabledVertexAttribArrays[index] = 1;
+ if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1;
+ };
+
+ var glDisableVertexAttribArray = _glDisableVertexAttribArray;
+ _glDisableVertexAttribArray = function(index) {
+ glDisableVertexAttribArray(index);
+ delete GLEmulation.enabledVertexAttribArrays[index];
+ if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index];
+ };
+
+ var glVertexAttribPointer = _glVertexAttribPointer;
+ _glVertexAttribPointer = function(index, size, type, normalized, stride, pointer) {
+ glVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ if (GLEmulation.currentVao) { // TODO: avoid object creation here? likely not hot though
+ GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer];
+ }
+ };
+ },
+
+ getAttributeFromCapability: function(cap) {
+ var attrib = null;
+ switch (cap) {
+ case 0x8078: // GL_TEXTURE_COORD_ARRAY
+ case 0x0de1: // GL_TEXTURE_2D - XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support it
+ attrib = GL.immediate.TEXTURE0 + GL.immediate.clientActiveTexture; break;
+ case 0x8074: // GL_VERTEX_ARRAY
+ attrib = GL.immediate.VERTEX; break;
+ case 0x8075: // GL_NORMAL_ARRAY
+ attrib = GL.immediate.NORMAL; break;
+ case 0x8076: // GL_COLOR_ARRAY
+ attrib = GL.immediate.COLOR; break;
+ }
+ return attrib;
},
getProcAddress: function(name) {
@@ -1375,6 +1642,9 @@ var LibraryGL = {
case 'glIsFramebuffer': ret = {{{ Functions.getIndex('_glIsFramebuffer', true) }}}; break;
case 'glCheckFramebufferStatus': ret = {{{ Functions.getIndex('_glCheckFramebufferStatus', true) }}}; break;
case 'glRenderbufferStorage': ret = {{{ Functions.getIndex('_glRenderbufferStorage', true) }}}; break;
+ case 'glGenVertexArrays': ret = {{{ Functions.getIndex('_glGenVertexArrays', true) }}}; break;
+ case 'glDeleteVertexArrays': ret = {{{ Functions.getIndex('_glDeleteVertexArrays', true) }}}; break;
+ case 'glBindVertexArray': ret = {{{ Functions.getIndex('_glBindVertexArray', true) }}}; break;
}
if (!ret) Module.printErr('WARNING: getProcAddress failed for ' + name);
return ret;
@@ -1427,10 +1697,24 @@ var LibraryGL = {
assert(id == 0);
},
+ glGetPointerv: function(name, p) {
+ var attribute;
+ switch(name) {
+ case 0x808E: // GL_VERTEX_ARRAY_POINTER
+ attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; break;
+ case 0x8090: // GL_COLOR_ARRAY_POINTER
+ attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break;
+ case 0x8092: // GL_TEXTURE_COORD_ARRAY_POINTER
+ attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0]; break;
+ default: throw 'TODO: glGetPointerv for ' + name;
+ }
+ {{{ makeSetValue('p', '0', 'attribute ? attribute.pointer : 0', 'i32') }}};
+ },
+
// GL Immediate mode
$GLImmediate__postset: 'GL.immediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(function() { GL.immediate.init() });',
- $GLImmediate__deps: ['$Browser', '$GL'],
+ $GLImmediate__deps: ['$Browser', '$GL', '$GLEmulation'],
$GLImmediate: {
MAX_TEXTURES: 7,
@@ -1479,17 +1763,6 @@ var LibraryGL = {
clientActiveTexture: 0,
clientColor: null,
- byteSizeByTypeRoot: 0x1400, // GL_BYTE
- byteSizeByType: [
- 1, // GL_BYTE
- 1, // GL_UNSIGNED_BYTE
- 2, // GL_SHORT
- 2, // GL_UNSIGNED_SHORT
- 4, // GL_INT
- 4, // GL_UNSIGNED_INT
- 4 // GL_FLOAT
- ],
-
setClientAttribute: function(name, size, type, stride, pointer) {
var attrib = this.clientAttributes[name];
attrib.name = name;
@@ -1500,62 +1773,6 @@ var LibraryGL = {
this.modifiedClientAttributes = true;
},
- // Temporary buffers
- MAX_TEMP_BUFFER_SIZE: 2*1024*1024,
- tempBufferIndexLookup: null,
- tempVertexBuffers: null,
- tempIndexBuffers: null,
- tempQuadIndexBuffer: null,
-
- generateTempBuffers: function() {
- this.tempBufferIndexLookup = new Uint8Array(this.MAX_TEMP_BUFFER_SIZE+1);
- this.tempVertexBuffers = [];
- this.tempIndexBuffers = [];
- var last = -1, curr = -1;
- var size = 1;
- for (var i = 0; i <= this.MAX_TEMP_BUFFER_SIZE; i++) {
- if (i > size) {
- size <<= 1;
- }
- if (size != last) {
- curr++;
- this.tempVertexBuffers[curr] = Module.ctx.createBuffer();
- Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, this.tempVertexBuffers[curr]);
- Module.ctx.bufferData(Module.ctx.ARRAY_BUFFER, size, Module.ctx.DYNAMIC_DRAW);
- Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, null);
- this.tempIndexBuffers[curr] = Module.ctx.createBuffer();
- Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.tempIndexBuffers[curr]);
- Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER, size, Module.ctx.DYNAMIC_DRAW);
- Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null);
- last = size;
- }
- this.tempBufferIndexLookup[i] = curr;
- }
- // GL_QUAD indexes can be precalculated
- this.tempQuadIndexBuffer = Module.ctx.createBuffer();
- Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.tempQuadIndexBuffer);
- var numIndexes = this.MAX_TEMP_BUFFER_SIZE >> 1;
- var quadIndexes = new Uint16Array(numIndexes);
- var i = 0, v = 0;
- while (1) {
- quadIndexes[i++] = v;
- if (i >= numIndexes) break;
- quadIndexes[i++] = v+1;
- if (i >= numIndexes) break;
- quadIndexes[i++] = v+2;
- if (i >= numIndexes) break;
- quadIndexes[i++] = v;
- if (i >= numIndexes) break;
- quadIndexes[i++] = v+2;
- if (i >= numIndexes) break;
- quadIndexes[i++] = v+3;
- if (i >= numIndexes) break;
- v += 4;
- }
- Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER, quadIndexes, Module.ctx.STATIC_DRAW);
- Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null);
- },
-
// Renderers
addRendererComponent: function(name, size, type) {
if (!this.rendererComponents[name]) {
@@ -1565,7 +1782,7 @@ var LibraryGL = {
#endif
this.enabledClientAttributes[name] = true;
this.setClientAttribute(name, size, type, 0, this.rendererComponentPointer);
- this.rendererComponentPointer += size * this.byteSizeByType[type - this.byteSizeByTypeRoot];
+ this.rendererComponentPointer += size * GL.byteSizeByType[type - GL.byteSizeByTypeRoot];
} else {
this.rendererComponents[name]++;
}
@@ -1589,7 +1806,7 @@ var LibraryGL = {
cacheItem = temp ? temp : (cacheItem[attribute.name] = GL.immediate.rendererCacheItemTemplate.slice());
temp = cacheItem[attribute.size];
cacheItem = temp ? temp : (cacheItem[attribute.size] = GL.immediate.rendererCacheItemTemplate.slice());
- var typeIndex = attribute.type - GL.immediate.byteSizeByTypeRoot; // ensure it starts at 0 to keep the cache items dense
+ var typeIndex = attribute.type - GL.byteSizeByTypeRoot; // ensure it starts at 0 to keep the cache items dense
temp = cacheItem[typeIndex];
cacheItem = temp ? temp : (cacheItem[typeIndex] = GL.immediate.rendererCacheItemTemplate.slice());
}
@@ -1776,35 +1993,43 @@ var LibraryGL = {
if (!GL.currArrayBuffer) {
var start = GL.immediate.firstVertex*GL.immediate.stride;
var end = GL.immediate.lastVertex*GL.immediate.stride;
- assert(end <= GL.immediate.MAX_TEMP_BUFFER_SIZE, 'too much vertex data');
- arrayBuffer = GL.immediate.tempVertexBuffers[GL.immediate.tempBufferIndexLookup[end]];
+ assert(end <= GL.MAX_TEMP_BUFFER_SIZE, 'too much vertex data');
+ arrayBuffer = GL.tempVertexBuffers[GL.tempBufferIndexLookup[end]];
// TODO: consider using the last buffer we bound, if it was larger. downside is larger buffer, but we might avoid rebinding and preparing
} else {
arrayBuffer = GL.currArrayBuffer;
}
// If the array buffer is unchanged and the renderer as well, then we can avoid all the work here
- // XXX We use some heuristics here, and this may not work in all cases. Try disabling this if you
- // have odd glitches (by setting canSkip always to 0, or even cleaning up the renderer right
- // after rendering)
+ // XXX We use some heuristics here, and this may not work in all cases. Try disabling GL_UNSAFE_OPTS if you
+ // have odd glitches
+#if GL_UNSAFE_OPTS
var lastRenderer = GL.immediate.lastRenderer;
var canSkip = this == lastRenderer &&
arrayBuffer == GL.immediate.lastArrayBuffer &&
(GL.currProgram || this.program) == GL.immediate.lastProgram &&
!GL.immediate.matricesModified;
if (!canSkip && lastRenderer) lastRenderer.cleanup();
+#endif
if (!GL.currArrayBuffer) {
// Bind the array buffer and upload data after cleaning up the previous renderer
+#if GL_UNSAFE_OPTS
+ // Potentially unsafe, since lastArrayBuffer might not reflect the true array buffer in code that mixes immediate/non-immediate
if (arrayBuffer != GL.immediate.lastArrayBuffer) {
+#endif
Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, arrayBuffer);
+#if GL_UNSAFE_OPTS
}
+#endif
Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER, start, GL.immediate.vertexData.subarray(start >> 2, end >> 2));
}
+#if GL_UNSAFE_OPTS
if (canSkip) return;
GL.immediate.lastRenderer = this;
GL.immediate.lastArrayBuffer = arrayBuffer;
GL.immediate.lastProgram = GL.currProgram || this.program;
GL.immediate.matricesModified = false;
+#endif
if (!GL.currProgram) {
Module.ctx.useProgram(this.program);
@@ -1880,9 +2105,11 @@ var LibraryGL = {
Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, null);
}
+#if GL_UNSAFE_OPTS
GL.immediate.lastRenderer = null;
GL.immediate.lastArrayBuffer = null;
GL.immediate.lastProgram = null;
+#endif
GL.immediate.matricesModified = true;
}
};
@@ -1958,12 +2185,12 @@ var LibraryGL = {
this.rendererCache = this.rendererCacheItemTemplate.slice();
// Buffers for data
- this.tempData = new Float32Array(this.MAX_TEMP_BUFFER_SIZE >> 2);
- this.indexData = new Uint16Array(this.MAX_TEMP_BUFFER_SIZE >> 1);
+ this.tempData = new Float32Array(GL.MAX_TEMP_BUFFER_SIZE >> 2);
+ this.indexData = new Uint16Array(GL.MAX_TEMP_BUFFER_SIZE >> 1);
this.vertexDataU8 = new Uint8Array(this.tempData.buffer);
- this.generateTempBuffers();
+ GL.generateTempBuffers(true);
this.clientColor = new Float32Array([1, 1, 1, 1]);
},
@@ -1997,22 +2224,61 @@ var LibraryGL = {
#endif
if (attribute.stride) stride = attribute.stride;
}
-
- var bytes = 0;
- for (var i = 0; i < attributes.length; i++) {
- var attribute = attributes[i];
- if (!attribute) break;
- attribute.offset = attribute.pointer - start;
- if (attribute.offset > bytes) { // ensure we start where we should
- assert((attribute.offset - bytes)%4 == 0); // XXX assuming 4-alignment
- bytes += attribute.offset - bytes;
+ var bytes = 0; // total size in bytes
+ if (!stride && !beginEnd) {
+ // beginEnd can not have stride in the attributes, that is fine. otherwise,
+ // no stride means that all attributes are in fact packed. to keep the rest of
+ // our emulation code simple, we perform unpacking/restriding here. this adds overhead, so
+ // it is a good idea to not hit this!
+#if ASSERTIONS
+ Runtime.warnOnce('Unpacking/restriding attributes, this is not fast');
+#endif
+ if (!GL.immediate.restrideBuffer) GL.immediate.restrideBuffer = _malloc(GL.MAX_TEMP_BUFFER_SIZE);
+ start = GL.immediate.restrideBuffer;
+#if ASSERTIONS
+ assert(start % 4 == 0);
+#endif
+ // calculate restrided offsets and total size
+ for (var i = 0; i < attributes.length; i++) {
+ var attribute = attributes[i];
+ if (!attribute) break;
+ var size = attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot];
+ if (size % 4 != 0) size += 4 - (size % 4); // align everything
+ attribute.offset = bytes;
+ bytes += size;
+ }
+#if ASSERTIONS
+ assert(count*bytes <= GL.MAX_TEMP_BUFFER_SIZE);
+#endif
+ // copy out the data (we need to know the stride for that, and define attribute.pointer
+ for (var i = 0; i < attributes.length; i++) {
+ var attribute = attributes[i];
+ if (!attribute) break;
+ var size4 = Math.floor((attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot])/4);
+ for (var j = 0; j < count; j++) {
+ for (var k = 0; k < size4; k++) { // copy in chunks of 4 bytes, our alignment makes this possible
+ HEAP32[((start + attribute.offset + bytes*j)>>2) + k] = HEAP32[(attribute.pointer>>2) + j*size4 + k];
+ }
+ }
+ attribute.pointer = start + attribute.offset;
+ }
+ } else {
+ // normal situation, everything is strided and in the same buffer
+ for (var i = 0; i < attributes.length; i++) {
+ var attribute = attributes[i];
+ if (!attribute) break;
+ attribute.offset = attribute.pointer - start;
+ if (attribute.offset > bytes) { // ensure we start where we should
+ assert((attribute.offset - bytes)%4 == 0); // XXX assuming 4-alignment
+ bytes += attribute.offset - bytes;
+ }
+ bytes += attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot];
+ if (bytes % 4 != 0) bytes += 4 - (bytes % 4); // XXX assuming 4-alignment
+ }
+ assert(beginEnd || bytes <= stride); // if not begin-end, explicit stride should make sense with total byte size
+ if (bytes < stride) { // ensure the size is that of the stride
+ bytes = stride;
}
- bytes += attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot];
- if (bytes % 4 != 0) bytes += 4 - (bytes % 4); // XXX assuming 4-alignment
- }
- assert(stride == 0 || bytes <= stride);
- if (bytes < stride) { // ensure the size is that of the stride
- bytes = stride;
}
GL.immediate.stride = bytes;
@@ -2026,6 +2292,9 @@ var LibraryGL = {
},
flush: function(numProvidedIndexes, startIndex, ptr) {
+#if ASSERTIONS
+ assert(numProvidedIndexes >= 0 || !numProvidedIndexes);
+#endif
startIndex = startIndex || 0;
ptr = ptr || 0;
@@ -2053,8 +2322,8 @@ var LibraryGL = {
}
if (!GL.currElementArrayBuffer) {
// If no element array buffer is bound, then indices is a literal pointer to clientside data
- assert(numProvidedIndexes << 1 <= GL.immediate.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (a)');
- var indexBuffer = GL.immediate.tempIndexBuffers[GL.immediate.tempBufferIndexLookup[numProvidedIndexes << 1]];
+ assert(numProvidedIndexes << 1 <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (a)');
+ var indexBuffer = GL.tempIndexBuffers[GL.tempBufferIndexLookup[numProvidedIndexes << 1]];
Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, indexBuffer);
Module.ctx.bufferSubData(Module.ctx.ELEMENT_ARRAY_BUFFER, 0, {{{ makeHEAPView('U16', 'ptr', 'ptr + (numProvidedIndexes << 1)') }}});
ptr = 0;
@@ -2069,8 +2338,8 @@ var LibraryGL = {
ptr = GL.immediate.firstVertex*3;
var numQuads = numVertexes / 4;
numIndexes = numQuads * 6; // 0 1 2, 0 2 3 pattern
- assert(ptr + (numIndexes << 1) <= GL.immediate.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (b)');
- Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.tempQuadIndexBuffer);
+ assert(ptr + (numIndexes << 1) <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (b)');
+ Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, GL.tempQuadIndexBuffer);
emulatedElementArrayBuffer = true;
}
@@ -2085,6 +2354,10 @@ var LibraryGL = {
if (emulatedElementArrayBuffer) {
Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GL.currElementArrayBuffer] || null);
}
+
+#if GL_UNSAFE_OPTS == 0
+ renderer.cleanup();
+#endif
}
},
@@ -2120,7 +2393,7 @@ var LibraryGL = {
GL.immediate.vertexData[GL.immediate.vertexCounter++] = y;
GL.immediate.vertexData[GL.immediate.vertexCounter++] = z || 0;
#if ASSERTIONS
- assert(GL.immediate.vertexCounter << 2 < GL.immediate.MAX_TEMP_BUFFER_SIZE);
+ assert(GL.immediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE);
#endif
GL.immediate.addRendererComponent(GL.immediate.VERTEX, 3, Module.ctx.FLOAT);
},
@@ -2134,8 +2407,10 @@ var LibraryGL = {
glVertex2fv: function(p) {
_glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, 0);
},
+
+ glVertex3i: 'glVertex3f',
- glVertex2i: function() { throw 'glVertex2i: TODO' },
+ glVertex2i: 'glVertex3f',
glTexCoord2i: function(u, v) {
#if ASSERTIONS
@@ -2299,25 +2574,21 @@ var LibraryGL = {
// ClientState/gl*Pointer
glEnableClientState: function(cap, disable) {
- var attrib;
- switch(cap) {
- case 0x8078: // GL_TEXTURE_COORD_ARRAY
- attrib = GL.immediate.TEXTURE0 + GL.immediate.clientActiveTexture; break;
- case 0x8074: // GL_VERTEX_ARRAY
- attrib = GL.immediate.VERTEX; break;
- case 0x8075: // GL_NORMAL_ARRAY
- attrib = GL.immediate.NORMAL; break;
- case 0x8076: // GL_COLOR_ARRAY
- attrib = GL.immediate.COLOR; break;
- default:
- throw 'unhandled clientstate: ' + cap;
+ var attrib = GLEmulation.getAttributeFromCapability(cap);
+ if (attrib === null) {
+#if ASSERTIONS
+ Module.printErr('WARNING: unhandled clientstate: ' + cap);
+#endif
+ return;
}
if (disable && GL.immediate.enabledClientAttributes[attrib]) {
GL.immediate.enabledClientAttributes[attrib] = false;
GL.immediate.totalEnabledClientAttributes--;
+ if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledClientStates[cap];
} else if (!disable && !GL.immediate.enabledClientAttributes[attrib]) {
GL.immediate.enabledClientAttributes[attrib] = true;
GL.immediate.totalEnabledClientAttributes++;
+ if (GLEmulation.currentVao) GLEmulation.currentVao.enabledClientStates[cap] = 1;
}
GL.immediate.modifiedClientAttributes = true;
},
@@ -2344,6 +2615,63 @@ var LibraryGL = {
GL.immediate.clientActiveTexture = texture - 0x84C0; // GL_TEXTURE0
},
+ // Vertex array object (VAO) support. TODO: when the WebGL extension is popular, use that and remove this code and GL.vaos
+ glGenVertexArrays__deps: ['$GLEMulation'],
+ glGenVertexArrays__sig: ['vii'],
+ glGenVertexArrays: function(n, vaos) {
+ for (var i = 0; i < n; i++) {
+ var id = GL.getNewId(GLEmulation.vaos);
+ GLEmulation.vaos[id] = {
+ id: id,
+ arrayBuffer: 0,
+ elementArrayBuffer: 0,
+ enabledVertexAttribArrays: {},
+ vertexAttribPointers: {},
+ enabledClientStates: {},
+ };
+ {{{ makeSetValue('vaos', 'i*4', 'id', 'i32') }}};
+ }
+ },
+ glDeleteVertexArrays__sig: ['vii'],
+ glDeleteVertexArrays: function(n, vaos) {
+ for (var i = 0; i < n; i++) {
+ var id = {{{ makeGetValue('vaos', 'i*4', 'i32') }}};
+ GLEmulation.vaos[id] = null;
+ if (GLEmulation.currentVao && GLEmulation.currentVao.id == id) GLEmulation.currentVao = null;
+ }
+ },
+ glBindVertexArray__sig: ['vi'],
+ glBindVertexArray: function(vao) {
+ // undo vao-related things, wipe the slate clean, both for vao of 0 or an actual vao
+ GLEmulation.currentVao = null; // make sure the commands we run here are not recorded
+ if (GL.immediate.lastRenderer) GL.immediate.lastRenderer.cleanup();
+ _glBindBuffer(Module.ctx.ARRAY_BUFFER, 0); // XXX if one was there before we were bound?
+ _glBindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, 0);
+ for (var vaa in GLEmulation.enabledVertexAttribArrays) {
+ Module.ctx.disableVertexAttribArray(vaa);
+ }
+ GLEmulation.enabledVertexAttribArrays = {};
+ GL.immediate.enabledClientAttributes = [0, 0];
+ GL.immediate.totalEnabledClientAttributes = 0;
+ GL.immediate.modifiedClientAttributes = true;
+ if (vao) {
+ // replay vao
+ var info = GLEmulation.vaos[vao];
+ _glBindBuffer(Module.ctx.ARRAY_BUFFER, info.arrayBuffer); // XXX overwrite current binding?
+ _glBindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, info.elementArrayBuffer);
+ for (var vaa in info.enabledVertexAttribArrays) {
+ _glEnableVertexAttribArray(vaa);
+ }
+ for (var vaa in info.vertexAttribPointers) {
+ _glVertexAttribPointer.apply(null, info.vertexAttribPointers[vaa]);
+ }
+ for (var attrib in info.enabledClientStates) {
+ _glEnableClientState(attrib|0);
+ }
+ GLEmulation.currentVao = info; // set currentVao last, so the commands we ran here were not recorded
+ }
+ },
+
// OpenGL Immediate Mode matrix routines.
// Note that in the future we might make these available only in certain modes.
glMatrixMode__deps: ['$GL', '$GLImmediateSetup', '$GLEmulation'], // emulation is not strictly needed, this is a workaround
@@ -2434,6 +2762,7 @@ var LibraryGL = {
GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix],
GL.immediate.matrix.lib.mat4.frustum(left, right, bottom, top_, nearVal, farVal));
},
+ glFrustumf: 'glFrustum',
glOrtho: function(left, right, bottom, top_, nearVal, farVal) {
GL.immediate.matricesModified = true;
@@ -2464,8 +2793,9 @@ var LibraryGL = {
gluPerspective: function(fov, aspect, near, far) {
GL.immediate.matricesModified = true;
- GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix],
- GL.immediate.matrix.lib.mat4.perspective(fov, aspect, near, far, GL.immediate.currentMatrix));
+ GL.immediate.matrix[GL.immediate.currentMatrix] =
+ GL.immediate.matrix.lib.mat4.perspective(fov, aspect, near, far,
+ GL.immediate.matrix[GL.immediate.currentMatrix]);
},
gluLookAt: function(ex, ey, ez, cx, cy, cz, ux, uy, uz) {
@@ -2534,36 +2864,140 @@ var LibraryGL = {
glTexGeni: function() { throw 'glTexGeni: TODO' },
glTexGenfv: function() { throw 'glTexGenfv: TODO' },
- glTexEnvi: function() { throw 'glTexEnvi: TODO' },
- glTexEnvfv: function() { throw 'glTexEnvfv: TODO' },
+ glTexEnvi: function() { Runtime.warnOnce('glTexEnvi: TODO') },
+ glTexEnvfv: function() { Runtime.warnOnce('glTexEnvfv: TODO') },
glTexImage1D: function() { throw 'glTexImage1D: TODO' },
glTexCoord3f: function() { throw 'glTexCoord3f: TODO' },
glGetTexLevelParameteriv: function() { throw 'glGetTexLevelParameteriv: TODO' },
- // signatures of simple pass-through functions, see later
- glActiveTexture__sig: 'vi',
+ glShadeModel: function() { Runtime.warnOnce('TODO: glShadeModel') },
+
+ // GLES2 emulation
+
+ glVertexAttribPointer__sig: 'viiiiii',
+ glVertexAttribPointer: function(index, size, type, normalized, stride, ptr) {
+#if FULL_ES2
+ var cb = GL.clientBuffers[index];
+#if ASSERTIONS
+ assert(cb, index);
+#endif
+ if (!GL.currArrayBuffer) {
+ cb.size = size;
+ cb.type = type;
+ cb.normalized = normalized;
+ cb.stride = stride;
+ cb.ptr = ptr;
+ return;
+ }
+ cb.enabled = false;
+#endif
+ Module.ctx.vertexAttribPointer(index, size, type, normalized, stride, ptr);
+ },
+
glEnableVertexAttribArray__sig: 'vi',
+ glEnableVertexAttribArray: function(index) {
+#if FULL_ES2
+ var cb = GL.clientBuffers[index];
+#if ASSERTIONS
+ assert(cb, index);
+#endif
+ cb.enabled = true;
+#endif
+ Module.ctx.enableVertexAttribArray(index);
+ },
+
glDisableVertexAttribArray__sig: 'vi',
- glVertexAttribPointer__sig: 'viiiiii',
+ glDisableVertexAttribArray: function(index) {
+#if FULL_ES2
+ var cb = GL.clientBuffers[index];
+#if ASSERTIONS
+ assert(cb, index);
+#endif
+ cb.enabled = false;
+#endif
+ Module.ctx.disableVertexAttribArray(index);
+ },
+
+ glDrawArrays: function(mode, first, count) {
+#if FULL_ES2
+ // bind any client-side buffers
+ GL.preDrawHandleClientVertexAttribBindings(first + count);
+#endif
+
+ Module.ctx.drawArrays(mode, first, count);
+
+#if FULL_ES2
+ GL.postDrawHandleClientVertexAttribBindings();
+#endif
+ },
+
+ glDrawElements: function(mode, count, type, indices) {
+#if FULL_ES2
+ var buf;
+ if (!GL.currElementArrayBuffer) {
+ var size = GL.calcBufLength(1, type, 0, count);
+ buf = GL.tempIndexBuffers[GL.tempBufferIndexLookup[size]];
+ Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, buf);
+ Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER,
+ HEAPU8.subarray(indices, indices + size),
+ Module.ctx.DYNAMIC_DRAW);
+ // the index is now 0
+ indices = 0;
+ }
+
+ // bind any client-side buffers
+ GL.preDrawHandleClientVertexAttribBindings(count);
+#endif
+
+ Module.ctx.drawElements(mode, count, type, indices);
+
+#if FULL_ES2
+ GL.postDrawHandleClientVertexAttribBindings(count);
+
+ if (!GL.currElementArrayBuffer) {
+ Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null);
+ }
+#endif
+ },
+
+ // signatures of simple pass-through functions, see later
+
+ glActiveTexture__sig: 'vi',
glCheckFramebufferStatus__sig: 'ii',
glRenderbufferStorage__sig: 'viiii',
+
+ // Open GLES1.1 compatibility
+
+ glGenFramebuffersOES : 'glGenFramebuffers',
+ glGenRenderbuffersOES : 'glGenRenderbuffers',
+ glBindFramebufferOES : 'glBindFramebuffer',
+ glBindRenderbufferOES : 'glBindRenderbuffer',
+ glGetRenderbufferParameterivOES : 'glGetRenderbufferParameteriv',
+ glFramebufferRenderbufferOES : 'glFramebufferRenderbuffer',
+ glRenderbufferStorageOES : 'glRenderbufferStorage',
+ glCheckFramebufferStatusOES : 'glCheckFramebufferStatus',
+ glDeleteFramebuffersOES : 'glDeleteFramebuffers',
+ glDeleteRenderbuffersOES : 'glDeleteRenderbuffers',
+ glGenVertexArraysOES: 'glGenVertexArrays',
+ glDeleteVertexArraysOES: 'glDeleteVertexArrays',
+ glBindVertexArrayOES: 'glBindVertexArray',
+ glFramebufferTexture2DOES: 'glFramebufferTexture2D'
};
// Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name
-[[0, 'shadeModel getError* finish flush'],
- [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'],
+[[0, 'getError* finish flush'],
+ [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'],
[2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint polygonOffset'],
- [3, 'texParameteri texParameterf drawArrays vertexAttrib2f stencilFunc stencilOp'],
- [4, 'viewport clearColor scissor vertexAttrib3f colorMask drawElements renderbufferStorage blendFuncSeparate blendColor stencilFuncSeparate stencilOpSeparate'],
+ [3, 'texParameteri texParameterf vertexAttrib2f stencilFunc stencilOp'],
+ [4, 'viewport clearColor scissor vertexAttrib3f colorMask renderbufferStorage blendFuncSeparate blendColor stencilFuncSeparate stencilOpSeparate'],
[5, 'vertexAttrib4f'],
- [6, 'vertexAttribPointer'],
[8, 'copyTexImage2D copyTexSubImage2D']].forEach(function(data) {
var num = data[0];
var names = data[1];
var args = range(num).map(function(i) { return 'x' + i }).join(', ');
- var plainStub = '(function(' + args + ') { ' + (num > 0 ? 'Module.ctx.NAME(' + args + ')' : '') + ' })';
- var returnStub = '(function(' + args + ') { ' + (num > 0 ? 'return Module.ctx.NAME(' + args + ')' : '') + ' })';
+ var plainStub = '(function(' + args + ') { Module.ctx.NAME(' + args + ') })';
+ var returnStub = '(function(' + args + ') { return Module.ctx.NAME(' + args + ') })';
names.split(' ').forEach(function(name) {
var stub = plainStub;
if (name[name.length-1] == '*') {
@@ -2596,5 +3030,10 @@ LibraryGL.$GLEmulation__deps.push(function() {
for (var func in Functions.getIndex.tentative) Functions.getIndex(func);
});
+if (FORCE_GL_EMULATION) {
+ LibraryGL.glDrawElements__deps = LibraryGL.glDrawElements__deps.concat('$GLEmulation');
+ LibraryGL.glDrawArrays__deps = LibraryGL.glDrawArrays__deps.concat('$GLEmulation');
+}
+
mergeInto(LibraryManager.library, LibraryGL);
diff --git a/src/library_glut.js b/src/library_glut.js
index 2e662698..bb4dfefa 100644
--- a/src/library_glut.js
+++ b/src/library_glut.js
@@ -381,6 +381,12 @@ var LibraryGLUT = {
return 1;
},
+ glutDestroyWindow__deps: ['$Browser'],
+ glutDestroyWindow: function(name) {
+ Module.ctx = Browser.destroyContext(Module['canvas'], true, true);
+ return 1;
+ },
+
glutReshapeWindow__deps: ['$GLUT', 'glutPostRedisplay'],
glutReshapeWindow: function(width, height) {
GLUT.cancelFullScreen();
diff --git a/src/library_sdl.js b/src/library_sdl.js
index cfab6410..5033b27e 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -44,6 +44,8 @@ var LibrarySDL = {
ctrlKey: false,
altKey: false,
+ textInput: false,
+
startTime: null,
mouseX: 0,
mouseY: 0,
@@ -173,6 +175,11 @@ var LibrarySDL = {
['i16', 'mod'],
['i32', 'unicode']
]),
+ TextInputEvent: Runtime.generateStructInfo([
+ ['i32', 'type'],
+ ['i32', 'windowID'],
+ ['b256', 'text'],
+ ]),
MouseMotionEvent: Runtime.generateStructInfo([
['i32', 'type'],
['i32', 'windowID'],
@@ -373,7 +380,7 @@ var LibrarySDL = {
}
}
// fall through
- case 'keydown': case 'keyup': case 'mousedown': case 'mouseup': case 'DOMMouseScroll': case 'mousewheel':
+ case 'keydown': case 'keyup': case 'keypress': case 'mousedown': case 'mouseup': case 'DOMMouseScroll': case 'mousewheel':
if (event.type == 'DOMMouseScroll' || event.type == 'mousewheel') {
var button = (event.type == 'DOMMouseScroll' ? event.detail : -event.wheelDelta) > 0 ? 4 : 3;
var event2 = {
@@ -397,6 +404,10 @@ var LibrarySDL = {
SDL.DOMButtons[event.button] = 0;
}
+ if (event.type == 'keypress' && !SDL.textInput) {
+ break;
+ }
+
SDL.events.push(event);
if (SDL.events.length >= 10000) {
Module.printErr('SDL event queue full, dropping earliest event');
@@ -476,6 +487,15 @@ var LibrarySDL = {
break;
}
+ case 'keypress': {
+ {{{ makeSetValue('ptr', 'SDL.structs.TextInputEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}
+ // Not filling in windowID for now
+ var cStr = intArrayFromString(String.fromCharCode(event.charCode));
+ for (var i = 0; i < cStr.length; ++i) {
+ {{{ makeSetValue('ptr', 'SDL.structs.TextInputEvent.text + i', 'cStr[i]', 'i8') }}};
+ }
+ break;
+ }
case 'mousedown': case 'mouseup':
if (event.type == 'mousedown') {
// SDL_BUTTON(x) is defined as (1 << ((x)-1)). SDL buttons are 1-3,
@@ -607,15 +627,18 @@ var LibrarySDL = {
SDL_Init: function(what) {
SDL.startTime = Date.now();
// capture all key events. we just keep down and up, but also capture press to prevent default actions
- document.onkeydown = SDL.receiveEvent;
- document.onkeyup = SDL.receiveEvent;
- document.onkeypress = SDL.receiveEvent;
+ if (!Module['doNotCaptureKeyboard']) {
+ document.onkeydown = SDL.receiveEvent;
+ document.onkeyup = SDL.receiveEvent;
+ document.onkeypress = SDL.receiveEvent;
+ }
window.onunload = SDL.receiveEvent;
SDL.keyboardState = _malloc(0x10000);
_memset(SDL.keyboardState, 0, 0x10000);
// Initialize this structure carefully for closure
SDL.DOMEventToSDLEvent['keydown'] = 0x300 /* SDL_KEYDOWN */;
SDL.DOMEventToSDLEvent['keyup'] = 0x301 /* SDL_KEYUP */;
+ SDL.DOMEventToSDLEvent['keypress'] = 0x303 /* SDL_TEXTINPUT */;
SDL.DOMEventToSDLEvent['mousedown'] = 0x401 /* SDL_MOUSEBUTTONDOWN */;
SDL.DOMEventToSDLEvent['mouseup'] = 0x402 /* SDL_MOUSEBUTTONUP */;
SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
@@ -638,8 +661,8 @@ var LibrarySDL = {
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*0', '0', '0', 'i32') }}} // TODO
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*1', '0', '0', 'i32') }}} // TODO
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*2', '0', '0', 'void*') }}}
- {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*3', '0', 'SDL.defaults.width', 'i32') }}}
- {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*4', '0', 'SDL.defaults.height', 'i32') }}}
+ {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*3', '0', 'Module["canvas"].width', 'i32') }}}
+ {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*4', '0', 'Module["canvas"].height', 'i32') }}}
return ret;
},
@@ -896,7 +919,26 @@ var LibrarySDL = {
},
SDL_ShowCursor: function(toggle) {
- // TODO
+ switch (toggle) {
+ case 0: // SDL_DISABLE
+ if (Browser.isFullScreen) { // only try to lock the pointer when in full screen mode
+ Module['canvas'].requestPointerLock();
+ return 0;
+ } else { // else return SDL_ENABLE to indicate the failure
+ return 1;
+ }
+ break;
+ case 1: // SDL_ENABLE
+ Module['canvas'].exitPointerLock();
+ return 1;
+ break;
+ case -1: // SDL_QUERY
+ return !Browser.pointerLock;
+ break;
+ default:
+ console.log( "SDL_ShowCursor called with unknown toggle parameter value: " + toggle + "." );
+ break;
+ }
},
SDL_GetError: function() {
@@ -1052,6 +1094,15 @@ var LibrarySDL = {
},
SDL_WM_GrabInput: function() {},
+
+ SDL_WM_ToggleFullScreen: function(surf) {
+ if (Browser.isFullScreen) {
+ Module['canvas'].cancelFullScreen();
+ return 1;
+ } else {
+ return 0;
+ }
+ },
// SDL_Image
@@ -1077,7 +1128,9 @@ var LibrarySDL = {
}
var surf = SDL.makeSurface(raw.width, raw.height, 0, false, 'load:' + filename);
var surfData = SDL.surfaces[surf];
+ surfData.ctx.globalCompositeOperation = "copy";
surfData.ctx.drawImage(raw, 0, 0, raw.width, raw.height, 0, 0, raw.width, raw.height);
+ surfData.ctx.globalCompositeOperation = "source-over";
// XXX SDL does not specify that loaded images must have available pixel data, in fact
// there are cases where you just want to blit them, so you just need the hardware
// accelerated version. However, code everywhere seems to assume that the pixels
@@ -1171,11 +1224,21 @@ var LibrarySDL = {
SDL_CondWait: function() {},
SDL_DestroyCond: function() {},
- SDL_StartTextInput: function() {}, // TODO
- SDL_StopTextInput: function() {}, // TODO
+ SDL_StartTextInput: function() {
+ SDL.textInput = true;
+ },
+ SDL_StopTextInput: function() {
+ SDL.textInput = false;
+ },
// SDL Mixer
+ Mix_Init: function(flags) {
+ if (!flags) return 0;
+ return 8; /* MIX_INIT_OGG */
+ },
+ Mix_Quit: function(){},
+
Mix_OpenAudio: function(frequency, format, channels, chunksize) {
SDL.allocateChannels(32);
// Just record the values for a later call to Mix_QuickLoad_RAW
@@ -1280,6 +1343,8 @@ var LibrarySDL = {
// the browser has already preloaded the audio file.
var channelInfo = SDL.channels[channel];
channelInfo.audio = audio = audio.cloneNode(true);
+ audio.numChannels = info.audio.numChannels;
+ audio.frequency = info.audio.frequency;
if (SDL.channelFinished) {
audio['onended'] = function() { // TODO: cache these
Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
@@ -1426,10 +1491,70 @@ var LibrarySDL = {
return (SDL.music.audio && !SDL.music.audio.paused) ? 1 : 0;
},
+ // http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_38.html#SEC38
+ // "Note: Does not check if the channel has been paused."
+ Mix_Playing: function(id) {
+ if (id === -1) {
+ var count = 0;
+ for (var i = 0; i < SDL.audios.length; i++) {
+ count += SDL.Mix_Playing(i);
+ }
+ return count;
+ }
+ var info = SDL.audios[id];
+ if (info && info.audio && !info.audio.paused) {
+ return 1;
+ }
+ return 0;
+ },
+
+ Mix_Pause: function(id) {
+ if (id === -1) {
+ for (var i = 0; i<SDL.audios.length;i++) {
+ SDL.Mix_Pause(i);
+ }
+ return;
+ }
+ var info = SDL.audios[id];
+ if (info && info.audio) {
+ info.audio.pause();
+ }
+ },
+
+ // http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_39.html#SEC39
+ Mix_Paused: function(id) {
+ if (id === -1) {
+ var pausedCount = 0;
+ for (var i = 0; i<SDL.audios.length;i++) {
+ pausedCount += SDL.Mix_Paused(i);
+ }
+ return pausedCount;
+ }
+ var info = SDL.audios[id];
+ if (info && info.audio && info.audio.paused) {
+ return 1;
+ }
+ return 0;
+ },
+
Mix_PausedMusic: function() {
return (SDL.music.audio && SDL.music.audio.paused) ? 1 : 0;
},
+ // http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_33.html#SEC33
+ Mix_Resume: function(id) {
+ if (id === -1) {
+ for (var i = 0; i<SDL.audios.length;i++) {
+ SDL.Mix_Resume(i);
+ }
+ return;
+ }
+ var info = SDL.audios[id];
+ if (info && info.audio) {
+ info.audio.play();
+ }
+ },
+
// SDL TTF
TTF_Init: function() { return 0 },
diff --git a/src/long.js b/src/long.js
index c3b0e605..c3651bd9 100644
--- a/src/long.js
+++ b/src/long.js
@@ -1551,6 +1551,17 @@ var i64Math = (function() { // Emscripten wrapper
HEAP32[tempDoublePtr>>2] = ret.low_;
HEAP32[tempDoublePtr+4>>2] = ret.high_;
},
+ abs: function(l, h) {
+ var x = new goog.math.Long(l, h);
+ var ret;
+ if (x.isNegative()) {
+ ret = x.negate();
+ } else {
+ ret = x;
+ }
+ HEAP32[tempDoublePtr>>2] = ret.low_;
+ HEAP32[tempDoublePtr+4>>2] = ret.high_;
+ },
ensureTemps: function() {
if (Wrapper.ensuredTemps) return;
Wrapper.ensuredTemps = true;
diff --git a/src/modules.js b/src/modules.js
index 5b48b305..797d4d83 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -180,7 +180,16 @@ var Variables = {
globals: {},
indexedGlobals: {}, // for indexed globals, ident ==> index
// Used in calculation of indexed globals
- nextIndexedOffset: 0
+ nextIndexedOffset: 0,
+
+ resolveAliasToIdent: function(ident) {
+ while (1) {
+ var varData = Variables.globals[ident];
+ if (!(varData && varData.targetIdent)) break;
+ ident = varData.targetIdent; // might need to eval to turn (6) into 6
+ }
+ return ident;
+ },
};
var Types = {
@@ -331,12 +340,23 @@ var Functions = {
}
}
}
+ if (table.length > 20) {
+ // add some newlines in the table, for readability
+ var j = 10;
+ while (j+10 < table.length) {
+ table[j] += '\n';
+ j += 10;
+ }
+ }
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';
+ if (SAFE_DYNCALLS) {
+ tables[t] += 'var FUNCTION_TABLE_NAMES = ' + JSON.stringify(table).replace(/\n/g, '').replace(/,0/g, ',0\n') + ';\n';
+ }
}
}
if (!generated && !ASM_JS) {
diff --git a/src/parseTools.js b/src/parseTools.js
index a076c862..48274cd5 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -78,6 +78,7 @@ function toNiceIdent(ident) {
assert(ident);
if (parseFloat(ident) == ident) return ident;
if (ident == 'null') return '0'; // see parseNumerical
+ if (ident == 'undef') return '0';
return ident.replace('%', '$').replace(/["&\\ \.@:<>,\*\[\]\(\)-]/g, '_');
}
@@ -103,6 +104,11 @@ function isNiceIdent(ident, loose) {
}
}
+function isJSVar(ident) {
+ return /^\(?[$_]?[\w$_\d ]*\)+$/.test(ident);
+
+}
+
function isStructPointerType(type) {
// This test is necessary for clang - in llvm-gcc, we
// could check for %struct. The downside is that %1 can
@@ -687,7 +693,7 @@ function makeCopyI64(value) {
function parseArbitraryInt(str, bits) {
// We parse the string into a vector of digits, base 10. This is convenient to work on.
- assert(bits % 32 == 0 || ('i' + (bits % 32)) in Runtime.INT_TYPES, 'Arbitrary-sized ints must tails that are of legal size');
+ assert(bits > 0); // NB: we don't check that the value in str can fit in this amount of bits
function str2vec(s) { // index 0 is the highest value
var ret = [];
@@ -977,27 +983,27 @@ 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, forceAsm) {
if (USE_TYPED_ARRAYS !== 2) {
return offset;
- } else {
- if (Runtime.getNativeFieldSize(type) > 4) {
- 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' || forceAsm)) offset = '(' + offset + '&' + memoryMask + ')';
- if (shifts != 0) {
- return '(' + offset + '>>' + shifts + ')';
+ }
+
+ if (Runtime.getNativeFieldSize(type) > 4) {
+ type = 'i32'; // XXX we emulate 64-bit values as 32
+ }
+
+ var sz = Runtime.getNativeTypeSize(type);
+ var shifts = Math.log(sz)/Math.LN2;
+ offset = '(' + offset + ')';
+ if (shifts != 0) {
+ if (CHECK_HEAP_ALIGN) {
+ return '(CHECK_ALIGN_' + sz + '(' + offset + ')>>' + shifts + ')';
} else {
- return offset;
+ return '(' + offset + '>>' + shifts + ')';
}
+ } else {
+ // we need to guard against overflows here, HEAP[U]8 expects a guaranteed int
+ return isJSVar(offset) ? offset : '(' + offset + '|0)';
}
}
@@ -1046,20 +1052,6 @@ function asmCoercion(value, type, signedness) {
}
}
-var TWO_TWENTY = Math.pow(2, 20);
-
-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 ((isNumber(a) && Math.abs(a) < TWO_TWENTY) || (isNumber(b) && Math.abs(b) < TWO_TWENTY)) {
- return '(((' + a + ')*(' + b + '))&-1)'; // small enough to emit directly as a multiply
- }
- if (USE_MATH_IMUL) {
- return 'Math.imul(' + a + ',' + b + ')';
- }
- return '(~~(+((' + a + ')|0) * +((' + b + ')|0)))';
-}
-
function asmFloatToInt(x) {
return '(~~(' + x + '))';
}
@@ -1071,7 +1063,6 @@ function makeGetTempDouble(i, type, forSet) { // get an aliased part of the temp
var ptr = getFastValue('tempDoublePtr', '+', Runtime.getNativeTypeSize(type)*i);
var offset;
if (type == 'double') {
- if (ASM_JS) ptr = '(' + ptr + ')&' + memoryMask;
offset = '(' + ptr + ')>>3';
} else {
offset = getHeapOffset(ptr, type);
@@ -1154,8 +1145,8 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
}
}
-function makeGetValueAsm(ptr, pos, type) {
- return makeGetValue(ptr, pos, type, null, null, null, null, null, true);
+function makeGetValueAsm(ptr, pos, type, unsigned) {
+ return makeGetValue(ptr, pos, type, null, unsigned, null, null, null, true);
}
function indexizeFunctions(value, type) {
@@ -1258,7 +1249,6 @@ function makeSetValueAsm(ptr, pos, value, type, noNeedFirst, ignore, align, noSa
return makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign, true);
}
-var SEEK_OPTIMAL_ALIGN_MIN = 20;
var UNROLL_LOOP_MAX = 8;
function makeSetValues(ptr, pos, value, type, num, align) {
@@ -1278,7 +1268,7 @@ function makeSetValues(ptr, pos, value, type, num, align) {
} 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
// TODO: optimize the case of numeric num but non-numeric value
- if (!isNumber(num) || !isNumber(value) || (align < 4 && parseInt(num) >= SEEK_OPTIMAL_ALIGN_MIN)) {
+ if (!isNumber(num) || !isNumber(value) || (parseInt(num)/align >= UNROLL_LOOP_MAX)) {
return '_memset(' + asmCoercion(getFastValue(ptr, '+', pos), 'i32') + ', ' + asmCoercion(value, 'i32') + ', ' + asmCoercion(num, 'i32') + ')';
}
num = parseInt(num);
@@ -1293,13 +1283,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 || 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) : '') + ', ' +
- '$$stop = $$dest + ' + Math.floor(num/possibleAlign) + '; $$dest < $$stop; $$dest++) {\n' +
- ' HEAP' + (possibleAlign*8) + '[$$dest] = ' + values[possibleAlign] + '\n}');
- }
+ ret.push(unroll('i' + (possibleAlign*8), Math.floor(num/possibleAlign), possibleAlign, values[possibleAlign]));
pos = getFastValue(pos, '+', Math.floor(num/possibleAlign)*possibleAlign);
num %= possibleAlign;
}
@@ -1336,7 +1320,7 @@ function makeCopyValues(dest, src, num, type, modifier, align, sep) {
unroll(type, 1) + ' }';
} 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)) {
+ if (!isNumber(num) || (parseInt(num)/align >= UNROLL_LOOP_MAX)) {
return '_memcpy(' + dest + ', ' + src + ', ' + num + ')';
}
num = parseInt(num);
@@ -1344,16 +1328,7 @@ function makeCopyValues(dest, src, num, type, modifier, align, sep) {
[4, 2, 1].forEach(function(possibleAlign) {
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 == ',' || ASM_JS) { // XXX test asm performance
- ret.push(unroll('i' + (possibleAlign*8), Math.floor(num/possibleAlign), possibleAlign));
- } else {
- assert(sep == ';');
- ret.push('for (var $$src = ' + src + (possibleAlign > 1 ? '>>' + log2(possibleAlign) : '') + ', ' +
- '$$dest = ' + dest + (possibleAlign > 1 ? '>>' + log2(possibleAlign) : '') + ', ' +
- '$$stop = $$src + ' + Math.floor(num/possibleAlign) + '; $$src < $$stop; $$src++, $$dest++) {\n' +
- ' HEAP' + (possibleAlign*8) + '[$$dest] = HEAP' + (possibleAlign*8) + '[$$src]\n}');
- }
+ ret.push(unroll('i' + (possibleAlign*8), Math.floor(num/possibleAlign), possibleAlign));
src = getFastValue(src, '+', Math.floor(num/possibleAlign)*possibleAlign);
dest = getFastValue(dest, '+', Math.floor(num/possibleAlign)*possibleAlign);
num %= possibleAlign;
@@ -1373,17 +1348,23 @@ function makeHEAPView(which, start, end) {
var PLUS_MUL = set('+', '*');
var MUL_DIV = set('*', '/');
var PLUS_MINUS = set('+', '-');
+var TWO_TWENTY = Math.pow(2, 20);
// Given two values and an operation, returns the result of that operation.
// Tries to do as much as possible at compile time.
+// Leaves overflows etc. unhandled, *except* for integer multiply, in order to be efficient with Math.imul
function getFastValue(a, op, b, type) {
a = a.toString();
b = b.toString();
+ a = a == 'true' ? '1' : (a == 'false' ? '0' : a);
+ b = b == 'true' ? '1' : (b == 'false' ? '0' : b);
if (isNumber(a) && isNumber(b)) {
if (op == 'pow') {
return Math.pow(a, b).toString();
} else {
- return eval(a + op + '(' + b + ')').toString(); // parens protect us from "5 - -12" being seen as "5--12" which is "(5--)12"
+ var value = eval(a + op + '(' + b + ')'); // parens protect us from "5 - -12" being seen as "5--12" which is "(5--)12"
+ if (op == '/' && type in Runtime.INT_TYPES) value = value|0; // avoid emitting floats
+ return value.toString();
}
}
if (op == 'pow') {
@@ -1411,8 +1392,14 @@ 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
+ if (!(type in Runtime.FLOAT_TYPES)) {
+ // if guaranteed small enough to not overflow into a double, do a normal multiply
+ var bits = getBits(type) || 32; // default is 32-bit multiply for things like getelementptr indexes
+ // Note that we can emit simple multiple in non-asm.js mode, but asm.js will not parse "16-bit" multiple, so must do imul there
+ if ((isNumber(a) && Math.abs(a) < TWO_TWENTY) || (isNumber(b) && Math.abs(b) < TWO_TWENTY) || (bits < 32 && !ASM_JS)) {
+ return '(((' + a + ')*(' + b + '))&' + ((Math.pow(2, bits)-1)|0) + ')'; // keep a non-eliminatable coercion directly on this
+ }
+ return 'Math.imul(' + a + ',' + b + ')';
}
} else {
if (a == '0') {
@@ -1557,7 +1544,7 @@ function makePointer(slab, pos, allocator, type, ptr) {
var ret = '';
var index = 0;
while (index < array.length) {
- ret = (ret ? ret + '.concat(' : '') + '[' + array.slice(index, index + chunkSize).map(JSON.stringify) + ']' + (ret ? ')' : '');
+ ret = (ret ? ret + '.concat(' : '') + '[' + array.slice(index, index + chunkSize).map(JSON.stringify) + ']' + (ret ? ')\n' : '');
index += chunkSize;
}
return ret;
@@ -1718,9 +1705,7 @@ function handleOverflow(text, bits) {
if (!bits) return text;
var correct = correctOverflows();
warnOnce(!correct || bits <= 32, 'Cannot correct overflows of this many bits: ' + bits);
- if (CHECK_OVERFLOWS) return 'CHECK_OVERFLOW(' + text + ', ' + bits + ', ' + Math.floor(correctSpecificOverflow() && !PGO) + (
- PGO ? ', "' + Debugging.getIdentifier() + '"' : ''
- ) + ')';
+ if (CHECK_OVERFLOWS) return 'CHECK_OVERFLOW(' + text + ', ' + bits + ', ' + Math.floor(correctSpecificOverflow()) + ')';
if (!correct) return text;
if (bits == 32) {
return '((' + text + ')|0)';
@@ -1828,9 +1813,7 @@ function makeSignOp(value, type, op, force, ignore) {
var bits, full;
if (type in Runtime.INT_TYPES) {
bits = parseInt(type.substr(1));
- full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || (correctSpecificSign() && !PGO)) + (
- PGO ? ', "' + (ignore ? '' : Debugging.getIdentifier()) + '"' : ''
- ) + ')';
+ full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || (correctSpecificSign())) + ')';
// Always sign/unsign constants at compile time, regardless of CHECK/CORRECT
if (isNumber(value)) {
return eval(full).toString();
@@ -1842,9 +1825,10 @@ function makeSignOp(value, type, op, force, ignore) {
if (!CHECK_SIGNS || ignore) {
if (bits === 32) {
if (op === 're') {
- return '((' + value + ')|0)';
+ return '(' + getFastValue(value, '|', '0') + ')';
} else {
- return '((' + value + ')>>>0)';
+
+ return '(' + getFastValue(value, '>>>', '0') + ')';
// Alternatively, we can consider the lengthier
// return makeInlineCalculation('VALUE >= 0 ? VALUE : ' + Math.pow(2, bits) + ' + VALUE', value, 'tempBigInt');
// which does not always turn us into a 32-bit *un*signed value
@@ -1853,7 +1837,7 @@ function makeSignOp(value, type, op, force, ignore) {
if (op === 're') {
return makeInlineCalculation('(VALUE << ' + (32-bits) + ') >> ' + (32-bits), value, 'tempInt');
} else {
- return '((' + value + ')&' + (Math.pow(2, bits)-1) + ')';
+ return '(' + getFastValue(value, '&', Math.pow(2, bits)-1) + ')';
}
} else { // bits > 32
if (op === 're') {
@@ -2143,14 +2127,7 @@ function processMathop(item) {
case 'add': return handleOverflow(getFastValue(idents[0], '+', idents[1], item.type), bits);
case 'sub': return handleOverflow(getFastValue(idents[0], '-', idents[1], item.type), bits);
case 'sdiv': case 'udiv': return makeRounding(getFastValue(idents[0], '/', idents[1], item.type), bits, op[0] === 's');
- case 'mul': {
- if (bits == 32 && PRECISE_I32_MUL) {
- Types.preciseI64MathUsed = true;
- return '(i64Math' + (ASM_JS ? '_' : '.') + 'multiply(' + asmCoercion(idents[0], 'i32') + ',0,' + asmCoercion(idents[1], 'i32') + ',0),' + makeGetValue('tempDoublePtr', 0, 'i32') + ')';
- } else {
- return '((' +getFastValue(idents[0], '*', idents[1], item.type) + ')&-1)'; // force a non-eliminatable coercion here, to prevent a double result from leaking
- }
- }
+ case 'mul': return getFastValue(idents[0], '*', idents[1], item.type); // overflow handling is already done in getFastValue for '*'
case 'urem': case 'srem': return getFastValue(idents[0], '%', idents[1], item.type);
case 'or': {
if (bits > 32) {
@@ -2211,7 +2188,6 @@ function processMathop(item) {
case 'ne': case 'eq': {
// We must sign them, so we do not compare -1 to 255 (could have unsigned them both too)
// since LLVM tells us if <=, >= etc. comparisons are signed, but not == and !=.
- assert(paramTypes[0] == paramTypes[1]);
idents[0] = makeSignOp(idents[0], paramTypes[0], 're');
idents[1] = makeSignOp(idents[1], paramTypes[1], 're');
return idents[0] + (variant === 'eq' ? '==' : '!=') + idents[1];
diff --git a/src/preamble.js b/src/preamble.js
index aab50e9a..9bc68d8f 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -150,51 +150,24 @@ function SAFE_HEAP_COPY_HISTORY(dest, src) {
//==========================================
#endif
-var CorrectionsMonitor = {
-#if PGO
- MAX_ALLOWED: Infinity,
-#else
- MAX_ALLOWED: 0, // XXX
+#if CHECK_HEAP_ALIGN
+//========================================
+// Debugging tools - alignment check
+//========================================
+function CHECK_ALIGN_8(addr) {
+ assert((addr & 7) == 0, "address must be 8-byte aligned, is " + addr + "!");
+ return addr;
+}
+function CHECK_ALIGN_4(addr) {
+ assert((addr & 3) == 0, "address must be 4-byte aligned, is " + addr + "!");
+ return addr;
+}
+function CHECK_ALIGN_2(addr) {
+ assert((addr & 1) == 0, "address must be 2-byte aligned!");
+ return addr;
+}
#endif
- corrections: 0,
- sigs: {},
- note: function(type, succeed, sig) {
- if (!succeed) {
- this.corrections++;
- if (this.corrections >= this.MAX_ALLOWED) abort('\n\nToo many corrections!');
- }
-#if PGO
- if (!sig)
- sig = (new Error().stack).toString().split('\n')[2].split(':').slice(-1)[0]; // Spidermonkey-specific FIXME
- sig = type + '|' + sig;
- if (!this.sigs[sig]) {
- //Module.print('Correction: ' + sig);
- this.sigs[sig] = [0, 0]; // fail, succeed
- }
- this.sigs[sig][succeed ? 1 : 0]++;
-#endif
- },
-
- print: function() {
-#if PGO
- var items = [];
- for (var sig in this.sigs) {
- items.push({
- sig: sig,
- fails: this.sigs[sig][0],
- succeeds: this.sigs[sig][1],
- total: this.sigs[sig][0] + this.sigs[sig][1]
- });
- }
- items.sort(function(x, y) { return y.total - x.total; });
- for (var i = 0; i < items.length; i++) {
- var item = items[i];
- Module.print(item.sig + ' : ' + item.total + ' hits, %' + (Math.ceil(100*item.fails/item.total)) + ' failures');
- }
-#endif
- }
-};
#if CHECK_OVERFLOWS
//========================================
@@ -207,24 +180,20 @@ function CHECK_OVERFLOW(value, bits, ignore, sig) {
// For signedness issue here, see settings.js, CHECK_SIGNED_OVERFLOWS
#if CHECK_SIGNED_OVERFLOWS
if (value === Infinity || value === -Infinity || value >= twopbits1 || value < -twopbits1) {
- CorrectionsMonitor.note('SignedOverflow', 0, sig);
- if (value === Infinity || value === -Infinity || Math.abs(value) >= twopbits) CorrectionsMonitor.note('Overflow');
+ throw 'SignedOverflow';
+ if (value === Infinity || value === -Infinity || Math.abs(value) >= twopbits) throw 'Overflow';
+ }
#else
if (value === Infinity || value === -Infinity || Math.abs(value) >= twopbits) {
- CorrectionsMonitor.note('Overflow', 0, sig);
+ throw 'Overflow';
+ }
#endif
#if CORRECT_OVERFLOWS
- // Fail on >32 bits - we warned at compile time
- if (bits <= 32) {
- value = value & (twopbits - 1);
- }
-#endif
- } else {
-#if CHECK_SIGNED_OVERFLOWS
- CorrectionsMonitor.note('SignedOverflow', 1, sig);
-#endif
- CorrectionsMonitor.note('Overflow', 1, sig);
+ // Fail on >32 bits - we warned at compile time
+ if (bits <= 32) {
+ value = value & (twopbits - 1);
}
+#endif
return value;
}
#endif
@@ -243,39 +212,6 @@ var INDENT = '';
var START_TIME = Date.now();
#endif
-#if PROFILE
-var PROFILING = 0;
-var PROFILING_ROOT = { time: 0, children: {}, calls: 0 };
-var PROFILING_NODE;
-
-function startProfiling() {
- PROFILING_NODE = PROFILING_ROOT;
- PROFILING = 1;
-}
-Module['startProfiling'] = startProfiling;
-
-function stopProfiling() {
- PROFILING = 0;
- assert(PROFILING_NODE === PROFILING_ROOT, 'Must have popped all the profiling call stack');
-}
-Module['stopProfiling'] = stopProfiling;
-
-function printProfiling() {
- function dumpData(name_, node, indent) {
- Module.print(indent + ('________' + node.time).substr(-8) + ': ' + name_ + ' (' + node.calls + ')');
- var children = [];
- for (var child in node.children) {
- children.push(node.children[child]);
- children[children.length-1].name_ = child;
- }
- children.sort(function(x, y) { return y.time - x.time });
- children.forEach(function(child) { dumpData(child.name_, child, indent + ' ') });
- }
- dumpData('root', PROFILING_ROOT, ' ');
-}
-Module['printProfiling'] = printProfiling;
-#endif
-
//========================================
// Runtime essentials
//========================================
@@ -334,11 +270,9 @@ Module["ccall"] = ccall;
// Returns the C function with a specified identifier (for C++, you need to do manual name mangling)
function getCFunc(ident) {
try {
- var func = eval('_' + ident);
+ var func = globalScope['Module']['_' + ident]; // closure exported function
+ if (!func) func = eval('_' + ident); // explicit lookup
} catch(e) {
- try {
- func = globalScope['Module']['_' + ident]; // closure exported function
- } catch(e) {}
}
assert(func, 'Cannot call unknown function ' + ident + ' (perhaps LLVM optimizations or closure removed it?)');
return func;
@@ -478,14 +412,6 @@ Module['ALLOC_STACK'] = ALLOC_STACK;
Module['ALLOC_STATIC'] = ALLOC_STATIC;
Module['ALLOC_NONE'] = ALLOC_NONE;
-// Simple unoptimized memset - necessary during startup
-var _memset = function(ptr, value, num) {
- var stop = ptr + num;
- while (ptr < stop) {
- {{{ makeSetValue('ptr++', 0, 'value', 'i8', null, true) }}};
- }
-}
-
// allocate(): This is for internal use. You can use it yourself as well, but the interface
// is a little tricky (see docs right below). The reason is that it is optimized
// for multiple syntaxes to save space in generated code. So you should
@@ -519,7 +445,18 @@ function allocate(slab, types, allocator, ptr) {
}
if (zeroinit) {
- _memset(ret, 0, size);
+ var ptr = ret, stop;
+#if USE_TYPED_ARRAYS == 2
+ assert((ret & 3) == 0);
+ stop = ret + (size & ~3);
+ for (; ptr < stop; ptr += 4) {
+ {{{ makeSetValue('ptr', '0', '0', 'i32', null, true) }}};
+ }
+#endif
+ stop = ret + size;
+ while (ptr < stop) {
+ {{{ makeSetValue('ptr++', '0', '0', 'i8', null, true) }}};
+ }
return ret;
}
@@ -530,7 +467,7 @@ function allocate(slab, types, allocator, ptr) {
}
#endif
- var i = 0, type;
+ var i = 0, type, typeSize, previousType;
while (i < size) {
var curr = slab[i];
@@ -552,7 +489,13 @@ function allocate(slab, types, allocator, ptr) {
#endif
setValue(ret+i, curr, type);
- i += Runtime.getNativeTypeSize(type);
+
+ // no need to look up size unless type changes, so cache it
+ if (previousType !== type) {
+ typeSize = Runtime.getNativeTypeSize(type);
+ previousType = type;
+ }
+ i += typeSize;
}
return ret;
@@ -626,6 +569,7 @@ function enlargeMemory() {
while (TOTAL_MEMORY <= STATICTOP) { // Simple heuristic. Override enlargeMemory() if your program has something more optimal for it
TOTAL_MEMORY = alignMemoryPage(2*TOTAL_MEMORY);
}
+ assert(TOTAL_MEMORY <= Math.pow(2, 30)); // 2^30==1GB is a practical maximum - 2^31 is already close to possible negative numbers etc.
#if USE_TYPED_ARRAYS == 1
var oldIHEAP = IHEAP;
Module['HEAP'] = Module['IHEAP'] = HEAP = IHEAP = new Int32Array(TOTAL_MEMORY);
@@ -655,47 +599,43 @@ function enlargeMemory() {
#endif
var TOTAL_STACK = Module['TOTAL_STACK'] || {{{ TOTAL_STACK }}};
-#if ASM_JS == 0
var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || {{{ TOTAL_MEMORY }}};
-#else
-var TOTAL_MEMORY = {{{ TOTAL_MEMORY }}}; // in asm, we hardcode the mask, so cannot adjust memory at runtime
-#endif
var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}};
// Initialize the runtime's memory
#if USE_TYPED_ARRAYS
// check for full engine support (use string 'subarray' to avoid closure compiler confusion)
- assert(!!Int32Array && !!Float64Array && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']),
- 'Cannot fallback to non-typed array case: Code is too specialized');
+assert(!!Int32Array && !!Float64Array && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']),
+ 'Cannot fallback to non-typed array case: Code is too specialized');
#if USE_TYPED_ARRAYS == 1
- HEAP = IHEAP = new Int32Array(TOTAL_MEMORY);
- IHEAPU = new Uint32Array(IHEAP.buffer);
+HEAP = IHEAP = new Int32Array(TOTAL_MEMORY);
+IHEAPU = new Uint32Array(IHEAP.buffer);
#if USE_FHEAP
- FHEAP = new Float64Array(TOTAL_MEMORY);
+FHEAP = new Float64Array(TOTAL_MEMORY);
#endif
#endif
#if USE_TYPED_ARRAYS == 2
- var buffer = new ArrayBuffer(TOTAL_MEMORY);
- HEAP8 = new Int8Array(buffer);
- HEAP16 = new Int16Array(buffer);
- HEAP32 = new Int32Array(buffer);
- HEAPU8 = new Uint8Array(buffer);
- HEAPU16 = new Uint16Array(buffer);
- HEAPU32 = new Uint32Array(buffer);
- HEAPF32 = new Float32Array(buffer);
- HEAPF64 = new Float64Array(buffer);
-
- // Endianness check (note: assumes compiler arch was little-endian)
- HEAP32[0] = 255;
- assert(HEAPU8[0] === 255 && HEAPU8[3] === 0, 'Typed arrays 2 must be run on a little-endian system');
+var buffer = new ArrayBuffer(TOTAL_MEMORY);
+HEAP8 = new Int8Array(buffer);
+HEAP16 = new Int16Array(buffer);
+HEAP32 = new Int32Array(buffer);
+HEAPU8 = new Uint8Array(buffer);
+HEAPU16 = new Uint16Array(buffer);
+HEAPU32 = new Uint32Array(buffer);
+HEAPF32 = new Float32Array(buffer);
+HEAPF64 = new Float64Array(buffer);
+
+// Endianness check (note: assumes compiler arch was little-endian)
+HEAP32[0] = 255;
+assert(HEAPU8[0] === 255 && HEAPU8[3] === 0, 'Typed arrays 2 must be run on a little-endian system');
#endif
#else
- // Make sure that our HEAP is implemented as a flat array.
- HEAP = []; // Hinting at the size with |new Array(TOTAL_MEMORY)| should help in theory but makes v8 much slower
- for (var i = 0; i < FAST_MEMORY; i++) {
- HEAP[i] = 0; // XXX We do *not* use {{| makeSetValue(0, 'i', 0, 'null') |}} here, since this is done just to optimize runtime speed
- }
+// Make sure that our HEAP is implemented as a flat array.
+HEAP = []; // Hinting at the size with |new Array(TOTAL_MEMORY)| should help in theory but makes v8 much slower
+for (var i = 0; i < FAST_MEMORY; i++) {
+ HEAP[i] = 0; // XXX We do *not* use {{| makeSetValue(0, 'i', 0, 'null') |}} here, since this is done just to optimize runtime speed
+}
#endif
Module['HEAP'] = HEAP;
@@ -722,7 +662,7 @@ STACK_MAX = TOTAL_STACK; // we lose a little stack here, but TOTAL_STACK is nice
#if USE_TYPED_ARRAYS == 2
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
+function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much
HEAP8[tempDoublePtr] = HEAP8[ptr];
HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];
HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];
@@ -773,9 +713,6 @@ function preMain() {
}
function exitRuntime() {
callRuntimeCallbacks(__ATEXIT__);
-
- // Print summary of correction activity
- CorrectionsMonitor.print();
}
// Tools
@@ -832,6 +769,20 @@ Module['writeArrayToMemory'] = writeArrayToMemory;
{{{ unSign }}}
{{{ reSign }}}
+#if PRECISE_I32_MUL
+if (!Math.imul) Math.imul = function(a, b) {
+ var ah = a >>> 16;
+ var al = a & 0xffff;
+ var bh = b >>> 16;
+ var bl = b & 0xffff;
+ return (al*bl + ((ah*bl + al*bh) << 16))|0;
+};
+#else
+Math.imul = function(a, b) {
+ return (a*b)|0; // fast but imprecise
+};
+#endif
+
// A counter of dependencies for calling run(). If we need to
// do asynchronous work before running, increment this and
// decrement it. Incrementing must happen in a place like
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index ae8577b1..ae393de3 100644
--- a/src/relooper/Relooper.cpp
+++ b/src/relooper/Relooper.cpp
@@ -347,17 +347,25 @@ struct RelooperRecursor {
RelooperRecursor(Relooper *ParentInit) : Parent(ParentInit) {}
};
+typedef std::list<Block*> BlockList;
+
void Relooper::Calculate(Block *Entry) {
// Scan and optimize the input
struct PreOptimizer : public RelooperRecursor {
PreOptimizer(Relooper *Parent) : RelooperRecursor(Parent) {}
BlockSet Live;
- void FindLive(Block *Curr) {
- if (Live.find(Curr) != Live.end()) return;
- Live.insert(Curr);
- for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
- FindLive(iter->first);
+ void FindLive(Block *Root) {
+ BlockList ToInvestigate;
+ ToInvestigate.push_back(Root);
+ while (ToInvestigate.size() > 0) {
+ Block *Curr = ToInvestigate.front();
+ ToInvestigate.pop_front();
+ if (Live.find(Curr) != Live.end()) continue;
+ Live.insert(Curr);
+ for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
+ ToInvestigate.push_back(iter->first);
+ }
}
}
@@ -529,7 +537,6 @@ void Relooper::Calculate(Block *Entry) {
// ignore directly reaching the entry itself by another entry.
void FindIndependentGroups(BlockSet &Blocks, BlockSet &Entries, BlockBlockSetMap& IndependentGroups) {
typedef std::map<Block*, Block*> BlockBlockMap;
- typedef std::list<Block*> BlockList;
struct HelperClass {
BlockBlockSetMap& IndependentGroups;
@@ -872,33 +879,38 @@ void Relooper::Calculate(Block *Entry) {
// A flow operation is trivially unneeded if the shape we naturally get to by normal code
// execution is the same as the flow forces us to.
void RemoveUnneededFlows(Shape *Root, Shape *Natural=NULL) {
- SHAPE_SWITCH(Root, {
- // If there is a next block, we already know at Simple creation time to make direct branches,
- // and we can do nothing more. If there is no next however, then Natural is where we will
- // go to by doing nothing, so we can potentially optimize some branches to direct.
- if (Simple->Next) {
- RemoveUnneededFlows(Simple->Next, Natural);
- } else {
- for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
- Block *Target = iter->first;
- Branch *Details = iter->second;
- if (Details->Type != Branch::Direct && Target->Parent == Natural) {
- Details->Type = Branch::Direct;
- if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) {
- Multiple->NeedLoop--;
+ Shape *Next = Root;
+ while (Next) {
+ Root = Next;
+ Next = NULL;
+ SHAPE_SWITCH(Root, {
+ // If there is a next block, we already know at Simple creation time to make direct branches,
+ // and we can do nothing more. If there is no next however, then Natural is where we will
+ // go to by doing nothing, so we can potentially optimize some branches to direct.
+ if (Simple->Next) {
+ Next = Simple->Next;
+ } else {
+ for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
+ Block *Target = iter->first;
+ Branch *Details = iter->second;
+ if (Details->Type != Branch::Direct && Target->Parent == Natural) {
+ Details->Type = Branch::Direct;
+ if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) {
+ Multiple->NeedLoop--;
+ }
}
}
}
- }
- }, {
- for (BlockShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
- RemoveUnneededFlows(iter->second, Multiple->Next);
- }
- RemoveUnneededFlows(Multiple->Next, Natural);
- }, {
- RemoveUnneededFlows(Loop->Inner, Loop->Inner);
- RemoveUnneededFlows(Loop->Next, Natural);
- });
+ }, {
+ for (BlockShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
+ RemoveUnneededFlows(iter->second, Multiple->Next);
+ }
+ Next = Multiple->Next;
+ }, {
+ RemoveUnneededFlows(Loop->Inner, Loop->Inner);
+ Next = Loop->Next;
+ });
+ }
}
// After we know which loops exist, we can calculate which need to be labeled
@@ -909,48 +921,54 @@ void Relooper::Calculate(Block *Entry) {
}
std::stack<Shape*> &LoopStack = *((std::stack<Shape*>*)Closure);
- SHAPE_SWITCH(Root, {
- MultipleShape *Fused = Shape::IsMultiple(Root->Next);
- // If we are fusing a Multiple with a loop into this Simple, then visit it now
- if (Fused && Fused->NeedLoop) {
- LoopStack.push(Fused);
- RECURSE_MULTIPLE_MANUAL(FindLabeledLoops, Fused);
- }
- for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
- Block *Target = iter->first;
- Branch *Details = iter->second;
- if (Details->Type != Branch::Direct) {
- assert(LoopStack.size() > 0);
- if (Details->Ancestor != LoopStack.top()) {
- LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor);
- Labeled->Labeled = true;
- Details->Labeled = true;
- } else {
- Details->Labeled = false;
+ Shape *Next = Root;
+ while (Next) {
+ Root = Next;
+ Next = NULL;
+
+ SHAPE_SWITCH(Root, {
+ MultipleShape *Fused = Shape::IsMultiple(Root->Next);
+ // If we are fusing a Multiple with a loop into this Simple, then visit it now
+ if (Fused && Fused->NeedLoop) {
+ LoopStack.push(Fused);
+ RECURSE_MULTIPLE_MANUAL(FindLabeledLoops, Fused);
+ }
+ for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
+ Block *Target = iter->first;
+ Branch *Details = iter->second;
+ if (Details->Type != Branch::Direct) {
+ assert(LoopStack.size() > 0);
+ if (Details->Ancestor != LoopStack.top()) {
+ LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor);
+ Labeled->Labeled = true;
+ Details->Labeled = true;
+ } else {
+ Details->Labeled = false;
+ }
}
}
- }
- if (Fused && Fused->NeedLoop) {
- LoopStack.pop();
- if (Fused->Next) FindLabeledLoops(Fused->Next);
- } else {
- if (Root->Next) FindLabeledLoops(Root->Next);
- }
- }, {
- if (Multiple->NeedLoop) {
- LoopStack.push(Multiple);
- }
- RECURSE_MULTIPLE(FindLabeledLoops);
- if (Multiple->NeedLoop) {
+ if (Fused && Fused->NeedLoop) {
+ LoopStack.pop();
+ Next = Fused->Next;
+ } else {
+ Next = Root->Next;
+ }
+ }, {
+ if (Multiple->NeedLoop) {
+ LoopStack.push(Multiple);
+ }
+ RECURSE_MULTIPLE(FindLabeledLoops);
+ if (Multiple->NeedLoop) {
+ LoopStack.pop();
+ }
+ Next = Root->Next;
+ }, {
+ LoopStack.push(Loop);
+ RECURSE_LOOP(FindLabeledLoops);
LoopStack.pop();
- }
- if (Root->Next) FindLabeledLoops(Root->Next);
- }, {
- LoopStack.push(Loop);
- RECURSE_LOOP(FindLabeledLoops);
- LoopStack.pop();
- if (Root->Next) FindLabeledLoops(Root->Next);
- });
+ Next = Root->Next;
+ });
+ }
if (First) {
delete (std::stack<Shape*>*)Closure;
diff --git a/src/relooper/fuzzer.py b/src/relooper/fuzzer.py
index 887eab3b..96929028 100644
--- a/src/relooper/fuzzer.py
+++ b/src/relooper/fuzzer.py
@@ -98,14 +98,14 @@ int main() {
open('fuzz.slow.js', 'w').write(slow)
open('fuzz.cpp', 'w').write(fast)
print '_'
- slow_out = subprocess.Popen(['/home/alon/Dev/mozilla-central/js/src/fast/js', '-m', '-n', 'fuzz.slow.js'], stdout=subprocess.PIPE).communicate()[0]
+ slow_out = subprocess.Popen(['/home/alon/Dev/odinmonkey/js/src/fast/js', '-m', '-n', 'fuzz.slow.js'], stdout=subprocess.PIPE).communicate()[0]
print '.'
subprocess.call(['g++', 'fuzz.cpp', 'Relooper.o', '-o', 'fuzz', '-g'])
print '*'
subprocess.call(['./fuzz'], stdout=open('fuzz.fast.js', 'w'))
print '-'
- fast_out = subprocess.Popen(['/home/alon/Dev/mozilla-central/js/src/fast/js', '-m', '-n', 'fuzz.fast.js'], stdout=subprocess.PIPE).communicate()[0]
+ fast_out = subprocess.Popen(['/home/alon/Dev/odinmonkey/js/src/fast/js', '-m', '-n', 'fuzz.fast.js'], stdout=subprocess.PIPE).communicate()[0]
print
if slow_out != fast_out:
diff --git a/src/relooper/test2.txt b/src/relooper/test2.txt
index a847e806..c77ce491 100644
--- a/src/relooper/test2.txt
+++ b/src/relooper/test2.txt
@@ -1,11 +1,12 @@
ep
-L1:
-if (ep -> LBB1) {
- LBB1
- if (!(LBB1 -> LBB2)) {
- break L1;
+do {
+ if (ep -> LBB1) {
+ LBB1
+ if (!(LBB1 -> LBB2)) {
+ break;
+ }
+ LBB2
}
- LBB2
-}
+} while(0);
LBB3
diff --git a/src/relooper/test3.txt b/src/relooper/test3.txt
index 7d06f06a..696542ef 100644
--- a/src/relooper/test3.txt
+++ b/src/relooper/test3.txt
@@ -1,25 +1,27 @@
ep
-L1:
-if (ep -> LBB1) {
- LBB1
- if (!(LBB1 -> LBB2)) {
- break L1;
+do {
+ if (ep -> LBB1) {
+ LBB1
+ if (!(LBB1 -> LBB2)) {
+ break;
+ }
+ LBB2
}
- LBB2
-}
+} while(0);
LBB3
-L5:
-if (LBB3 -> LBB4) {
- LBB4
- if (!(LBB4 -> LBB5)) {
- break L5;
- }
- while(1) {
- LBB5
- if (LBB5 -> LBB6) {
- break L5;
+L5: do {
+ if (LBB3 -> LBB4) {
+ LBB4
+ if (!(LBB4 -> LBB5)) {
+ break;
+ }
+ while(1) {
+ LBB5
+ if (LBB5 -> LBB6) {
+ break L5;
+ }
}
}
-}
+} while(0);
LBB6
diff --git a/src/relooper/test4.txt b/src/relooper/test4.txt
index 2ab3265a..f0bfb972 100644
--- a/src/relooper/test4.txt
+++ b/src/relooper/test4.txt
@@ -1,16 +1,17 @@
//19
-L1:
-if ( 1 ) {
- //20
- if (!( 1 )) {
+do {
+ if ( 1 ) {
+ //20
+ if (!( 1 )) {
+ label = 4;
+ break;
+ }
+ //21
+ break;
+ } else {
label = 4;
- break L1;
}
- //21
- break L1;
-} else {
- label = 4;
-}
+} while(0);
if (label == 4) {
//22
}
diff --git a/src/relooper/test6.txt b/src/relooper/test6.txt
index 0ec7e666..c5effd08 100644
--- a/src/relooper/test6.txt
+++ b/src/relooper/test6.txt
@@ -1,11 +1,12 @@
//0
-L1:
-if (check(0)) {
- //1
- if (!(check(1))) {
- break L1;
+do {
+ if (check(0)) {
+ //1
+ if (!(check(1))) {
+ break;
+ }
+ //2
}
- //2
-}
+} while(0);
//3
diff --git a/src/relooper/test_debug.txt b/src/relooper/test_debug.txt
index 02377fb7..1c7d0508 100644
--- a/src/relooper/test_debug.txt
+++ b/src/relooper/test_debug.txt
@@ -83,13 +83,14 @@ int main() {
// === Optimizing shapes ===
// Fusing Multiple to Simple
ep
-L1:
-if (ep -> LBB1) {
- LBB1
- if (!(LBB1 -> LBB2)) {
- break L1;
+do {
+ if (ep -> LBB1) {
+ LBB1
+ if (!(LBB1 -> LBB2)) {
+ break;
+ }
+ LBB2
}
- LBB2
-}
+} while(0);
LBB3
diff --git a/src/relooper/test_fuzz1.txt b/src/relooper/test_fuzz1.txt
index 09edb594..5122257e 100644
--- a/src/relooper/test_fuzz1.txt
+++ b/src/relooper/test_fuzz1.txt
@@ -3,12 +3,13 @@
print('entry'); var label; var state; var decisions = [4, 1, 7, 2, 6, 6, 8]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }
print(5); state = check();
print(6); state = check();
-L3:
-if (state == 7) {
- print(7); state = check();
- label = 3;
- break L3;
-}
+do {
+ if (state == 7) {
+ print(7); state = check();
+ label = 3;
+ break;
+ }
+} while(0);
L5: while(1) {
if (label == 3) {
label = 0;
diff --git a/src/relooper/test_fuzz5.txt b/src/relooper/test_fuzz5.txt
index 7c795d53..9548205c 100644
--- a/src/relooper/test_fuzz5.txt
+++ b/src/relooper/test_fuzz5.txt
@@ -3,21 +3,22 @@
print('entry'); var label; var state; var decisions = [133, 98, 134, 143, 162, 187, 130, 87, 91, 49, 102, 47, 9, 132, 179, 176, 157, 25, 64, 161, 57, 107, 16, 167, 185, 45, 191, 180, 23, 131]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }
L1: while(1) {
print(7); state = check();
- L3:
- if (state % 3 == 1) {
- label = 3;
- } else if (state % 3 == 0) {
- print(8); state = check();
- if (state % 2 == 0) {
- label = 5;
- break L3;
+ do {
+ if (state % 3 == 1) {
+ label = 3;
+ } else if (state % 3 == 0) {
+ print(8); state = check();
+ if (state % 2 == 0) {
+ label = 5;
+ break;
+ } else {
+ label = 7;
+ break;
+ }
} else {
- label = 7;
- break L3;
+ break L1;
}
- } else {
- break L1;
- }
+ } while(0);
while(1) {
if (label == 3) {
label = 0;
diff --git a/src/relooper/test_inf.txt b/src/relooper/test_inf.txt
index 3e292433..379d2083 100644
--- a/src/relooper/test_inf.txt
+++ b/src/relooper/test_inf.txt
@@ -5,34 +5,35 @@ if (uint(i4) >= uint(i5)) {
code 1
}
code 3
-L5:
-if (!(i2 == 0)) {
- code 4
- while(1) {
- code 5
- if (uint(i6) >= uint(i7)) {
- code 7
- } else {
- code 6
- }
- code 8
- if (uint(i6) >= uint(i7)) {
- code 10
- } else {
- code 9
- }
- code 11
- if (uint(i5) >= uint(i6)) {
- code 13
- } else {
- code 12
- }
- code 14
- if (!(i2 != 0)) {
- break L5;
+L5: do {
+ if (!(i2 == 0)) {
+ code 4
+ while(1) {
+ code 5
+ if (uint(i6) >= uint(i7)) {
+ code 7
+ } else {
+ code 6
+ }
+ code 8
+ if (uint(i6) >= uint(i7)) {
+ code 10
+ } else {
+ code 9
+ }
+ code 11
+ if (uint(i5) >= uint(i6)) {
+ code 13
+ } else {
+ code 12
+ }
+ code 14
+ if (!(i2 != 0)) {
+ break L5;
+ }
}
}
-}
+} while(0);
code 15
if (uint(i4) >= uint(i5)) {
code 17
@@ -40,178 +41,179 @@ if (uint(i4) >= uint(i5)) {
code 16
}
code 18
-L26:
-if (!(i2 == 0)) {
- code 19
- while(1) {
- code 20
- if (uint(i5) >= uint(i6)) {
- code 22
- } else {
- code 21
- }
- code 23
- if (uint(i5) >= uint(i6)) {
- code 25
- } else {
- code 24
- }
- code 26
- if (uint(i5) >= uint(i6)) {
- code 28
- } else {
- code 27
- }
- code 29
- if (uint(i5) >= uint(i6)) {
- code 31
- } else {
- code 30
- }
- code 32
- if (uint(i5) >= uint(i6)) {
- code 34
- } else {
- code 33
- }
- code 35
- if (uint(i5) >= uint(i6)) {
- code 37
- } else {
- code 36
- }
- code 38
- if (uint(i5) >= uint(i6)) {
- code 40
- } else {
- code 39
- }
- code 41
- if (uint(i5) >= uint(i6)) {
- code 43
- } else {
- code 42
- }
- code 44
- if (uint(i5) >= uint(i6)) {
- code 46
- } else {
- code 45
- }
- code 47
- if (uint(i5) >= uint(i6)) {
- code 49
- } else {
- code 48
- }
- code 50
- if (uint(i5) >= uint(i6)) {
- code 52
- } else {
- code 51
- }
- code 53
- if (uint(i5) >= uint(i6)) {
- code 55
- } else {
- code 54
- }
- code 56
- if (uint(i5) >= uint(i6)) {
- code 58
- } else {
- code 57
- }
- code 59
- if (uint(i5) >= uint(i6)) {
- code 61
- } else {
- code 60
- }
- code 62
- if (uint(i5) >= uint(i6)) {
- code 64
- } else {
- code 63
- }
- code 65
- if (uint(i5) >= uint(i6)) {
- code 67
- } else {
- code 66
- }
- code 68
- if (uint(i5) >= uint(i6)) {
- code 70
- } else {
- code 69
- }
- code 71
- if (uint(i5) >= uint(i6)) {
- code 73
- } else {
- code 72
- }
- code 74
- if (uint(i5) >= uint(i6)) {
- code 76
- } else {
- code 75
- }
- code 77
- if (uint(i5) >= uint(i6)) {
- code 79
- } else {
- code 78
- }
- code 80
- if (uint(i5) >= uint(i6)) {
- code 82
- } else {
- code 81
- }
- code 83
- if (uint(i5) >= uint(i6)) {
- code 85
- } else {
- code 84
- }
- code 86
- if (uint(i5) >= uint(i6)) {
- code 88
- } else {
- code 87
- }
- code 89
- if (uint(i5) >= uint(i6)) {
- code 91
- } else {
- code 90
- }
- code 92
- if (uint(i5) >= uint(i6)) {
- code 94
- } else {
- code 93
- }
- code 95
- if (uint(i5) >= uint(i6)) {
- code 97
- } else {
- code 96
- }
- code 98
- if (uint(i5) >= uint(i6)) {
- code 100
- } else {
- code 99
- }
- code 101
- if (!(i2 != 0)) {
- break L26;
+L26: do {
+ if (!(i2 == 0)) {
+ code 19
+ while(1) {
+ code 20
+ if (uint(i5) >= uint(i6)) {
+ code 22
+ } else {
+ code 21
+ }
+ code 23
+ if (uint(i5) >= uint(i6)) {
+ code 25
+ } else {
+ code 24
+ }
+ code 26
+ if (uint(i5) >= uint(i6)) {
+ code 28
+ } else {
+ code 27
+ }
+ code 29
+ if (uint(i5) >= uint(i6)) {
+ code 31
+ } else {
+ code 30
+ }
+ code 32
+ if (uint(i5) >= uint(i6)) {
+ code 34
+ } else {
+ code 33
+ }
+ code 35
+ if (uint(i5) >= uint(i6)) {
+ code 37
+ } else {
+ code 36
+ }
+ code 38
+ if (uint(i5) >= uint(i6)) {
+ code 40
+ } else {
+ code 39
+ }
+ code 41
+ if (uint(i5) >= uint(i6)) {
+ code 43
+ } else {
+ code 42
+ }
+ code 44
+ if (uint(i5) >= uint(i6)) {
+ code 46
+ } else {
+ code 45
+ }
+ code 47
+ if (uint(i5) >= uint(i6)) {
+ code 49
+ } else {
+ code 48
+ }
+ code 50
+ if (uint(i5) >= uint(i6)) {
+ code 52
+ } else {
+ code 51
+ }
+ code 53
+ if (uint(i5) >= uint(i6)) {
+ code 55
+ } else {
+ code 54
+ }
+ code 56
+ if (uint(i5) >= uint(i6)) {
+ code 58
+ } else {
+ code 57
+ }
+ code 59
+ if (uint(i5) >= uint(i6)) {
+ code 61
+ } else {
+ code 60
+ }
+ code 62
+ if (uint(i5) >= uint(i6)) {
+ code 64
+ } else {
+ code 63
+ }
+ code 65
+ if (uint(i5) >= uint(i6)) {
+ code 67
+ } else {
+ code 66
+ }
+ code 68
+ if (uint(i5) >= uint(i6)) {
+ code 70
+ } else {
+ code 69
+ }
+ code 71
+ if (uint(i5) >= uint(i6)) {
+ code 73
+ } else {
+ code 72
+ }
+ code 74
+ if (uint(i5) >= uint(i6)) {
+ code 76
+ } else {
+ code 75
+ }
+ code 77
+ if (uint(i5) >= uint(i6)) {
+ code 79
+ } else {
+ code 78
+ }
+ code 80
+ if (uint(i5) >= uint(i6)) {
+ code 82
+ } else {
+ code 81
+ }
+ code 83
+ if (uint(i5) >= uint(i6)) {
+ code 85
+ } else {
+ code 84
+ }
+ code 86
+ if (uint(i5) >= uint(i6)) {
+ code 88
+ } else {
+ code 87
+ }
+ code 89
+ if (uint(i5) >= uint(i6)) {
+ code 91
+ } else {
+ code 90
+ }
+ code 92
+ if (uint(i5) >= uint(i6)) {
+ code 94
+ } else {
+ code 93
+ }
+ code 95
+ if (uint(i5) >= uint(i6)) {
+ code 97
+ } else {
+ code 96
+ }
+ code 98
+ if (uint(i5) >= uint(i6)) {
+ code 100
+ } else {
+ code 99
+ }
+ code 101
+ if (!(i2 != 0)) {
+ break L26;
+ }
}
}
-}
+} while(0);
code 102
if (uint(i4) >= uint(i5)) {
code 104
@@ -219,136 +221,137 @@ if (uint(i4) >= uint(i5)) {
code 103
}
code 105
-L143:
-if (!(i2 == 0)) {
- code 106
- while(1) {
- code 107
- if (uint(i5) >= uint(i6)) {
- code 109
- } else {
- code 108
- }
- code 110
- if (uint(i5) >= uint(i6)) {
- code 112
- } else {
- code 111
- }
- code 113
- if (uint(i5) >= uint(i6)) {
- code 115
- } else {
- code 114
- }
- code 116
- if (uint(i5) >= uint(i6)) {
- code 118
- } else {
- code 117
- }
- code 119
- if (uint(i5) >= uint(i6)) {
- code 121
- } else {
- code 120
- }
- code 122
- if (uint(i5) >= uint(i6)) {
- code 124
- } else {
- code 123
- }
- code 125
- if (uint(i5) >= uint(i6)) {
- code 127
- } else {
- code 126
- }
- code 128
- if (uint(i5) >= uint(i6)) {
- code 130
- } else {
- code 129
- }
- code 131
- if (uint(i5) >= uint(i6)) {
- code 133
- } else {
- code 132
- }
- code 134
- if (uint(i5) >= uint(i6)) {
- code 136
- } else {
- code 135
- }
- code 137
- if (uint(i5) >= uint(i6)) {
- code 139
- } else {
- code 138
- }
- code 140
- if (uint(i5) >= uint(i6)) {
- code 142
- } else {
- code 141
- }
- code 143
- if (uint(i5) >= uint(i6)) {
- code 145
- } else {
- code 144
- }
- code 146
- if (uint(i5) >= uint(i6)) {
- code 148
- } else {
- code 147
- }
- code 149
- if (uint(i5) >= uint(i6)) {
- code 151
- } else {
- code 150
- }
- code 152
- if (uint(i5) >= uint(i6)) {
- code 154
- } else {
- code 153
- }
- code 155
- if (uint(i5) >= uint(i6)) {
- code 157
- } else {
- code 156
- }
- code 158
- if (uint(i5) >= uint(i6)) {
- code 160
- } else {
- code 159
- }
- code 161
- if (uint(i5) >= uint(i6)) {
- code 163
- } else {
- code 162
- }
- code 164
- if (uint(i5) >= uint(i6)) {
- code 166
- } else {
- code 165
- }
- code 167
- if (!(i2 != 0)) {
- break L143;
+L143: do {
+ if (!(i2 == 0)) {
+ code 106
+ while(1) {
+ code 107
+ if (uint(i5) >= uint(i6)) {
+ code 109
+ } else {
+ code 108
+ }
+ code 110
+ if (uint(i5) >= uint(i6)) {
+ code 112
+ } else {
+ code 111
+ }
+ code 113
+ if (uint(i5) >= uint(i6)) {
+ code 115
+ } else {
+ code 114
+ }
+ code 116
+ if (uint(i5) >= uint(i6)) {
+ code 118
+ } else {
+ code 117
+ }
+ code 119
+ if (uint(i5) >= uint(i6)) {
+ code 121
+ } else {
+ code 120
+ }
+ code 122
+ if (uint(i5) >= uint(i6)) {
+ code 124
+ } else {
+ code 123
+ }
+ code 125
+ if (uint(i5) >= uint(i6)) {
+ code 127
+ } else {
+ code 126
+ }
+ code 128
+ if (uint(i5) >= uint(i6)) {
+ code 130
+ } else {
+ code 129
+ }
+ code 131
+ if (uint(i5) >= uint(i6)) {
+ code 133
+ } else {
+ code 132
+ }
+ code 134
+ if (uint(i5) >= uint(i6)) {
+ code 136
+ } else {
+ code 135
+ }
+ code 137
+ if (uint(i5) >= uint(i6)) {
+ code 139
+ } else {
+ code 138
+ }
+ code 140
+ if (uint(i5) >= uint(i6)) {
+ code 142
+ } else {
+ code 141
+ }
+ code 143
+ if (uint(i5) >= uint(i6)) {
+ code 145
+ } else {
+ code 144
+ }
+ code 146
+ if (uint(i5) >= uint(i6)) {
+ code 148
+ } else {
+ code 147
+ }
+ code 149
+ if (uint(i5) >= uint(i6)) {
+ code 151
+ } else {
+ code 150
+ }
+ code 152
+ if (uint(i5) >= uint(i6)) {
+ code 154
+ } else {
+ code 153
+ }
+ code 155
+ if (uint(i5) >= uint(i6)) {
+ code 157
+ } else {
+ code 156
+ }
+ code 158
+ if (uint(i5) >= uint(i6)) {
+ code 160
+ } else {
+ code 159
+ }
+ code 161
+ if (uint(i5) >= uint(i6)) {
+ code 163
+ } else {
+ code 162
+ }
+ code 164
+ if (uint(i5) >= uint(i6)) {
+ code 166
+ } else {
+ code 165
+ }
+ code 167
+ if (!(i2 != 0)) {
+ break L143;
+ }
}
}
-}
+} while(0);
code 168
if (uint(i4) >= uint(i5)) {
code 170
diff --git a/src/runtime.js b/src/runtime.js
index e5b86065..dc604a8d 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -25,7 +25,7 @@ var RuntimeGenerator = {
sep = 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));
+ var ret = RuntimeGenerator.alloc(size, 'STACK', false, sep, USE_TYPED_ARRAYS != 2 || (isNumber(size) && parseInt(size) % {{{ QUANTUM_SIZE }}} == 0));
if (ASSERTIONS) {
ret += sep + 'assert(STACKTOP|0 < STACK_MAX|0)';
}
@@ -45,7 +45,7 @@ var RuntimeGenerator = {
if (ASSERTIONS) {
ret += '; assert(STACKTOP < STACK_MAX)';
}
- if (INIT_STACK) {
+ if (false) {
ret += '; _memset(' + asmCoercion('__stackBase__', 'i32') + ', 0, ' + initial + ')';
}
return ret;
@@ -242,6 +242,10 @@ var Runtime = {
} else if (Runtime.isStructType(field)) {
size = Types.types[field].flatSize;
alignSize = Types.types[field].alignSize;
+ } else if (field[0] == 'b') {
+ // bN, large number field, like a [N x i8]
+ size = field.substr(1)|0;
+ alignSize = 1;
} else {
throw 'Unclear type in struct: ' + field + ', in ' + type.name_ + ' :: ' + dump(Types.types[type.name_]);
}
@@ -353,7 +357,7 @@ var Runtime = {
},
addFunction: function(func, sig) {
- assert(sig);
+ //assert(sig); // TODO: support asm
var table = FUNCTION_TABLE; // TODO: support asm
var ret = table.length;
table.push(func);
@@ -361,6 +365,11 @@ var Runtime = {
return ret;
},
+ removeFunction: function(index) {
+ var table = FUNCTION_TABLE; // TODO: support asm
+ table[index] = null;
+ },
+
warnOnce: function(text) {
if (!Runtime.warnOnce.shown) Runtime.warnOnce.shown = {};
if (!Runtime.warnOnce.shown[text]) {
@@ -505,26 +514,19 @@ function getRuntime() {
// example, -1 in int32 would be a very large number as unsigned.
function unSign(value, bits, ignore, sig) {
if (value >= 0) {
-#if CHECK_SIGNS
- if (!ignore) CorrectionsMonitor.note('UnSign', 1, sig);
-#endif
return value;
}
#if CHECK_SIGNS
- if (!ignore) CorrectionsMonitor.note('UnSign', 0, sig);
+ if (!ignore) throw 'UnSign';
#endif
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;
- // TODO: clean up previous line
}
// 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, sig) {
if (value <= 0) {
-#if CHECK_SIGNS
- if (!ignore) CorrectionsMonitor.note('ReSign', 1, sig);
-#endif
return value;
}
var half = bits <= 32 ? Math.abs(1 << (bits-1)) // abs is needed if bits == 32
@@ -536,10 +538,7 @@ function reSign(value, bits, ignore, sig) {
// 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
#if CHECK_SIGNS
- if (!ignore) {
- CorrectionsMonitor.note('ReSign', 0, sig);
- noted = true;
- }
+ if (!ignore) throw 'ReSign';
#endif
value = -2*half + value; // Cannot bitshift half, as it may be at the limit of the bits JS uses in bitshifts
}
@@ -548,18 +547,9 @@ function reSign(value, bits, ignore, sig) {
// without CHECK_SIGNS, we would just do the |0 shortcut, so check that that
// would indeed give the exact same result.
if (bits === 32 && (value|0) !== value && typeof value !== 'boolean') {
- if (!ignore) {
- CorrectionsMonitor.note('ReSign', 0, sig);
- noted = true;
- }
+ if (!ignore) throw 'ReSign';
}
- if (!noted) CorrectionsMonitor.note('ReSign', 1, sig);
#endif
return value;
}
-// Just a stub. We don't care about noting compile-time corrections. But they are called.
-var CorrectionsMonitor = {
- note: function(){}
-};
-
diff --git a/src/settings.js b/src/settings.js
index 0234d0ca..1bfcf92a 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -37,7 +37,6 @@ var VERBOSE = 0; // When set to 1, will generate more verbose output during comp
var INVOKE_RUN = 1; // Whether we will call run(). Disable if you embed the generated
// code in your own, and will call run() yourself at the right time
-var INIT_STACK = 0; // Whether to initialize memory on the stack to 0.
var INIT_HEAP = 0; // Whether to initialize memory anywhere other than the stack to 0.
var TOTAL_STACK = 5*1024*1024; // The total stack size. There is no way to enlarge the stack, so this
// value must be large enough for the program's requirements. If
@@ -59,6 +58,8 @@ var ALLOW_MEMORY_GROWTH = 0; // If false, we abort with an error if we try to al
// Code embetterments
var MICRO_OPTS = 1; // Various micro-optimizations, like nativizing variables
var RELOOP = 0; // Recreate js native loops from llvm data
+var RELOOPER = 'relooper.js'; // Loads the relooper from this path relative to compiler.js
+
var USE_TYPED_ARRAYS = 2; // Use typed arrays for the heap. See https://github.com/kripken/emscripten/wiki/Code-Generation-Modes/
// 0 means no typed arrays are used.
// 1 has two heaps, IHEAP (int32) and FHEAP (double),
@@ -92,13 +93,9 @@ var PRECISE_I64_MATH = 1; // If enabled, i64 addition etc. is emulated - which i
// that we can't know at compile time that 64-bit math is needed. For example, if you
// print 64-bit values with printf, but never add them, we can't know at compile time
// and you need to set this to 2.
-var PRECISE_I32_MUL = 0; // If enabled, i64 math is done in i32 multiplication. This is necessary if the values
- // exceed the JS double-integer limit of ~52 bits. This option can normally be disabled
- // because generally i32 multiplication works ok without it, and enabling it has a big
- // impact on performance.
- // Note that you can hand-optimize your code to avoid the need for this: If you do
- // multiplications that actually need 64-bit precision inside 64-bit values, things
- // will work properly. (Unless the LLVM optimizer turns them into 32-bit values?)
+var PRECISE_I32_MUL = 1; // If enabled, i32 multiplication is done with full precision, which means it is
+ // correct even if the value exceeds the JS double-integer limit of ~52 bits (otherwise,
+ // rounding will occur above that range).
var CLOSURE_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure
// compiler. This potentially lets closure optimize the code better.
@@ -111,10 +108,11 @@ var SKIP_STACK_IN_SMALL = 1; // When enabled, does not push/pop the stack at all
// In particular, be careful with the autodebugger! (We do turn
// this off automatically in that case, though.)
var INLINE_LIBRARY_FUNCS = 1; // Will inline library functions that have __inline defined
-var INLINING_LIMIT = 50; // A limit on inlining. If 0, we will inline normally in LLVM and
+var INLINING_LIMIT = 0; // A limit on inlining. If 0, we will inline normally in LLVM and
// closure. If greater than 0, we will *not* inline in LLVM, and
// we will prevent inlining of functions of this size or larger
- // in closure.
+ // in closure. 50 is a reasonable setting if you do not want
+ // inlining
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
@@ -132,8 +130,23 @@ var SAFE_HEAP = 0; // Check each write to the heap, for example, this will give
// that 3 is the option you usually want here.
var SAFE_HEAP_LOG = 0; // Log out all SAFE_HEAP operations
+var CHECK_HEAP_ALIGN = 0; // Check heap accesses for alignment, but don't do as
+ // near extensive (or slow) checks as SAFE_HEAP.
+
+var SAFE_DYNCALLS = 0; // Show stack traces on missing function pointer/virtual method calls
+
var ASM_HEAP_LOG = 0; // Simple heap logging, like SAFE_HEAP_LOG but cheaper, and in asm.js
+var CORRUPTION_CHECK = 0; // When enabled, will emit a buffer area at the beginning and
+ // end of each allocation on the heap, filled with canary
+ // values that can be checked later. Corruption is checked for
+ // at the end of each at each free() (see jsifier to add more, and you
+ // can add more manual checks by calling CorruptionChecker.checkAll).
+ // 0 means not enabled, higher values mean the size of the
+ // buffer areas as a multiple of the allocated area (so
+ // 1 means 100%, or buffer areas equal to allocated area,
+ // both before and after). This must be an integer.
+
var LABEL_DEBUG = 0; // 1: Print out functions as we enter them
// 2: Also print out each label as we enter it
var LABEL_FUNCTION_FILTERS = []; // Filters for function label debug.
@@ -148,11 +161,15 @@ var LIBRARY_DEBUG = 0; // Print out when we enter a library call (library*.js).
// Runtime.debug at runtime for logging to cease, and can set it when you
// want it back. A simple way to set it in C++ is
// emscripten_run_script("Runtime.debug = ...;");
-var GL_DEBUG = 0; // Print out all calls into WebGL. As with LIBRARY_DEBUG, you can set a runtime
- // option, in this case GL.debug.
var SOCKET_DEBUG = 0; // Log out socket/network data transfer.
-var PROFILE_MAIN_LOOP = 0; // Profile the function called in set_main_loop
+var GL_DEBUG = 0; // Print out all calls into WebGL. As with LIBRARY_DEBUG, you can set a runtime
+ // option, in this case GL.debug.
+var GL_TESTING = 0; // When enabled, sets preserveDrawingBuffer in the context, to allow tests to work (but adds overhead)
+var GL_MAX_TEMP_BUFFER_SIZE = 2097152; // How large GL emulation temp buffers are
+var GL_UNSAFE_OPTS = 1; // Enables some potentially-unsafe optimizations in GL emulation code
+var FULL_ES2 = 0; // Forces support for all GLES2 features, not just the WebGL-friendly subset.
+var FORCE_GL_EMULATION = 0; // Forces inclusion of full GL emulation code.
var DISABLE_EXCEPTION_CATCHING = 0; // Disables generating code to actually catch exceptions. If the code you
// are compiling does not actually rely on catching exceptions (but the
@@ -203,21 +220,10 @@ var FS_LOG = 0; // Log all FS operations. This is especially helpful when you'r
// a new project and want to see a list of file system operations happening
// so that you can create a virtual file system with all of the required files.
-var PGO = 0; // Profile-guided optimization.
- // When run with the CHECK_* options, will not fail on errors. Instead, will
- // keep a record of which checks succeeded and which failed. On shutdown, will
- // print out that information. This is useful for knowing which lines need
- // checking enabled and which do not, that is, this is a way to automate the
- // generation of line data for CORRECT_*_LINES options.
- // All CORRECT_* options default to 1 with PGO builds.
- // See https://github.com/kripken/emscripten/wiki/Optimizing-Code for more info
-
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.
-var PROFILE = 0; // Enables runtime profiling. See test_profiling for a usage example.
-
var EXPORT_ALL = 0; // If true, we export all the symbols
var EXPORTED_FUNCTIONS = ['_main']; // Functions that are explicitly exported. These functions are kept alive
// through LLVM dead code elimination, and also made accessible outside of
@@ -325,12 +331,13 @@ var BENCHMARK = 0; // If 1, will just time how long main() takes to execute, and
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 EXPLICIT_ZEXT = 0; // If 1, generate an explicit conversion of zext i1 to i32, using ?:
var NECESSARY_BLOCKADDRS = []; // List of (function, block) for all block addresses that are taken.
+var EMIT_GENERATED_FUNCTIONS = 0; // whether to emit the list of generated functions, needed for external JS optimization passes
+
// Compiler debugging options
var DEBUG_TAGS_SHOWING = [];
// Some useful items:
diff --git a/src/shell.html b/src/shell.html
index 3a0171de..8743d403 100644
--- a/src/shell.html
+++ b/src/shell.html
@@ -19,7 +19,14 @@
</div>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<hr/>
- <div class="emscripten"><input type="button" value="fullscreen" onclick="Module.requestFullScreen()"></div>
+ <div class="emscripten">
+ <input type="checkbox" id="resize">Resize canvas
+ <input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer
+ &nbsp;&nbsp;&nbsp;
+ <input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked,
+ document.getElementById('resize').checked)">
+ </div>
+
<hr/>
<textarea class="emscripten" id="output" rows="8"></textarea>
<hr>
@@ -32,6 +39,7 @@
var element = document.getElementById('output');
element.value = ''; // clear browser cache
return function(text) {
+ text = Array.prototype.slice.call(arguments).join(' ');
// These replacements are necessary if you render to raw HTML
//text = text.replace(/&/g, "&amp;");
//text = text.replace(/</g, "&lt;");
@@ -42,6 +50,7 @@
};
})(),
printErr: function(text) {
+ text = Array.prototype.slice.call(arguments).join(' ');
if (0) { // XXX disabled for safety typeof dump == 'function') {
dump(text + '\n'); // fast, straight to the real console
} else {