summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2013-03-15 18:21:34 -0700
committerAlon Zakai <alonzakai@gmail.com>2013-03-15 18:21:34 -0700
commitb22f6fbbbebb5df55ceb8fdc9f7c4d111c902c5e (patch)
treec568136b2cf95d897d128b362720602a3b401244 /src
parent8c9a37a40a164dba330390af2eabf5ad05625001 (diff)
parent27d1a249622d33ab8aff2814d13569507336873b (diff)
Merge branch 'incoming'
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js22
-rw-r--r--src/compiler.js10
-rw-r--r--src/experimental/allow_loopvars_from_memsetcpy_inasm.diff97
-rw-r--r--src/experimental/simplifyGeneratedFunctionsDetection.diff336
-rw-r--r--src/headless.js5
-rw-r--r--src/intertyper.js23
-rw-r--r--src/jsifier.js72
-rw-r--r--src/library.js501
-rw-r--r--src/library_browser.js80
-rw-r--r--src/library_gl.js228
-rw-r--r--src/library_openal.js471
-rw-r--r--src/library_sdl.js105
-rw-r--r--src/long.js18
-rw-r--r--src/modules.js7
-rw-r--r--src/parseTools.js86
-rw-r--r--src/preamble.js34
-rw-r--r--src/relooper/Relooper.cpp72
-rw-r--r--src/relooper/fuzzer.py4
-rw-r--r--src/relooper/test.txt4
-rw-r--r--src/runtime.js66
-rw-r--r--src/settings.js29
-rw-r--r--src/shell.html9
-rw-r--r--src/utility.js9
23 files changed, 1730 insertions, 558 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index ecb5ea6b..92b7d8cf 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -18,7 +18,7 @@ function recomputeLines(func) {
// Handy sets
var BRANCH_INVOKE = set('branch', 'invoke');
-var LABEL_ENDERS = set('branch', 'return');
+var LABEL_ENDERS = set('branch', 'return', 'switch');
var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic');
var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam');
@@ -231,9 +231,10 @@ function analyzer(data, sidePass) {
}
if (isIllegalType(item.valueType) || isIllegalType(item.type)) {
isIllegal = true;
- }
- if ((item.intertype == 'load' || item.intertype == 'store') && isStructType(item.valueType)) {
+ } else if ((item.intertype == 'load' || item.intertype == 'store') && isStructType(item.valueType)) {
isIllegal = true; // storing an entire structure is illegal
+ } else if (item.intertype == 'mathop' && item.op == 'trunc' && isIllegalType(item.params[1].ident)) { // trunc stores target value in second ident
+ isIllegal = true;
}
});
if (!isIllegal) {
@@ -290,7 +291,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,
@@ -401,7 +402,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,
@@ -652,13 +653,14 @@ function analyzer(data, sidePass) {
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');
+ assert(PRECISE_I64_MATH, 'Must have precise i64 math for non-constant 64-bit shifts');
+ Types.preciseI64MathUsed = 1;
value.intertype = 'value';
- value.ident = 'Runtime' + (ASM_JS ? '_' : '.') + 'bitshift64(' +
+ value.ident = 'var ' + value.assignTo + '$0 = _bitshift64' + value.op[0].toUpperCase() + value.op.substr(1) + '(' +
asmCoercion(sourceElements[0].ident, 'i32') + ',' +
asmCoercion(sourceElements[1].ident, 'i32') + ',' +
- Runtime['BITSHIFT64_' + value.op.toUpperCase()] + ',' +
asmCoercion(value.params[1].ident + '$0', 'i32') + ');' +
- 'var ' + value.assignTo + '$0 = ' + makeGetTempDouble(0, 'i32') + ', ' + value.assignTo + '$1 = ' + makeGetTempDouble(1, 'i32') + ';';
+ 'var ' + value.assignTo + '$1 = tempRet0;';
value.assignTo = null;
i++;
continue;
@@ -952,6 +954,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,
@@ -965,6 +968,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,
@@ -1380,7 +1384,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 3047daf1..bb72c7dd 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -141,8 +141,13 @@ if (phase == 'pre') {
if (settings_file) {
var settings = JSON.parse(read(settings_file));
- for (setting in settings) {
- eval(setting + ' = ' + JSON.stringify(settings[setting]));
+ for (key in settings) {
+ var value = settings[key];
+ if (value[0] == '@') {
+ // response file type thing, workaround for large inputs: value is @path-to-file
+ value = JSON.parse(read(value.substr(1)));
+ }
+ eval(key + ' = ' + JSON.stringify(value));
}
}
@@ -163,6 +168,7 @@ if (SAFE_HEAP >= 2) {
EXPORTED_FUNCTIONS = set(EXPORTED_FUNCTIONS);
EXPORTED_GLOBALS = set(EXPORTED_GLOBALS);
EXCEPTION_CATCHING_WHITELIST = set(EXCEPTION_CATCHING_WHITELIST);
+DEAD_FUNCTIONS = numberedSet(DEAD_FUNCTIONS);
RUNTIME_DEBUG = LIBRARY_DEBUG || GL_DEBUG;
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/experimental/simplifyGeneratedFunctionsDetection.diff b/src/experimental/simplifyGeneratedFunctionsDetection.diff
new file mode 100644
index 00000000..09e0ebcd
--- /dev/null
+++ b/src/experimental/simplifyGeneratedFunctionsDetection.diff
@@ -0,0 +1,336 @@
+diff --git a/emscripten.py b/emscripten.py
+index b698654..a7843c7 100755
+--- a/emscripten.py
++++ b/emscripten.py
+@@ -294,8 +294,6 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
+ outfile.write(blockaddrsize(indexize(pre)))
+ pre = None
+
+- #if DEBUG: outfile.write('// funcs\n')
+-
+ # forward
+ forwarded_data = json.dumps(forwarded_json)
+ forwarded_file = temp_files.get('.2.json').name
+@@ -483,9 +481,14 @@ Runtime.stackRestore = function(top) { asm.stackRestore(top) };
+ else:
+ function_tables_defs = '\n'.join([table for table in last_forwarded_json['Functions']['tables'].itervalues()])
+ outfile.write(function_tables_defs)
++
++ outfile.write('// EMSCRIPTEN_START_FUNCS\n')
++
+ outfile.write(blockaddrsize(indexize(funcs_js)))
+ funcs_js = None
+
++ outfile.write('// EMSCRIPTEN_END_FUNCS\n')
++
+ outfile.write(indexize(post))
+ if DEBUG: print >> sys.stderr, ' emscript: phase 3 took %s seconds' % (time.time() - t)
+
+diff --git a/src/jsifier.js b/src/jsifier.js
+index ff58ece..d6aa110 100644
+--- a/src/jsifier.js
++++ b/src/jsifier.js
+@@ -1587,12 +1587,6 @@ 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)
+- 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/settings.js b/src/settings.js
+index 1bfcf92..99c83f4 100644
+--- a/src/settings.js
++++ b/src/settings.js
+@@ -336,8 +336,6 @@ var EXPLICIT_ZEXT = 0; // If 1, generate an explicit conversion of zext i1 to i3
+
+ 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/tools/js-optimizer.js b/tools/js-optimizer.js
+index f2dc516..9fa038c 100644
+--- a/tools/js-optimizer.js
++++ b/tools/js-optimizer.js
+@@ -140,16 +140,6 @@ var UNDEFINED_NODE = ['unary-prefix', 'void', ['num', 0]];
+ var TRUE_NODE = ['unary-prefix', '!', ['num', 0]];
+ var FALSE_NODE = ['unary-prefix', '!', ['num', 1]];
+
+-var GENERATED_FUNCTIONS_MARKER = '// EMSCRIPTEN_GENERATED_FUNCTIONS:';
+-var generatedFunctions = null;
+-function setGeneratedFunctions(metadata) {
+- var start = metadata.indexOf(GENERATED_FUNCTIONS_MARKER);
+- generatedFunctions = set(eval(metadata.substr(start + GENERATED_FUNCTIONS_MARKER.length)));
+-}
+-function isGenerated(ident) {
+- return ident in generatedFunctions;
+-}
+-
+ function srcToAst(src) {
+ return uglify.parser.parse(src);
+ }
+@@ -212,21 +202,9 @@ function traverse(node, pre, post, stack) {
+ return result;
+ }
+
+-// Only walk through the generated functions
+-function traverseGenerated(ast, pre, post, stack) {
+- assert(generatedFunctions);
+- traverse(ast, function(node) {
+- if (node[0] == 'defun' && isGenerated(node[1])) {
+- traverse(node, pre, post, stack);
+- return null;
+- }
+- });
+-}
+-
+-function traverseGeneratedFunctions(ast, callback) {
+- assert(generatedFunctions);
++function traverseFunctions(ast, callback) {
+ traverse(ast, function(node) {
+- if (node[0] == 'defun' && isGenerated(node[1])) {
++ if (node[0] == 'defun') {
+ callback(node);
+ return null;
+ }
+@@ -418,7 +396,7 @@ function simplifyExpressionsPre(ast) {
+ var rerun = true;
+ while (rerun) {
+ rerun = false;
+- traverseGenerated(ast, function process(node, type, stack) {
++ traverse(ast, function process(node, type, stack) {
+ if (type == 'binary' && node[1] == '|') {
+ if (node[2][0] == 'num' && node[3][0] == 'num') {
+ return ['num', node[2][1] | node[3][1]];
+@@ -455,7 +433,7 @@ function simplifyExpressionsPre(ast) {
+ }
+
+ // &-related optimizations
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ if (type == 'binary' && node[1] == '&' && node[3][0] == 'num') {
+ if (node[2][0] == 'num') return ['num', node[2][1] & node[3][1]];
+ var input = node[2];
+@@ -489,7 +467,7 @@ function simplifyExpressionsPre(ast) {
+
+ if (asm) {
+ // optimize num >> num, in asm we need this here since we do not run optimizeShifts
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ if (type == 'binary' && node[1] == '>>' && node[2][0] == 'num' && node[3][0] == 'num') {
+ node[0] = 'num';
+ node[1] = node[2][1] >> node[3][1];
+@@ -505,7 +483,7 @@ function simplifyExpressionsPre(ast) {
+ var rerun = true;
+ while (rerun) {
+ rerun = false;
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ if (type == 'binary' && node[1] == '+') {
+ if (node[2][0] == 'num' && node[3][0] == 'num') {
+ rerun = true;
+@@ -528,7 +506,7 @@ function simplifyExpressionsPre(ast) {
+
+ // if (x == 0) can be if (!x), etc.
+ function simplifyZeroComp(ast) {
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ var binary;
+ if (type == 'if' && (binary = node[1])[0] == 'binary') {
+ if ((binary[1] == '!=' || binary[1] == '!==') && binary[3][0] == 'num' && binary[3][1] == 0) {
+@@ -554,7 +532,7 @@ function simplifyExpressionsPre(ast) {
+ // TODO: when shifting a variable, if there are other uses, keep an unshifted version too, to prevent slowdowns?
+ function optimizeShiftsInternal(ast, conservative) {
+ var MAX_SHIFTS = 3;
+- traverseGeneratedFunctions(ast, function(fun) {
++ traverseFunctions(ast, function(fun) {
+ var funMore = true;
+ var funFinished = {};
+ while (funMore) {
+@@ -999,7 +977,7 @@ function vacuum(ast) {
+ } break;
+ }
+ }
+- traverseGeneratedFunctions(ast, function(node) {
++ traverseFunctions(ast, function(node) {
+ vacuumInternal(node);
+ simplifyNotComps(node);
+ });
+@@ -1021,7 +999,7 @@ function getStatements(node) {
+ // if (condition) { label == x } else ..
+ // We can hoist the multiple block into the condition, thus removing code and one 'if' check
+ function hoistMultiples(ast) {
+- traverseGeneratedFunctions(ast, function(node) {
++ traverseFunctions(ast, function(node) {
+ traverse(node, function(node, type) {
+ var statements = getStatements(node);
+ if (!statements) return;
+@@ -1135,7 +1113,7 @@ function hoistMultiples(ast) {
+ // if (..) { .. break|continue } else { .. }
+ // to
+ // if (..) { .. break|continue } ..
+- traverseGenerated(ast, function(container, type) {
++ traverse(ast, function(container, type) {
+ var statements = getStatements(container);
+ if (!statements) return;
+ for (var i = 0; i < statements.length; i++) {
+@@ -1168,7 +1146,7 @@ function loopOptimizer(ast) {
+ function passTwo(ast) {
+ var neededDos = [];
+ // Find unneeded labels
+- traverseGenerated(ast, function(node, type, stack) {
++ traverse(ast, function(node, type, stack) {
+ if (type == 'label' && node[2][0] in LOOP) {
+ // this is a labelled loop. we don't know if it's needed yet. Mark its label for removal for now now.
+ stack.push(node);
+@@ -1212,7 +1190,7 @@ function loopOptimizer(ast) {
+ // We return whether another pass is necessary
+ var more = false;
+ // Remove unneeded labels
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ if (type == 'label' && node[1][0] == '+') {
+ more = true;
+ var ident = node[1].substr(1);
+@@ -1227,13 +1205,13 @@ function loopOptimizer(ast) {
+ });
+ // Remove unneeded one-time loops. We need such loops if (1) they have a label, or (2) they have a direct break so they are in neededDos.
+ // First, add all labeled loops of this nature to neededDos
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ if (type == 'label' && node[2][0] == 'do') {
+ neededDos.push(node[2]);
+ }
+ });
+ // Remove unneeded dos, we know who they are now
+- traverseGenerated(ast, function(node, type) {
++ traverse(ast, function(node, type) {
+ if (type == 'do' && neededDos.indexOf(node) < 0) {
+ assert(jsonCompare(node[1], ['num', 0]), 'Trying to remove a one-time do loop that is not one of our generated ones.;');
+ more = true;
+@@ -1407,7 +1385,7 @@ function denormalizeAsm(func, data) {
+ // we still need the eliminator? Closure? And in what order? Perhaps just
+ // closure simple?
+ function registerize(ast) {
+- traverseGeneratedFunctions(ast, function(fun) {
++ traverseFunctions(ast, function(fun) {
+ if (asm) var asmData = normalizeAsm(fun);
+ // Add parameters as a first (fake) var (with assignment), so they get taken into consideration
+ var params = {}; // note: params are special, they can never share a register between them (see later)
+@@ -1671,7 +1649,7 @@ var ABORTING_ELIMINATOR_SCAN_NODES = set('new', 'object', 'function', 'defun', '
+
+ function eliminate(ast, memSafe) {
+ // Find variables that have a single use, and if they can be eliminated, do so
+- traverseGeneratedFunctions(ast, function(func, type) {
++ traverseFunctions(ast, function(func, type) {
+ if (asm) var asmData = normalizeAsm(func);
+ //printErr('eliminate in ' + func[1]);
+
+@@ -2218,9 +2196,6 @@ var passes = {
+ var src = read(arguments_[0]);
+ var ast = srcToAst(src);
+ //printErr(JSON.stringify(ast)); throw 1;
+-var metadata = src.split('\n').filter(function(line) { return line.indexOf(GENERATED_FUNCTIONS_MARKER) >= 0 })[0];
+-//assert(metadata, 'Must have EMSCRIPTEN_GENERATED_FUNCTIONS metadata');
+-if (metadata) setGeneratedFunctions(metadata);
+
+ arguments_.slice(1).forEach(function(arg) {
+ passes[arg](ast);
+diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py
+index 2fd2211..a253afb 100644
+--- a/tools/js_optimizer.py
++++ b/tools/js_optimizer.py
+@@ -41,17 +41,17 @@ def run_on_js(filename, passes, js_engine, jcache):
+ if os.linesep != '\n':
+ js = js.replace(os.linesep, '\n') # we assume \n in the splitting code
+
+- # Find suffix
+- suffix_marker = '// EMSCRIPTEN_GENERATED_FUNCTIONS'
+- suffix_start = js.find(suffix_marker)
+- suffix = ''
+- if suffix_start >= 0:
+- suffix = js[suffix_start:js.find('\n', suffix_start)] + '\n'
+- # if there is metadata, we will run only on the generated functions. If there isn't, we will run on everything.
+- generated = set(eval(suffix[len(suffix_marker)+1:]))
+-
+- if not suffix and jcache:
+- # JCache cannot be used without metadata, since it might reorder stuff, and that's dangerous since only generated can be reordered
++ # Find markers
++ start_marker = '// EMSCRIPTEN_START_FUNCS\n'
++ end_marker = '// EMSCRIPTEN_END_FUNCS\n'
++ start = js.find(start_marker)
++ end = js.find(end_marker)
++ assert (start >= 0) == (end >= 0), 'must have both markers or neither'
++ have_markers = start >= 0
++ # if there are markers, we will run only on the generated functions. If there isn't, we will run on everything.
++
++ if not have_markers and jcache:
++ # JCache cannot be used without markers, since it might reorder stuff, and that's dangerous since only generated can be reordered
+ # This means jcache does not work after closure compiler runs, for example. But you won't get much benefit from jcache with closure
+ # anyhow (since closure is likely the longest part of the build).
+ if DEBUG: print >>sys.stderr, 'js optimizer: no metadata, so disabling jcache'
+@@ -59,27 +59,10 @@ def run_on_js(filename, passes, js_engine, jcache):
+
+ # If we process only generated code, find that and save the rest on the side
+ func_sig = re.compile('( *)function (_[\w$]+)\(')
+- if suffix:
+- pos = 0
+- gen_start = 0
+- gen_end = 0
+- while 1:
+- m = func_sig.search(js, pos)
+- if not m: break
+- pos = m.end()
+- indent = m.group(1)
+- ident = m.group(2)
+- if ident in generated:
+- if not gen_start:
+- gen_start = m.start()
+- assert gen_start
+- gen_end = js.find('\n%s}\n' % indent, m.end()) + (3 + len(indent))
+- assert gen_end > gen_start
+- pre = js[:gen_start]
+- post = js[gen_end:]
+- if 'last' in passes:
+- post = post.replace(suffix, '') # no need to write out the metadata - nothing after us needs it
+- js = js[gen_start:gen_end]
++ if have_markers:
++ pre = js[:start + len(start_marker)] # includes start marker
++ post = js[end:] # includes end marker
++ js = js[start + len(start_marker):end]
+ else:
+ pre = ''
+ post = ''
+@@ -95,7 +78,7 @@ def run_on_js(filename, passes, js_engine, jcache):
+ if m:
+ ident = m.group(2)
+ else:
+- if suffix: continue # ignore whitespace
++ if have_markers: continue # ignore whitespace
+ ident = 'anon_%d' % i
+ assert ident
+ funcs.append((ident, func))
+@@ -131,7 +114,6 @@ def run_on_js(filename, passes, js_engine, jcache):
+ temp_file = temp_files.get('.jsfunc_%d.js' % i).name
+ f = open(temp_file, 'w')
+ f.write(chunk)
+- f.write(suffix)
+ f.close()
+ return temp_file
+ filenames = [write_chunk(chunks[i], i) for i in range(len(chunks))]
+@@ -169,7 +151,6 @@ def run_on_js(filename, passes, js_engine, jcache):
+ f.write(cached); # TODO: preserve order
+ f.write('\n')
+ f.write(post);
+- # No need to write suffix: if there was one, it is inside post which exists when suffix is there
+ f.write('\n')
+ f.close()
+
diff --git a/src/headless.js b/src/headless.js
index 8e847d27..d81fb5a3 100644
--- a/src/headless.js
+++ b/src/headless.js
@@ -537,7 +537,7 @@ var document = {
case /* GL_MAX_FRAGMENT_UNIFORM_VECTORS */ 0x8DFD: return 4096;
case /* GL_MAX_VARYING_VECTORS */ 0x8DFC: return 32;
case /* GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS */ 0x8B4D: return 32;
- default: throw 'getParameter ' + pname;
+ default: console.log('getParameter ' + pname + '?'); return 0;
}
},
getSupportedExtensions: function() {
@@ -686,6 +686,7 @@ var document = {
document.callEventListeners('pointerlockchange');
});
},
+ exitPointerLock: function(){},
style: {},
eventListeners: {},
addEventListener: document.addEventListener,
@@ -748,6 +749,8 @@ var document = {
body: {
appendChild: function(){},
},
+ exitPointerLock: function(){},
+ cancelFullScreen: function(){},
};
var alert = function(x) {
print(x);
diff --git a/src/intertyper.js b/src/intertyper.js
index 2103ecfa..57e3011d 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -122,16 +122,19 @@ function intertyper(data, sidePass, baseLineNums) {
SKIP_STACK_IN_SMALL = 0;
}
- unparsedBundles.push({
- intertype: 'unparsedFunction',
- // We need this early, to know basic function info - ident, params, varargs
- ident: toNiceIdent(func.ident),
- params: func.params,
- returnType: func.returnType,
- hasVarArgs: func.hasVarArgs,
- lineNum: currFunctionLineNum,
- lines: currFunctionLines
- });
+ var ident = toNiceIdent(func.ident);
+ if (!(ident in DEAD_FUNCTIONS)) {
+ unparsedBundles.push({
+ intertype: 'unparsedFunction',
+ // We need this early, to know basic function info - ident, params, varargs
+ ident: ident,
+ params: func.params,
+ returnType: func.returnType,
+ hasVarArgs: func.hasVarArgs,
+ lineNum: currFunctionLineNum,
+ lines: currFunctionLines
+ });
+ }
currFunctionLines = [];
}
}
diff --git a/src/jsifier.js b/src/jsifier.js
index ff58ece2..4263618a 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -397,6 +397,20 @@ function JSify(data, functionsOnly, givenFunctions) {
}
});
+ function processLibraryFunction(snippet, ident) {
+ snippet = snippet.toString();
+ assert(snippet.indexOf('XXX missing C define') == -1,
+ 'Trying to include a library function with missing C defines: ' + ident + ' | ' + snippet);
+
+ // name the function; overwrite if it's already named
+ snippet = snippet.replace(/function(?:\s+([^(]+))?\s*\(/, 'function _' + ident + '(');
+ if (LIBRARY_DEBUG) {
+ snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.printErr("[library call:' + ident + ': " + Array.prototype.slice.call(arguments).map(Runtime.prettyPrint) + "]"); ');
+ snippet = snippet.substr(0, snippet.length-1) + '}).apply(this, arguments); if (Runtime.debug && typeof ret !== "undefined") Module.printErr(" [ return:" + Runtime.prettyPrint(ret)); return ret; \n}';
+ }
+ return snippet;
+ }
+
// functionStub
substrate.addActor('FunctionStub', {
processItem: function(item) {
@@ -434,16 +448,7 @@ function JSify(data, functionsOnly, givenFunctions) {
snippet = stringifyWithFunctions(snippet);
} else if (typeof snippet === 'function') {
isFunction = true;
- snippet = snippet.toString();
- assert(snippet.indexOf('XXX missing C define') == -1,
- 'Trying to include a library function with missing C defines: ' + ident + ' | ' + snippet);
-
- // name the function; overwrite if it's already named
- snippet = snippet.replace(/function(?:\s+([^(]+))?\s*\(/, 'function _' + ident + '(');
- if (LIBRARY_DEBUG) {
- snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.printErr("[library call:' + ident + ': " + Array.prototype.slice.call(arguments).map(Runtime.prettyPrint) + "]"); ');
- snippet = snippet.substr(0, snippet.length-1) + '}).apply(this, arguments); if (Runtime.debug && typeof ret !== "undefined") Module.printErr(" [ return:" + Runtime.prettyPrint(ret)); return ret; \n}';
- }
+ snippet = processLibraryFunction(snippet, ident);
if (ASM_JS) Functions.libraryFunctions[ident] = 1;
}
@@ -600,6 +605,10 @@ function JSify(data, functionsOnly, givenFunctions) {
func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n';
+ if (PGO) {
+ func.JS += ' PGOMonitor.called["' + func.ident + '"] = 1;\n';
+ }
+
if (ASM_JS) {
// spell out argument types
func.params.forEach(function(param) {
@@ -731,7 +740,7 @@ function JSify(data, functionsOnly, givenFunctions) {
return indent + ' case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n'
+ getLabelLines(label, indent + ' ');
}).join('\n') + '\n';
- if (ASSERTIONS) ret += indent + ' default: assert(0, "bad label: " + label);\n';
+ if (ASSERTIONS) ret += indent + ' default: assert(0' + (ASM_JS ? '' : ', "bad label: " + label') + ');\n';
ret += indent + '}\n';
if (func.setjmpTable) {
ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }';
@@ -1312,6 +1321,8 @@ function JSify(data, functionsOnly, givenFunctions) {
} else {
callIdent = ident;
}
+ if (callIdent == '0') return 'abort(-2)';
+
var args = [];
var argsTypes = [];
var varargs = [];
@@ -1414,6 +1425,12 @@ function JSify(data, functionsOnly, givenFunctions) {
returnType = getReturnType(type);
}
+ if (callIdent in DEAD_FUNCTIONS) {
+ var ret = 'abort(' + DEAD_FUNCTIONS[callIdent] + ')';
+ if (ASM_JS) ret = asmCoercion(ret, returnType);
+ return ret;
+ }
+
if (byPointer) {
var sig = Functions.getSignature(returnType, argsTypes, hasVarArgs);
if (ASM_JS) {
@@ -1443,7 +1460,7 @@ function JSify(data, functionsOnly, givenFunctions) {
makeFuncLineActor('unreachable', function(item) {
if (ASSERTIONS) {
- return 'throw "Reached an unreachable!"';
+ return ASM_JS ? 'abort()' : 'throw "Reached an unreachable!"';
} else {
return ';';
}
@@ -1552,16 +1569,25 @@ 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) {
+ if (!INCLUDE_FULL_LIBRARY) {
+ ['i64Add', 'bitshift64Shl', 'bitshift64Lshr', 'bitshift64Ashr'].forEach(function(func) {
+ print(processLibraryFunction(LibraryManager.library[func], func)); // must be first to be close to generated code
+ Functions.implementedFunctions['_' + func] = LibraryManager.library[func + '__sig'];
+ });
+ }
+ print('// EMSCRIPTEN_END_FUNCS\n');
print(read('long.js'));
} else {
+ print('// EMSCRIPTEN_END_FUNCS\n');
print('// Warning: printing of i64 values may be slightly rounded! No deep i64 math used, so precise i64 code not included');
print('var i64Math = null;');
}
+
+ if (CORRUPTION_CHECK) {
+ assert(!ASM_JS); // cannot monkeypatch asm!
+ print(processMacros(read('corruptionCheck.js')));
+ }
if (HEADLESS) {
print('if (!ENVIRONMENT_IS_WEB) {');
print(read('headless.js').replace("'%s'", "'http://emscripten.org'").replace("'?%s'", "''").replace('%s,', 'null,').replace('%d', '0'));
@@ -1587,11 +1613,17 @@ 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)
- if (EMIT_GENERATED_FUNCTIONS) {
- print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) {
+ // Print out some useful metadata
+ if (EMIT_GENERATED_FUNCTIONS || PGO) {
+ var generatedFunctions = JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) {
return IGNORED_FUNCTIONS.indexOf(func.ident) < 0;
- })) + '\n');
+ }));
+ if (PGO) {
+ print('PGOMonitor.allGenerated = ' + generatedFunctions + ';\nremoveRunDependency("pgo");\n');
+ }
+ if (EMIT_GENERATED_FUNCTIONS) {
+ print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + generatedFunctions + '\n');
+ }
}
PassManager.serialize();
diff --git a/src/library.js b/src/library.js
index 1cd7e3f5..45187d8d 100644
--- a/src/library.js
+++ b/src/library.js
@@ -504,7 +504,7 @@ LibraryManager.library = {
}
var utf8 = new Runtime.UTF8Processor();
function simpleOutput(val) {
- if (val === null || val === '\n'.charCodeAt(0)) {
+ if (val === null || val === {{{ charCode('\n') }}}) {
output.printer(output.buffer.join(''));
output.buffer = [];
} else {
@@ -600,8 +600,8 @@ LibraryManager.library = {
quit: function() {
if (!FS.init.initialized) return;
// Flush any partially-printed lines in stdout and stderr. Careful, they may have been closed
- if (FS.streams[2] && FS.streams[2].object.output.buffer.length > 0) FS.streams[2].object.output('\n'.charCodeAt(0));
- if (FS.streams[3] && FS.streams[3].object.output.buffer.length > 0) FS.streams[3].object.output('\n'.charCodeAt(0));
+ if (FS.streams[2] && FS.streams[2].object.output.buffer.length > 0) FS.streams[2].object.output({{{ charCode('\n') }}});
+ if (FS.streams[3] && FS.streams[3].object.output.buffer.length > 0) FS.streams[3].object.output({{{ charCode('\n') }}});
},
// Standardizes a path. Useful for making comparisons of pathnames work in a consistent manner.
@@ -828,11 +828,11 @@ LibraryManager.library = {
// Null or empty results in '.'.
var me = ___libgenSplitName;
if (!me.ret) {
- me.ret = allocate(['.'.charCodeAt(0), 0], 'i8', ALLOC_NORMAL);
+ me.ret = allocate([{{{ charCode('.') }}}, 0], 'i8', ALLOC_NORMAL);
}
return [me.ret, -1];
} else {
- var slash = '/'.charCodeAt(0);
+ var slash = {{{ charCode('/') }}};
var allSlashes = true;
var slashPositions = [];
for (var i = 0; {{{ makeGetValue('path', 'i', 'i8') }}} !== 0; i++) {
@@ -1730,7 +1730,12 @@ LibraryManager.library = {
}
var contents = stream.object.contents;
var size = Math.min(contents.length - offset, nbyte);
- if (contents.subarray || contents.slice) { // typed array or normal array
+#if USE_TYPED_ARRAYS == 2
+ if (contents.subarray) { // typed array
+ HEAPU8.set(contents.subarray(offset, offset+size), buf);
+ } else
+#endif
+ if (contents.slice) { // normal array
for (var i = 0; i < size; i++) {
{{{ makeSetValue('buf', 'i', 'contents[offset + i]', 'i8') }}}
}
@@ -2403,6 +2408,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;
@@ -2451,9 +2457,9 @@ LibraryManager.library = {
_scanString: function(format, get, unget, varargs) {
if (!__scanString.whiteSpace) {
__scanString.whiteSpace = {};
- __scanString.whiteSpace[' '.charCodeAt(0)] = 1;
- __scanString.whiteSpace['\t'.charCodeAt(0)] = 1;
- __scanString.whiteSpace['\n'.charCodeAt(0)] = 1;
+ __scanString.whiteSpace[{{{ charCode(' ') }}}] = 1;
+ __scanString.whiteSpace[{{{ charCode('\t') }}}] = 1;
+ __scanString.whiteSpace[{{{ charCode('\n') }}}] = 1;
__scanString.whiteSpace[' '] = 1;
__scanString.whiteSpace['\t'] = 1;
__scanString.whiteSpace['\n'] = 1;
@@ -2513,8 +2519,8 @@ LibraryManager.library = {
if (format[formatIndex] === '%') {
formatIndex++;
var maxSpecifierStart = formatIndex;
- while (format[formatIndex].charCodeAt(0) >= '0'.charCodeAt(0) &&
- format[formatIndex].charCodeAt(0) <= '9'.charCodeAt(0)) {
+ while (format[formatIndex].charCodeAt(0) >= {{{ charCode('0') }}} &&
+ format[formatIndex].charCodeAt(0) <= {{{ charCode('9') }}}) {
formatIndex++;
}
var max_;
@@ -2560,11 +2566,11 @@ LibraryManager.library = {
while ((curr < max_ || isNaN(max_)) && next > 0) {
if (!(next in __scanString.whiteSpace) && // stop on whitespace
(type == 's' ||
- ((type === 'd' || type == 'u' || type == 'i') && ((next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0)) ||
- (first && next == '-'.charCodeAt(0)))) ||
- (type === 'x' && (next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0) ||
- next >= 'a'.charCodeAt(0) && next <= 'f'.charCodeAt(0) ||
- next >= 'A'.charCodeAt(0) && next <= 'F'.charCodeAt(0)))) &&
+ ((type === 'd' || type == 'u' || type == 'i') && ((next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}}) ||
+ (first && next == {{{ charCode('-') }}}))) ||
+ (type === 'x' && (next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}} ||
+ next >= {{{ charCode('a') }}} && next <= {{{ charCode('f') }}} ||
+ next >= {{{ charCode('A') }}} && next <= {{{ charCode('F') }}}))) &&
(formatIndex >= format.length || next !== format[formatIndex].charCodeAt(0))) { // Stop when we read something that is coming up
buffer.push(String.fromCharCode(next));
next = get();
@@ -2669,7 +2675,7 @@ LibraryManager.library = {
curr = {{{ makeGetValue(0, 'textIndex', 'i8') }}};
if (curr === 0) break;
next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}};
- if (curr == '%'.charCodeAt(0)) {
+ if (curr == {{{ charCode('%') }}}) {
// Handle flags.
var flagAlwaysSigned = false;
var flagLeftAlign = false;
@@ -2677,16 +2683,16 @@ LibraryManager.library = {
var flagZeroPad = false;
flagsLoop: while (1) {
switch (next) {
- case '+'.charCodeAt(0):
+ case {{{ charCode('+') }}}:
flagAlwaysSigned = true;
break;
- case '-'.charCodeAt(0):
+ case {{{ charCode('-') }}}:
flagLeftAlign = true;
break;
- case '#'.charCodeAt(0):
+ case {{{ charCode('#') }}}:
flagAlternative = true;
break;
- case '0'.charCodeAt(0):
+ case {{{ charCode('0') }}}:
if (flagZeroPad) {
break flagsLoop;
} else {
@@ -2702,13 +2708,13 @@ LibraryManager.library = {
// Handle width.
var width = 0;
- if (next == '*'.charCodeAt(0)) {
+ if (next == {{{ charCode('*') }}}) {
width = getNextArg('i32');
textIndex++;
next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}};
} else {
- while (next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0)) {
- width = width * 10 + (next - '0'.charCodeAt(0));
+ while (next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}}) {
+ width = width * 10 + (next - {{{ charCode('0') }}});
textIndex++;
next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}};
}
@@ -2716,20 +2722,20 @@ LibraryManager.library = {
// Handle precision.
var precisionSet = false;
- if (next == '.'.charCodeAt(0)) {
+ if (next == {{{ charCode('.') }}}) {
var precision = 0;
precisionSet = true;
textIndex++;
next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}};
- if (next == '*'.charCodeAt(0)) {
+ if (next == {{{ charCode('*') }}}) {
precision = getNextArg('i32');
textIndex++;
} else {
while(1) {
var precisionChr = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}};
- if (precisionChr < '0'.charCodeAt(0) ||
- precisionChr > '9'.charCodeAt(0)) break;
- precision = precision * 10 + (precisionChr - '0'.charCodeAt(0));
+ if (precisionChr < {{{ charCode('0') }}} ||
+ precisionChr > {{{ charCode('9') }}}) break;
+ precision = precision * 10 + (precisionChr - {{{ charCode('0') }}});
textIndex++;
}
}
@@ -2743,7 +2749,7 @@ LibraryManager.library = {
switch (String.fromCharCode(next)) {
case 'h':
var nextNext = {{{ makeGetValue(0, 'textIndex+2', 'i8') }}};
- if (nextNext == 'h'.charCodeAt(0)) {
+ if (nextNext == {{{ charCode('h') }}}) {
textIndex++;
argSize = 1; // char (actually i32 in varargs)
} else {
@@ -2752,7 +2758,7 @@ LibraryManager.library = {
break;
case 'l':
var nextNext = {{{ makeGetValue(0, 'textIndex+2', 'i8') }}};
- if (nextNext == 'l'.charCodeAt(0)) {
+ if (nextNext == {{{ charCode('l') }}}) {
textIndex++;
argSize = 8; // long long
} else {
@@ -2778,7 +2784,7 @@ LibraryManager.library = {
// Handle type specifier.
if (['d', 'i', 'u', 'o', 'x', 'X', 'p'].indexOf(String.fromCharCode(next)) != -1) {
// Integer.
- var signed = next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0);
+ var signed = next == {{{ charCode('d') }}} || next == {{{ charCode('i') }}};
argSize = argSize || 4;
var currArg = getNextArg('i' + (argSize * 8));
#if PRECISE_I64_MATH
@@ -2788,7 +2794,7 @@ LibraryManager.library = {
#if USE_TYPED_ARRAYS == 2
// Flatten i64-1 [low, high] into a (slightly rounded) double
if (argSize == 8) {
- currArg = Runtime.makeBigInt(currArg[0], currArg[1], next == 'u'.charCodeAt(0));
+ currArg = Runtime.makeBigInt(currArg[0], currArg[1], next == {{{ charCode('u') }}});
}
#endif
// Truncate to requested size.
@@ -2799,20 +2805,20 @@ LibraryManager.library = {
// Format the number.
var currAbsArg = Math.abs(currArg);
var prefix = '';
- if (next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0)) {
+ if (next == {{{ charCode('d') }}} || next == {{{ charCode('i') }}}) {
#if PRECISE_I64_MATH
if (argSize == 8 && i64Math) argText = i64Math.stringify(origArg[0], origArg[1], null); else
#endif
argText = reSign(currArg, 8 * argSize, 1).toString(10);
- } else if (next == 'u'.charCodeAt(0)) {
+ } else if (next == {{{ charCode('u') }}}) {
#if PRECISE_I64_MATH
if (argSize == 8 && i64Math) argText = i64Math.stringify(origArg[0], origArg[1], true); else
#endif
argText = unSign(currArg, 8 * argSize, 1).toString(10);
currArg = Math.abs(currArg);
- } else if (next == 'o'.charCodeAt(0)) {
+ } else if (next == {{{ charCode('o') }}}) {
argText = (flagAlternative ? '0' : '') + currAbsArg.toString(8);
- } else if (next == 'x'.charCodeAt(0) || next == 'X'.charCodeAt(0)) {
+ } else if (next == {{{ charCode('x') }}} || next == {{{ charCode('X') }}}) {
prefix = flagAlternative ? '0x' : '';
#if PRECISE_I64_MATH
if (argSize == 8 && i64Math) argText = (origArg[1]>>>0).toString(16) + (origArg[0]>>>0).toString(16); else
@@ -2830,11 +2836,11 @@ LibraryManager.library = {
} else {
argText = currAbsArg.toString(16);
}
- if (next == 'X'.charCodeAt(0)) {
+ if (next == {{{ charCode('X') }}}) {
prefix = prefix.toUpperCase();
argText = argText.toUpperCase();
}
- } else if (next == 'p'.charCodeAt(0)) {
+ } else if (next == {{{ charCode('p') }}}) {
if (currAbsArg === 0) {
argText = '(nil)';
} else {
@@ -2892,27 +2898,27 @@ LibraryManager.library = {
// Convert g/G to f/F or e/E, as per:
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html
- if (next == 'g'.charCodeAt(0) || next == 'G'.charCodeAt(0)) {
+ if (next == {{{ charCode('g') }}} || next == {{{ charCode('G') }}}) {
isGeneral = true;
precision = precision || 1;
var exponent = parseInt(currArg.toExponential(effectivePrecision).split('e')[1], 10);
if (precision > exponent && exponent >= -4) {
- next = ((next == 'g'.charCodeAt(0)) ? 'f' : 'F').charCodeAt(0);
+ next = ((next == {{{ charCode('g') }}}) ? 'f' : 'F').charCodeAt(0);
precision -= exponent + 1;
} else {
- next = ((next == 'g'.charCodeAt(0)) ? 'e' : 'E').charCodeAt(0);
+ next = ((next == {{{ charCode('g') }}}) ? 'e' : 'E').charCodeAt(0);
precision--;
}
effectivePrecision = Math.min(precision, 20);
}
- if (next == 'e'.charCodeAt(0) || next == 'E'.charCodeAt(0)) {
+ if (next == {{{ charCode('e') }}} || next == {{{ charCode('E') }}}) {
argText = currArg.toExponential(effectivePrecision);
// Make sure the exponent has at least 2 digits.
if (/[eE][-+]\d$/.test(argText)) {
argText = argText.slice(0, -1) + '0' + argText.slice(-1);
}
- } else if (next == 'f'.charCodeAt(0) || next == 'F'.charCodeAt(0)) {
+ } else if (next == {{{ charCode('f') }}} || next == {{{ charCode('F') }}}) {
argText = currArg.toFixed(effectivePrecision);
}
@@ -2932,7 +2938,7 @@ LibraryManager.library = {
argText = parts[0] + (parts.length > 1 ? 'e' + parts[1] : '');
// Capitalize 'E' if needed.
- if (next == 'E'.charCodeAt(0)) argText = argText.toUpperCase();
+ if (next == {{{ charCode('E') }}}) argText = argText.toUpperCase();
// Add sign.
if (flagAlwaysSigned && currArg >= 0) {
@@ -2954,20 +2960,20 @@ LibraryManager.library = {
}
// Adjust case.
- if (next < 'a'.charCodeAt(0)) argText = argText.toUpperCase();
+ if (next < {{{ charCode('a') }}}) argText = argText.toUpperCase();
// Insert the result into the buffer.
argText.split('').forEach(function(chr) {
ret.push(chr.charCodeAt(0));
});
- } else if (next == 's'.charCodeAt(0)) {
+ } else if (next == {{{ charCode('s') }}}) {
// String.
var arg = getNextArg('i8*') || nullString;
var argLength = _strlen(arg);
if (precisionSet) argLength = Math.min(argLength, precision);
if (!flagLeftAlign) {
while (argLength < width--) {
- ret.push(' '.charCodeAt(0));
+ ret.push({{{ charCode(' ') }}});
}
}
for (var i = 0; i < argLength; i++) {
@@ -2975,21 +2981,21 @@ LibraryManager.library = {
}
if (flagLeftAlign) {
while (argLength < width--) {
- ret.push(' '.charCodeAt(0));
+ ret.push({{{ charCode(' ') }}});
}
}
- } else if (next == 'c'.charCodeAt(0)) {
+ } else if (next == {{{ charCode('c') }}}) {
// Character.
if (flagLeftAlign) ret.push(getNextArg('i8'));
while (--width > 0) {
- ret.push(' '.charCodeAt(0));
+ ret.push({{{ charCode(' ') }}});
}
if (!flagLeftAlign) ret.push(getNextArg('i8'));
- } else if (next == 'n'.charCodeAt(0)) {
+ } else if (next == {{{ charCode('n') }}}) {
// Write the length written so far to the next parameter.
var ptr = getNextArg('i32*');
{{{ makeSetValue('ptr', '0', 'ret.length', 'i32') }}}
- } else if (next == '%'.charCodeAt(0)) {
+ } else if (next == {{{ charCode('%') }}}) {
// Literal percent sign.
ret.push(curr);
} else {
@@ -3138,7 +3144,7 @@ LibraryManager.library = {
var streamObj = FS.streams[stream];
if (streamObj.error || streamObj.eof) return 0;
var byte_;
- for (var i = 0; i < n - 1 && byte_ != '\n'.charCodeAt(0); i++) {
+ for (var i = 0; i < n - 1 && byte_ != {{{ charCode('\n') }}}; i++) {
byte_ = _fgetc(stream);
if (byte_ == -1) {
if (streamObj.error) return 0;
@@ -3244,7 +3250,7 @@ LibraryManager.library = {
if (ret < 0) {
return ret;
} else {
- var newlineRet = _fputc('\n'.charCodeAt(0), stdout);
+ var newlineRet = _fputc({{{ charCode('\n') }}}, stdout);
return (newlineRet < 0) ? -1 : ret + 1;
}
},
@@ -3370,8 +3376,8 @@ LibraryManager.library = {
var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}};
if (s) {
_fputs(s, stdout);
- _fputc(':'.charCodeAt(0), stdout);
- _fputc(' '.charCodeAt(0), stdout);
+ _fputc({{{ charCode(':') }}}, stdout);
+ _fputc({{{ charCode(' ') }}}, stdout);
}
var errnum = {{{ makeGetValue('___errno_location()', '0', 'i32') }}};
_puts(_strerror(errnum));
@@ -3669,6 +3675,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) {
@@ -3726,93 +3743,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.
@@ -3820,19 +3750,19 @@ LibraryManager.library = {
// Check for a plus/minus sign.
var multiplier = 1;
- if ({{{ makeGetValue('str', 0, 'i8') }}} == '-'.charCodeAt(0)) {
+ if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('-') }}}) {
multiplier = -1;
str++;
- } else if ({{{ makeGetValue('str', 0, 'i8') }}} == '+'.charCodeAt(0)) {
+ } else if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('+') }}}) {
str++;
}
// Find base.
var finalBase = base;
if (!finalBase) {
- if ({{{ makeGetValue('str', 0, 'i8') }}} == '0'.charCodeAt(0)) {
- if ({{{ makeGetValue('str+1', 0, 'i8') }}} == 'x'.charCodeAt(0) ||
- {{{ makeGetValue('str+1', 0, 'i8') }}} == 'X'.charCodeAt(0)) {
+ if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) {
+ if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} ||
+ {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) {
finalBase = 16;
str += 2;
} else {
@@ -3896,9 +3826,9 @@ LibraryManager.library = {
while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++;
// Check for a plus/minus sign.
- if ({{{ makeGetValue('str', 0, 'i8') }}} == '-'.charCodeAt(0)) {
+ if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('-') }}}) {
str++;
- } else if ({{{ makeGetValue('str', 0, 'i8') }}} == '+'.charCodeAt(0)) {
+ } else if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('+') }}}) {
str++;
}
@@ -3906,9 +3836,9 @@ LibraryManager.library = {
var ok = false;
var finalBase = base;
if (!finalBase) {
- if ({{{ makeGetValue('str', 0, 'i8') }}} == '0'.charCodeAt(0)) {
- if ({{{ makeGetValue('str+1', 0, 'i8') }}} == 'x'.charCodeAt(0) ||
- {{{ makeGetValue('str+1', 0, 'i8') }}} == 'X'.charCodeAt(0)) {
+ if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) {
+ if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} ||
+ {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) {
finalBase = 16;
str += 2;
} else {
@@ -3971,11 +3901,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);
@@ -4235,13 +4160,15 @@ LibraryManager.library = {
memcpy__inline: function (dest, src, num, align) {
var ret = '';
#if ASSERTIONS
+#if ASM_JS == 0
ret += "assert(" + num + " % 1 === 0);"; //, 'memcpy given ' + " + num + " + ' bytes to copy. Problem with quantum=1 corrections perhaps?');";
#endif
+#endif
ret += makeCopyValues(dest, src, num, 'null', null, align);
return ret;
},
- memcpy__asm: 'true',
+ memcpy__asm: true,
memcpy__sig: 'iiii',
memcpy: function (dest, src, num) {
dest = dest|0; src = src|0; num = num|0;
@@ -4314,7 +4241,7 @@ 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;
@@ -4402,14 +4329,18 @@ LibraryManager.library = {
}
},
+ strcpy__asm: true,
+ strcpy__sig: 'iii',
strcpy: function(pdest, psrc) {
+ pdest = pdest|0; psrc = psrc|0;
var i = 0;
do {
- {{{ makeCopyValues('pdest+i', 'psrc+i', 1, 'i8', null, 1) }}};
- i ++;
- } while ({{{ makeGetValue('psrc', 'i-1', 'i8') }}} != 0);
- return pdest;
+ {{{ makeCopyValues('(pdest+i)|0', '(psrc+i)|0', 1, 'i8', null, 1) }}};
+ i = (i+1)|0;
+ } while (({{{ makeGetValue('psrc', 'i-1', 'i8') }}})|0 != 0);
+ return pdest|0;
},
+
stpcpy: function(pdest, psrc) {
var i = 0;
do {
@@ -4419,14 +4350,18 @@ LibraryManager.library = {
return pdest + i - 1;
},
+ strncpy__asm: true,
+ strncpy__sig: 'iiii',
strncpy: function(pdest, psrc, num) {
- var padding = false, curr;
- for (var i = 0; i < num; i++) {
- curr = padding ? 0 : {{{ makeGetValue('psrc', 'i', 'i8') }}};
+ pdest = pdest|0; psrc = psrc|0; num = num|0;
+ var padding = 0, curr = 0, i = 0;
+ while ((i|0) < (num|0)) {
+ curr = padding ? 0 : {{{ makeGetValueAsm('psrc', 'i', 'i8') }}};
{{{ makeSetValue('pdest', 'i', 'curr', 'i8') }}}
- padding = padding || {{{ makeGetValue('psrc', 'i', 'i8') }}} == 0;
+ padding = padding ? 1 : ({{{ makeGetValueAsm('psrc', 'i', 'i8') }}} == 0);
+ i = (i+1)|0;
}
- return pdest;
+ return pdest|0;
},
strlwr__deps:['tolower'],
@@ -4451,15 +4386,18 @@ LibraryManager.library = {
}
},
+ strcat__asm: true,
+ strcat__sig: 'iii',
strcat__deps: ['strlen'],
strcat: function(pdest, psrc) {
- var len = _strlen(pdest);
+ pdest = pdest|0; psrc = psrc|0;
var i = 0;
+ pdest = (pdest + _strlen(pdest))|0;
do {
- {{{ makeCopyValues('pdest+len+i', 'psrc+i', 1, 'i8', null, 1) }}};
- i ++;
- } while ({{{ makeGetValue('psrc', 'i-1', 'i8') }}} != 0);
- return pdest;
+ {{{ makeCopyValues('pdest+i', 'psrc+i', 1, 'i8', null, 1) }}};
+ i = (i+1)|0;
+ } while ({{{ makeGetValueAsm('psrc', 'i-1', 'i8') }}} != 0);
+ return pdest|0;
},
strncat__deps: ['strlen'],
@@ -4485,9 +4423,12 @@ LibraryManager.library = {
// We always assume ASCII locale.
strcoll: 'strcmp',
+ strcasecmp__asm: true,
+ strcasecmp__sig: 'iii',
strcasecmp__deps: ['strncasecmp'],
strcasecmp: function(px, py) {
- return _strncasecmp(px, py, TOTAL_MEMORY);
+ px = px|0; py = py|0;
+ return _strncasecmp(px, py, -1)|0;
},
strncmp: function(px, py, n) {
@@ -4508,26 +4449,29 @@ LibraryManager.library = {
return 0;
},
+ strncasecmp__asm: true,
+ strncasecmp__sig: 'iiii',
strncasecmp__deps: ['tolower'],
strncasecmp: function(px, py, n) {
- var i = 0;
- while (i < n) {
- var x = _tolower({{{ makeGetValue('px', 'i', 'i8', 0, 1) }}});
- var y = _tolower({{{ makeGetValue('py', 'i', 'i8', 0, 1) }}});
- if (x == y && x == 0) return 0;
- if (x == 0) return -1;
- if (y == 0) return 1;
- if (x == y) {
- i ++;
+ px = px|0; py = py|0; n = n|0;
+ var i = 0, x = 0, y = 0;
+ while ((i>>>0) < (n>>>0)) {
+ x = _tolower({{{ makeGetValueAsm('px', 'i', 'i8', 0, 1) }}});
+ y = _tolower({{{ makeGetValueAsm('py', 'i', 'i8', 0, 1) }}});
+ if (((x|0) == (y|0)) & ((x|0) == 0)) return 0;
+ if ((x|0) == 0) return -1;
+ if ((y|0) == 0) return 1;
+ if ((x|0) == (y|0)) {
+ i = (i + 1)|0;
continue;
} else {
- return x > y ? 1 : -1;
+ return ((x>>>0) > (y>>>0) ? 1 : -1)|0;
}
}
return 0;
},
- memcmp__asm: 'true',
+ memcmp__asm: true,
memcmp__sig: 'iiii',
memcmp: function(p1, p2, num) {
p1 = p1|0; p2 = p2|0; num = num|0;
@@ -4729,58 +4673,61 @@ LibraryManager.library = {
return chr & 0x7F;
},
toupper: function(chr) {
- if (chr >= 'a'.charCodeAt(0) && chr <= 'z'.charCodeAt(0)) {
- return chr - 'a'.charCodeAt(0) + 'A'.charCodeAt(0);
+ if (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) {
+ return chr - {{{ charCode('a') }}} + {{{ charCode('A') }}};
} else {
return chr;
}
},
_toupper: 'toupper',
+
+ tolower__asm: true,
+ tolower__sig: 'ii',
tolower: function(chr) {
- if (chr >= 'A'.charCodeAt(0) && chr <= 'Z'.charCodeAt(0)) {
- return chr - 'A'.charCodeAt(0) + 'a'.charCodeAt(0);
- } else {
- return chr;
- }
+ chr = chr|0;
+ if ((chr|0) < {{{ charCode('A') }}}) return chr|0;
+ if ((chr|0) > {{{ charCode('Z') }}}) return chr|0;
+ return (chr - {{{ charCode('A') }}} + {{{ charCode('a') }}})|0;
},
_tolower: 'tolower',
+
// The following functions are defined as macros in glibc.
islower: function(chr) {
- return chr >= 'a'.charCodeAt(0) && chr <= 'z'.charCodeAt(0);
+ return chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}};
},
isupper: function(chr) {
- return chr >= 'A'.charCodeAt(0) && chr <= 'Z'.charCodeAt(0);
+ return chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}};
},
isalpha: function(chr) {
- return (chr >= 'a'.charCodeAt(0) && chr <= 'z'.charCodeAt(0)) ||
- (chr >= 'A'.charCodeAt(0) && chr <= 'Z'.charCodeAt(0));
+ return (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) ||
+ (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}});
},
isdigit: function(chr) {
- return chr >= '0'.charCodeAt(0) && chr <= '9'.charCodeAt(0);
+ return chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}};
},
isdigit_l: 'isdigit', // no locale support yet
isxdigit: function(chr) {
- return (chr >= '0'.charCodeAt(0) && chr <= '9'.charCodeAt(0)) ||
- (chr >= 'a'.charCodeAt(0) && chr <= 'f'.charCodeAt(0)) ||
- (chr >= 'A'.charCodeAt(0) && chr <= 'F'.charCodeAt(0));
+ return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) ||
+ (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('f') }}}) ||
+ (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('F') }}});
},
isxdigit_l: 'isxdigit', // no locale support yet
isalnum: function(chr) {
- return (chr >= '0'.charCodeAt(0) && chr <= '9'.charCodeAt(0)) ||
- (chr >= 'a'.charCodeAt(0) && chr <= 'z'.charCodeAt(0)) ||
- (chr >= 'A'.charCodeAt(0) && chr <= 'Z'.charCodeAt(0));
+ return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) ||
+ (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) ||
+ (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}});
},
ispunct: function(chr) {
- return (chr >= '!'.charCodeAt(0) && chr <= '/'.charCodeAt(0)) ||
- (chr >= ':'.charCodeAt(0) && chr <= '@'.charCodeAt(0)) ||
- (chr >= '['.charCodeAt(0) && chr <= '`'.charCodeAt(0)) ||
- (chr >= '{'.charCodeAt(0) && chr <= '~'.charCodeAt(0));
+ return (chr >= {{{ charCode('!') }}} && chr <= {{{ charCode('/') }}}) ||
+ (chr >= {{{ charCode(':') }}} && chr <= {{{ charCode('@') }}}) ||
+ (chr >= {{{ charCode('[') }}} && chr <= {{{ charCode('`') }}}) ||
+ (chr >= {{{ charCode('{') }}} && chr <= {{{ charCode('~') }}});
},
isspace: function(chr) {
return chr in { 32: 0, 9: 0, 10: 0, 11: 0, 12: 0, 13: 0 };
},
isblank: function(chr) {
- return chr == ' '.charCodeAt(0) || chr == '\t'.charCodeAt(0);
+ return chr == {{{ charCode(' ') }}} || chr == {{{ charCode('\t') }}};
},
iscntrl: function(chr) {
return (0 <= chr && chr <= 0x1F) || chr === 0x7F;
@@ -4976,6 +4923,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;
},
@@ -5270,13 +5231,6 @@ LibraryManager.library = {
{{{ makeStructuralReturn(['(x*y)>>>0', 'x*y > 4294967295']) }}};
},
- llvm_uadd_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }],
- llvm_uadd_with_overflow_i64: function(xl, xh, yl, yh) {
- i64Math.add(xl, xh, yl, yh);
- {{{ makeStructuralReturn([makeGetTempDouble(0, 'i32'), makeGetTempDouble(1, 'i32'), '0']) }}};
- // XXX Need to hack support for second param in long.js
- },
-
llvm_umul_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }],
llvm_umul_with_overflow_i64: function(xl, xh, yl, yh) {
i64Math.multiply(xl, xh, yl, yh);
@@ -5506,6 +5460,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);
},
@@ -6064,6 +6026,15 @@ LibraryManager.library = {
__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) {
@@ -6170,7 +6141,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) {
@@ -6693,6 +6664,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!)
// ==========================================================================
@@ -6708,8 +6686,15 @@ LibraryManager.library = {
},
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;
@@ -7385,6 +7370,81 @@ LibraryManager.library = {
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
},
+
+ //============================
+ // i64 math
+ //============================
+
+ i64Add__asm: true,
+ i64Add__sig: 'iiiii',
+ i64Add: function(a, b, c, d) {
+ /*
+ x = a + b*2^32
+ y = c + d*2^32
+ result = l + h*2^32
+ */
+ a = a|0; b = b|0; c = c|0; d = d|0;
+ var l = 0, h = 0;
+ l = (a + c)>>>0;
+ h = (b + d)>>>0;
+ if ((l>>>0) < (a>>>0)) { // iff we overflowed
+ h = (h+1)>>>0;
+ }
+ {{{ makeStructuralReturn(['l|0', 'h'], true) }}};
+ },
+ llvm_uadd_with_overflow_i64__asm: true,
+ llvm_uadd_with_overflow_i64__sig: 'iiiii',
+ llvm_uadd_with_overflow_i64: function(a, b, c, d) {
+ a = a|0; b = b|0; c = c|0; d = d|0;
+ var l = 0, h = 0, overflow = 0;
+ l = (a + c)>>>0;
+ h = (b + d)>>>0;
+ if ((l>>>0) < (a>>>0)) { // iff we overflowed
+ h = (h+1)>>>0;
+ overflow = 1;
+ }
+ {{{ makeStructuralReturn(['l|0', 'h', 'overflow'], true) }}};
+ },
+
+ bitshift64Shl__asm: true,
+ bitshift64Shl__sig: 'iiii',
+ bitshift64Shl: function(low, high, bits) {
+ low = low|0; high = high|0; bits = bits|0;
+ var ander = 0;
+ if ((bits|0) < 32) {
+ ander = ((1 << bits) - 1)|0;
+ tempRet0 = (high << bits) | ((low&(ander << (32 - bits))) >>> (32 - bits));
+ return low << bits;
+ }
+ tempRet0 = low << (bits - 32);
+ return 0;
+ },
+ bitshift64Ashr__asm: true,
+ bitshift64Ashr__sig: 'iiii',
+ bitshift64Ashr: function(low, high, bits) {
+ low = low|0; high = high|0; bits = bits|0;
+ var ander = 0;
+ if ((bits|0) < 32) {
+ ander = ((1 << bits) - 1)|0;
+ tempRet0 = high >> bits;
+ return (low >>> bits) | ((high&ander) << (32 - bits));
+ }
+ tempRet0 = (high|0) < 0 ? -1 : 0;
+ return (high >> (bits - 32))|0;
+ },
+ bitshift64Lshr__asm: true,
+ bitshift64Lshr__sig: 'iiii',
+ bitshift64Lshr: function(low, high, bits) {
+ low = low|0; high = high|0; bits = bits|0;
+ var ander = 0;
+ if ((bits|0) < 32) {
+ ander = ((1 << bits) - 1)|0;
+ tempRet0 = high >>> bits;
+ return (low >>> bits) | ((high&ander) << (32 - bits));
+ }
+ tempRet0 = 0;
+ return (high >>> (bits - 32))|0;
+ },
};
function autoAddDeps(object, name) {
@@ -7396,3 +7456,4 @@ function autoAddDeps(object, name) {
}
}
+
diff --git a/src/library_browser.js b/src/library_browser.js
index 5b19a360..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: [],
@@ -205,10 +206,10 @@ mergeInto(LibraryManager.library, {
try {
if (useWebGL) {
ctx = canvas.getContext('experimental-webgl', {
- alpha: false,
#if GL_TESTING
- preserveDrawingBuffer: true
+ preserveDrawingBuffer: true,
#endif
+ alpha: false
});
} else {
ctx = canvas.getContext('2d');
@@ -273,36 +274,60 @@ mergeInto(LibraryManager.library, {
}
return ctx;
},
+
destroyContext: function(canvas, useWebGL, setInModule) {},
- requestFullScreen: function() {
+
+ 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'] ||
@@ -380,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_gl.js b/src/library_gl.js
index 4977d2e9..9e12e4ee 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -21,7 +21,6 @@ var LibraryGL = {
#if FULL_ES2
clientBuffers: [],
- enabledClientBuffers: [],
#endif
currArrayBuffer: 0,
currElementArrayBuffer: 0,
@@ -59,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) {
@@ -207,20 +265,35 @@ var LibraryGL = {
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) {
- if (!GL.enabledClientBuffers[i] || !GL.clientBuffers[i]) continue;
-
- GL.resetBufferBinding = true;
-
var cb = GL.clientBuffers[i];
-
- var buf = Module.ctx.createBuffer();
+ if (!cb.clientside || !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 + GL.calcBufLength(cb.size, cb.type, cb.stride, count)),
- Module.ctx.DYNAMIC_DRAW);
+ Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER,
+ 0,
+ HEAPU8.subarray(cb.ptr, cb.ptr + size));
Module.ctx.vertexAttribPointer(i, cb.size, cb.type, cb.normalized, cb.stride, 0);
}
},
@@ -239,6 +312,13 @@ var LibraryGL = {
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, clientside: 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') ||
@@ -612,7 +692,7 @@ var LibraryGL = {
glGetVertexAttribfv: function(index, pname, params) {
#if FULL_ES2
- if (GL.clientBuffers[index]) {
+ if (GL.clientBuffers[index].enabled) {
Module.printErr("glGetVertexAttribfv on client-side array: not supported, bad data returned");
}
#endif
@@ -628,7 +708,7 @@ var LibraryGL = {
glGetVertexAttribiv: function(index, pname, params) {
#if FULL_ES2
- if (GL.clientBuffers[index]) {
+ if (GL.clientBuffers[index].enabled) {
Module.printErr("glGetVertexAttribiv on client-side array: not supported, bad data returned");
}
#endif
@@ -644,7 +724,7 @@ var LibraryGL = {
glGetVertexAttribPointerv: function(index, pname, pointer) {
#if FULL_ES2
- if (GL.clientBuffers[index]) {
+ if (GL.clientBuffers[index].enabled) {
Module.printErr("glGetVertexAttribPointer on client-side array: not supported, bad data returned");
}
#endif
@@ -1693,62 +1773,6 @@ var LibraryGL = {
this.modifiedClientAttributes = true;
},
- // Temporary buffers
- MAX_TEMP_BUFFER_SIZE: {{{ GL_MAX_TEMP_BUFFER_SIZE }}},
- 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]) {
@@ -1969,8 +1993,8 @@ 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;
@@ -2161,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]);
},
@@ -2209,7 +2233,7 @@ var LibraryGL = {
#if ASSERTIONS
Runtime.warnOnce('Unpacking/restriding attributes, this is not fast');
#endif
- if (!GL.immediate.restrideBuffer) GL.immediate.restrideBuffer = _malloc(GL.immediate.MAX_TEMP_BUFFER_SIZE);
+ if (!GL.immediate.restrideBuffer) GL.immediate.restrideBuffer = _malloc(GL.MAX_TEMP_BUFFER_SIZE);
start = GL.immediate.restrideBuffer;
#if ASSERTIONS
assert(start % 4 == 0);
@@ -2224,7 +2248,7 @@ var LibraryGL = {
bytes += size;
}
#if ASSERTIONS
- assert(count*bytes <= GL.immediate.MAX_TEMP_BUFFER_SIZE);
+ 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++) {
@@ -2298,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;
@@ -2314,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;
}
@@ -2369,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);
},
@@ -2849,15 +2873,25 @@ var LibraryGL = {
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) {
- GL.clientBuffers[index] = { size: size, type: type, normalized: normalized, stride: stride, ptr: ptr };
+ cb.size = size;
+ cb.type = type;
+ cb.normalized = normalized;
+ cb.stride = stride;
+ cb.ptr = ptr;
+ cb.clientside = true;
return;
}
-
- GL.clientBuffers[index] = null;
+ cb.clientside = false;
#endif
Module.ctx.vertexAttribPointer(index, size, type, normalized, stride, ptr);
},
@@ -2865,7 +2899,11 @@ var LibraryGL = {
glEnableVertexAttribArray__sig: 'vi',
glEnableVertexAttribArray: function(index) {
#if FULL_ES2
- GL.enabledClientBuffers[index] = true;
+ var cb = GL.clientBuffers[index];
+#if ASSERTIONS
+ assert(cb, index);
+#endif
+ cb.enabled = true;
#endif
Module.ctx.enableVertexAttribArray(index);
},
@@ -2873,7 +2911,11 @@ var LibraryGL = {
glDisableVertexAttribArray__sig: 'vi',
glDisableVertexAttribArray: function(index) {
#if FULL_ES2
- GL.enabledClientBuffers[index] = false;
+ var cb = GL.clientBuffers[index];
+#if ASSERTIONS
+ assert(cb, index);
+#endif
+ cb.enabled = false;
#endif
Module.ctx.disableVertexAttribArray(index);
},
@@ -2881,7 +2923,7 @@ var LibraryGL = {
glDrawArrays: function(mode, first, count) {
#if FULL_ES2
// bind any client-side buffers
- GL.preDrawHandleClientVertexAttribBindings(count);
+ GL.preDrawHandleClientVertexAttribBindings(first + count);
#endif
Module.ctx.drawArrays(mode, first, count);
@@ -2895,11 +2937,12 @@ var LibraryGL = {
#if FULL_ES2
var buf;
if (!GL.currElementArrayBuffer) {
- buf = Module.ctx.createBuffer();
+ 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 + GL.calcBufLength(1, type, 0, count)),
- Module.ctx.DYNAMIC_DRAW);
+ Module.ctx.bufferSubData(Module.ctx.ELEMENT_ARRAY_BUFFER,
+ 0,
+ HEAPU8.subarray(indices, indices + size));
// the index is now 0
indices = 0;
}
@@ -2915,17 +2958,18 @@ var LibraryGL = {
if (!GL.currElementArrayBuffer) {
Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null);
- Module.ctx.deleteBuffer(buf);
}
#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',
diff --git a/src/library_openal.js b/src/library_openal.js
new file mode 100644
index 00000000..5511ccb7
--- /dev/null
+++ b/src/library_openal.js
@@ -0,0 +1,471 @@
+//"use strict";
+
+var LibraryOpenAL = {
+ $AL__deps: ['$Browser'],
+ $AL: {
+ contexts: [],
+ currentContext: null,
+ },
+
+ alcProcessContext: function(context) {},
+ alcSuspendContext: function(context) {},
+
+ alcMakeContextCurrent: function(context) {
+ if (context == 0) {
+ AL.currentContext = null;
+ } else {
+ AL.currentContext = AL.contexts[context - 1];
+ }
+ },
+
+ alcDestroyContext: function(context) {
+ // Stop playback, etc
+ },
+
+ alcCloseDevice: function(device) {
+ // Stop playback, etc
+ },
+
+ alcOpenDevice: function(deviceName) {
+ if (typeof(AudioContext) == "function" ||
+ typeof(webkitAudioContext) == "function") {
+ return 1; // non-null pointer -- we just simulate one device
+ } else {
+ return 0;
+ }
+ },
+
+ alcCreateContext: function(device, attrList) {
+ if (device != 1) {
+ return 0;
+ }
+
+ if (attrList) {
+ console.log("The attrList argument of alcCreateContext is not supported yet");
+ return 0;
+ }
+
+ var ctx;
+ try {
+ ctx = new AudioContext();
+ } catch (e) {
+ try {
+ ctx = new webkitAudioContext();
+ } catch (e) {}
+ }
+
+ if (ctx) {
+ AL.contexts.push({ctx: ctx, err: 0, src: [], buf: []});
+ return AL.contexts.length;
+ } else {
+ return 0;
+ }
+ },
+
+ alGetError: function() {
+ if (!AL.currentContext) {
+ return 0xA004 /* AL_INVALID_OPERATION */;
+ } else {
+ return AL.currentContext.err;
+ }
+ },
+
+ alDeleteSources: function(count, sources)
+ {
+ if (!AL.currentContext) {
+ console.error("alDeleteSources called without a valid context");
+ return;
+ }
+ for (var i = 0; i < count; ++i) {
+ var sourceIdx = {{{ makeGetValue('sources', 'i', 'i32') }}} - 1;
+ delete AL.currentContext.src[sourceIdx];
+ }
+ },
+
+ alGenSources: function(count, sources) {
+ if (!AL.currentContext) {
+ console.error("alGenSources called without a valid context");
+ return;
+ }
+ for (var i = 0; i < count; ++i) {
+ var gain = AL.currentContext.ctx.createGain();
+ var panner = AL.currentContext.ctx.createPanner();
+ if (typeof(webkitAudioContext) == 'function') {
+ gain.connect(panner);
+ panner.connect(AL.currentContext.ctx.destination);
+ } else {
+ // Work around a Firefox bug (bug 849916)
+ gain.connect(AL.currentContext.ctx.destination);
+ }
+ gain.gain.value = 1; // work around a Firefox bug (bug 850970)
+ AL.currentContext.src.push({
+ loop: false,
+ buffer: null,
+ gain: gain,
+ panner: panner,
+ paused: false,
+ playTime: -1,
+ pausedTime: 0
+ });
+ {{{ makeSetValue('sources', 'i', 'AL.currentContext.src.length', 'i32') }}};
+ }
+ },
+
+ alSourcei: function(source, param, value) {
+ if (!AL.currentContext) {
+ console.error("alSourcei called without a valid context");
+ return;
+ }
+ if (source > AL.currentContext.src.length) {
+ console.error("alSourcei called with an invalid source");
+ return;
+ }
+ switch (param) {
+ case 0x1007 /* AL_LOOPING */:
+ AL.currentContext.src[source - 1].loop = (value != 0 /* AL_FALSE */);
+ break;
+ case 0x1009 /* AL_BUFFER */:
+ if (value == 0) {
+ AL.currentContext.src[source - 1].buffer = null;
+ } else {
+ AL.currentContext.src[source - 1].buffer = AL.currentContext.buf[value - 1].buf;
+ }
+ break;
+ default:
+ console.log("alSourcei with param " + param + " not implemented yet");
+ break;
+ }
+ },
+
+ alSourcef: function(source, param, value) {
+ if (!AL.currentContext) {
+ consoue.error("alSourcef called without a valid context");
+ return;
+ }
+ if (source > AL.currentContext.src.length) {
+ console.error("alSourcef called with an invalid source");
+ return;
+ }
+ switch (param) {
+ case 0x100A /* AL_GAIN */:
+ AL.currentContext.src[source - 1].gain.gain.value = value;
+ break;
+ case 0x1003 /* AL_PITCH */:
+ console.log("alSourcef was called with AL_PITCH, but Web Audio does not support static pitch changes");
+ break;
+ default:
+ console.log("alSourcef with param " + param + " not implemented yet");
+ break;
+ }
+ },
+
+ alSourcefv: function(source, param, value) {
+ if (!AL.currentContext) {
+ consoue.error("alSourcefv called without a valid context");
+ return;
+ }
+ if (source > AL.currentContext.src.length) {
+ console.error("alSourcefv called with an invalid source");
+ return;
+ }
+ switch (param) {
+ case 0x1004 /* AL_POSITION */:
+ AL.currentContext.src[source - 1].panner.setPosition(
+ {{{ makeGetValue('value', '0', 'float') }}},
+ {{{ makeGetValue('value', '1', 'float') }}},
+ {{{ makeGetValue('value', '2', 'float') }}}
+ );
+ break;
+ case 0x1006 /* AL_VELOCITY */:
+ AL.currentContext.src[source - 1].panner.setVelocity(
+ {{{ makeGetValue('value', '0', 'float') }}},
+ {{{ makeGetValue('value', '1', 'float') }}},
+ {{{ makeGetValue('value', '2', 'float') }}}
+ );
+ break;
+ default:
+ console.log("alSourcefv with param " + param + " not implemented yet");
+ break;
+ }
+ },
+
+ alSourceQueueBuffers: function(source, count, buffers) {
+ if (!AL.currentContext) {
+ console.error("alSourceQueueBuffers called without a valid context");
+ return;
+ }
+ if (source > AL.currentContext.src.length) {
+ console.error("alSourceQueueBuffers called with an invalid source");
+ return;
+ }
+ if (count != 1) {
+ console.error("Queuing multiple buffers using alSourceQueueBuffers is not supported yet");
+ return;
+ }
+ for (var i = 0; i < count; ++i) {
+ var buffer = {{{ makeGetValue('buffers', 'i', 'i32') }}};
+ if (buffer > AL.currentContext.buf.length) {
+ console.error("alSourceQueueBuffers called with an invalid buffer");
+ return;
+ }
+ AL.currentContext.src[source - 1].buffer = AL.currentContext.buf[buffer - 1].buf;
+ }
+ },
+
+ alSourceUnqueueBuffers: function(source, count, buffers)
+ {
+ if (!AL.currentContext) {
+ console.error("alSourceUnqueueBuffers called without a valid context");
+ return;
+ }
+ if (source > AL.currentContext.src.length) {
+ console.error("alSourceUnqueueBuffers called with an invalid source");
+ return;
+ }
+ if (count != 1) {
+ console.error("Queuing multiple buffers using alSourceUnqueueBuffers is not supported yet");
+ return;
+ }
+ for (var i = 0; i < count; ++i) {
+ var buffer = AL.currentContext.src[source - 1].buffer;
+ for (var j = 0; j < AL.currentContext.buf.length; ++j) {
+ if (buffer == AL.currentContext.buf[j].buf) {
+ {{{ makeSetValue('buffers', 'i', 'j+1', 'i32') }}};
+ AL.currentContext.src[source - 1].buffer = null;
+ break;
+ }
+ }
+ }
+ },
+
+ alDeleteBuffers: function(count, buffers)
+ {
+ if (!AL.currentContext) {
+ console.error("alDeleteBuffers called without a valid context");
+ return;
+ }
+ for (var i = 0; i < count; ++i) {
+ var bufferIdx = {{{ makeGetValue('buffers', 'i', 'i32') }}} - 1;
+ var buffer = AL.currentContext.buf[bufferIdx].buf;
+ for (var j = 0; j < AL.currentContext.src.length; ++j) {
+ if (buffer == AL.currentContext.src[j].buffer) {
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
+ return;
+ }
+ }
+ delete AL.currentContext.buf[bufferIdx];
+ }
+ },
+
+ alGenBuffers: function(count, buffers) {
+ if (!AL.currentContext) {
+ console.error("alGenBuffers called without a valid context");
+ return;
+ }
+ for (var i = 0; i < count; ++i) {
+ AL.currentContext.buf.push({buf: null});
+ {{{ makeSetValue('buffers', 'i', 'AL.currentContext.buf.length', 'i32') }}};
+ }
+ },
+
+ alBufferData: function(buffer, format, data, size, freq) {
+ if (!AL.currentContext) {
+ console.error("alBufferData called without a valid context");
+ return;
+ }
+ if (buffer > AL.currentContext.buf.length) {
+ console.error("alBufferData called with an invalid buffer");
+ return;
+ }
+ var channels, bytes;
+ switch (format) {
+ case 0x1100 /* AL_FORMAT_MONO8 */:
+ bytes = 1;
+ channels = 1;
+ break;
+ case 0x1101 /* AL_FORMAT_MONO16 */:
+ bytes = 2;
+ channels = 1;
+ break;
+ case 0x1102 /* AL_FORMAT_STEREO8 */:
+ bytes = 1;
+ channels = 2;
+ break;
+ case 0x1103 /* AL_FORMAT_STEREO16 */:
+ bytes = 2;
+ channels = 2;
+ break;
+ default:
+ console.error("alBufferData called with invalid format " + format);
+ return;
+ }
+ AL.currentContext.buf[buffer - 1].buf = AL.currentContext.ctx.createBuffer(channels, size / (bytes * channels), freq);
+ var buf = new Array(channels);
+ for (var i = 0; i < channels; ++i) {
+ buf[i] = AL.currentContext.buf[buffer - 1].buf.getChannelData(i);
+ }
+ for (var i = 0; i < size / (bytes * channels); ++i) {
+ for (var j = 0; j < channels; ++j) {
+ switch (bytes) {
+ case 1:
+ var val = {{{ makeGetValue('data', 'i*channels+j', 'i8') }}};
+ buf[j][i] = -1.0 + val * (2/256);
+ break;
+ case 2:
+ var val = {{{ makeGetValue('data', '2*(i*channels+j)', 'i16') }}};
+ buf[j][i] = val/32768;
+ break;
+ }
+ }
+ }
+ },
+
+ alSourcePlay__deps: ["alSourceStop"],
+ alSourcePlay: function(source) {
+ if (!AL.currentContext) {
+ console.error("alSourcePlay called without a valid context");
+ return;
+ }
+ if (source > AL.currentContext.src.length) {
+ console.error("alSourcePlay called with an invalid source");
+ return;
+ }
+ var offset = 0;
+ if ("src" in AL.currentContext.src[source - 1]) {
+ // If the source is already playing, we need to resume from beginning.
+ // We do that by stopping the current source and replaying it.
+ _alSourceStop(source);
+ } else if (AL.currentContext.src[source - 1].paused) {
+ // So now we have to resume playback, remember the offset here.
+ offset = AL.currentContext.src[source - 1].pausedTime -
+ AL.currentContext.src[source - 1].playTime;
+ }
+ var src = AL.currentContext.ctx.createBufferSource();
+ src.loop = AL.currentContext.src[source - 1].loop;
+ src.buffer = AL.currentContext.src[source - 1].buffer;
+ src.connect(AL.currentContext.src[source - 1].gain);
+ src.start(0, offset);
+ // Work around Firefox bug 851338
+ AL.currentContext.src[source - 1].playTime = AL.currentContext.ctx.currentTime || 0;
+ AL.currentContext.src[source - 1].paused = false;
+ AL.currentContext.src[source - 1]['src'] = src;
+ },
+
+ alSourceStop: function(source) {
+ if (!AL.currentContext) {
+ console.error("alSourceStop called without a valid context");
+ return;
+ }
+ if (source > AL.currentContext.src.length) {
+ console.error("alSourceStop called with an invalid source");
+ return;
+ }
+ if ("src" in AL.currentContext.src[source - 1]) {
+ AL.currentContext.src[source - 1]["src"].stop(0);
+ delete AL.currentContext.src[source - 1]["src"];
+ }
+ },
+
+ alSourcePause: function(source) {
+ if (!AL.currentContext) {
+ console.error("alSourcePause called without a valid context");
+ return;
+ }
+ if (source > AL.currentContext.src.length) {
+ console.error("alSourcePause called with an invalid source");
+ return;
+ }
+ if ("src" in AL.currentContext.src[source - 1] &&
+ !AL.currentContext.src[source - 1].paused) {
+ AL.currentContext.src[source - 1].paused = true;
+ // Work around Firefox bug 851338
+ AL.currentContext.src[source - 1].pausedTime = AL.currentContext.ctx.currentTime || 0;
+ AL.currentContext.src[source - 1]["src"].stop(0);
+ delete AL.currentContext.src[source - 1].src;
+ }
+ },
+
+ alGetSourcei: function(source, param, value) {
+ if (!AL.currentContext) {
+ console.error("alGetSourcei called without a valid context");
+ return;
+ }
+ if (source > AL.currentContext.src.length) {
+ console.error("alGetSourcei called with an invalid source");
+ return;
+ }
+ switch (param) {
+ case 0x202 /* AL_SOURCE_RELATIVE */:
+ // Always return 1
+ {{{ makeSetValue('value', '0', '1', 'i32') }}};
+ break;
+ case 0x1009 /* AL_BUFFER */:
+ if (AL.currentContext.src[source - 1].buffer == null) {
+ {{{ makeSetValue('value', '0', '0', 'i32') }}};
+ } else {
+ var buf = AL.currentContext.src[source - 1].buffer;
+ for (var i = 0; i < AL.currentContext.buf.length; ++i) {
+ if (buf == AL.currentContext.buf[i].buf) {
+ {{{ makeSetValue('value', '0', 'i+1', 'i32') }}};
+ return;
+ }
+ }
+ {{{ makeSetValue('value', '0', '0', 'i32') }}};
+ }
+ break;
+ case 0x1010 /* AL_SOURCE_STATE */:
+ if ("src" in AL.currentContext.src[source - 1]) {
+ {{{ makeSetValue('value', '0', '0x1012', 'i32') }}} /* AL_PLAYING */;
+ } else if (AL.currentContext.src[source - 1].paused) {
+ {{{ makeSetValue('value', '0', '0x1013', 'i32') }}} /* AL_PAUSED */;
+ } else if (AL.currentContext.src[source - 1].playTime == -1) {
+ {{{ makeSetValue('value', '0', '0x1011', 'i32') }}} /* AL_INITIAL */;
+ } else {
+ {{{ makeSetValue('value', '0', '0x1014', 'i32') }}} /* AL_STOPPED */;
+ }
+ break;
+ case 0x1015 /* AL_BUFFERS_QUEUED */:
+ if (AL.currentContext.src[source - 1].buffer) {
+ {{{ makeSetValue('value', '0', '1', 'i32') }}}
+ } else {
+ {{{ makeSetValue('value', '0', '0', 'i32') }}}
+ }
+ break;
+ case 0x1016 /* AL_BUFFERS_PROCESSED */:
+ // Always return 1
+ {{{ makeSetValue('value', '0', '1', 'i32') }}}
+ break;
+ }
+ },
+
+ alDistanceModel: function(model) {
+ if (model != 0 /* AL_NONE */) {
+ console.log("Only alDistanceModel(AL_NONE) is currently supported");
+ }
+ },
+
+ alListenerfv: function(param, values) {
+ console.log("alListenerfv is not supported yet");
+ },
+
+ alIsExtensionPresent: function(extName) {
+ return 0;
+ },
+
+ alcIsExtensionPresent: function(device, extName) {
+ return 0;
+ },
+
+ alGetProcAddress: function(fname) {
+ return 0;
+ },
+
+ alcGetProcAddress: function(device, fname) {
+ return 0;
+ },
+
+};
+
+autoAddDeps(LibraryOpenAL, '$AL');
+mergeInto(LibraryManager.library, LibraryOpenAL);
+
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 96ae6fa2..9fc979d2 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -35,6 +35,7 @@ var LibrarySDL = {
mixerFormat: 0x8010, // AUDIO_S16LSB
mixerNumChannels: 2,
mixerChunkSize: 1024,
+ channelMinimumNumber: 0,
GL: false, // Set to true if we call SDL_SetVideoMode with SDL_OPENGL, and if so, we do not create 2D canvases&contexts for blitting
// Note that images loaded before SDL_SetVideoMode will not get this optimization
@@ -661,8 +662,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;
},
@@ -919,7 +920,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() {
@@ -1075,6 +1095,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
@@ -1289,7 +1318,9 @@ var LibrarySDL = {
Mix_FreeChunk: function(id) {
SDL.audios[id] = null;
},
-
+ Mix_ReserveChannels: function(num) {
+ SDL.channelMinimumNumber = num;
+ },
Mix_PlayChannel: function(channel, id, loops) {
// TODO: handle loops
@@ -1302,8 +1333,8 @@ var LibrarySDL = {
// If the user asks us to allocate a channel automatically, get the first
// free one.
if (channel == -1) {
- channel = 0;
- for (var i = 0; i < SDL.numChannels; i++) {
+ channel = SDL.channelMinimumNumber;
+ for (var i = SDL.channelMinimumNumber; i < SDL.numChannels; i++) {
if (!SDL.channels[i].audio) {
channel = i;
break;
@@ -1375,6 +1406,7 @@ var LibrarySDL = {
audio.play();
}
audio.volume = channelInfo.volume;
+ audio.paused = false;
return channel;
},
Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing
@@ -1463,10 +1495,71 @@ 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(channel) {
+ if (channel === -1) {
+ var count = 0;
+ for (var i = 0; i < SDL.channels.length; i++) {
+ count += _Mix_Playing(i);
+ }
+ return count;
+ }
+ var info = SDL.channels[channel];
+ if (info && info.audio && !info.audio.paused) {
+ return 1;
+ }
+ return 0;
+ },
+
+ Mix_Pause: function(channel) {
+ if (channel === -1) {
+ for (var i = 0; i<SDL.channels.length;i++) {
+ _Mix_Pause(i);
+ }
+ return;
+ }
+ var info = SDL.channels[channel];
+ if (info && info.audio) {
+ info.audio.pause();
+ info.audio.paused = true;
+ }
+ },
+
+ // http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_39.html#SEC39
+ Mix_Paused: function(channel) {
+ if (channel === -1) {
+ var pausedCount = 0;
+ for (var i = 0; i<SDL.channels.length;i++) {
+ pausedCount += _Mix_Paused(i);
+ }
+ return pausedCount;
+ }
+ var info = SDL.channels[channel];
+ 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(channel) {
+ if (channel === -1) {
+ for (var i = 0; i<SDL.channels.length;i++) {
+ _Mix_Resume(i);
+ }
+ return;
+ }
+ var info = SDL.channels[channel];
+ 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..6d0e873d 100644
--- a/src/long.js
+++ b/src/long.js
@@ -1530,13 +1530,6 @@ var i64Math = (function() { // Emscripten wrapper
// Emscripten wrapper
var Wrapper = {
- add: function(xl, xh, yl, yh) {
- var x = new goog.math.Long(xl, xh);
- var y = new goog.math.Long(yl, yh);
- var ret = x.add(y);
- HEAP32[tempDoublePtr>>2] = ret.low_;
- HEAP32[tempDoublePtr+4>>2] = ret.high_;
- },
subtract: function(xl, xh, yl, yh) {
var x = new goog.math.Long(xl, xh);
var y = new goog.math.Long(yl, yh);
@@ -1551,6 +1544,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 afdbc21e..bda8a605 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -90,6 +90,7 @@ var Debugging = {
lines[i] = ';';
continue;
}
+ if (line[0] == '!') skipLine = true;
lines[i] = skipLine ? ';' : line;
}
@@ -290,7 +291,7 @@ var Functions = {
var sig = ASM_JS ? Functions.implementedFunctions[ident] || Functions.unimplementedFunctions[ident] || LibraryManager.library[ident.substr(1) + '__sig'] : 'x';
assert(sig, ident);
if (!tables[sig]) tables[sig] = emptyTable(sig); // TODO: make them compact
- tables[sig][this.indexedFunctions[ident]] = ident;
+ tables[sig][this.indexedFunctions[ident]] = ident in DEAD_FUNCTIONS ? '0' : ident;
}
var generated = false;
var wrapped = {};
@@ -314,7 +315,7 @@ var Functions = {
}
if (ASM_JS) {
var curr = table[i];
- if (curr && !Functions.implementedFunctions[curr]) {
+ if (curr && curr != '0' && !Functions.implementedFunctions[curr]) {
// This is a library function, we can't just put it in the function table, need a wrapper
if (!wrapped[curr]) {
var args = '', arg_coercions = '', call = curr + '(', retPre = '', retPost = '';
@@ -373,7 +374,7 @@ var LibraryManager = {
load: function() {
if (this.library) return;
- var libraries = ['library.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js'].concat(additionalLibraries);
+ var libraries = ['library.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js'].concat(additionalLibraries);
for (var i = 0; i < libraries.length; i++) {
eval(processMacros(preprocess(read(libraries[i]))));
}
diff --git a/src/parseTools.js b/src/parseTools.js
index 7f4f3a18..2664baed 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -172,6 +172,7 @@ function getBits(type) {
}
if (isStructType(type)) {
var typeData = Types.types[type];
+ if (typeData === undefined) return 0;
return typeData.flatSize*8;
}
return 0;
@@ -583,7 +584,7 @@ function cleanOutTokens(filterOut, tokens, indexes) {
if (typeof indexes !== 'object') indexes = [indexes];
for (var i = indexes.length-1; i >=0; i--) {
var index = indexes[i];
- while (tokens[index].text in filterOut) {
+ while (index < tokens.length && tokens[index].text in filterOut) {
tokens.splice(index, 1);
}
}
@@ -1248,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) {
@@ -1268,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);
@@ -1283,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;
}
@@ -1326,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);
@@ -1334,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;
@@ -1739,13 +1724,14 @@ function makeLLVMStruct(values) {
}
}
-function makeStructuralReturn(values) {
+function makeStructuralReturn(values, inAsm) {
if (USE_TYPED_ARRAYS == 2) {
- var i = 0;
- return 'return (' + values.slice(1).map(function(value) {
- return ASM_JS ? 'asm.setTempRet' + (i++) + '(' + value + ')'
+ var i = -1;
+ return 'return ' + asmCoercion(values.slice(1).map(function(value) {
+ i++;
+ return ASM_JS ? (inAsm ? 'tempRet' + i + ' = ' + value : 'asm.setTempRet' + i + '(' + value + ')')
: 'tempRet' + (i++) + ' = ' + value;
- }).concat([values[0]]).join(',') + ')';
+ }).concat([values[0]]).join(','), 'i32');
} else {
var i = 0;
return 'return { ' + values.map(function(value) {
@@ -1986,6 +1972,10 @@ function processMathop(item) {
return finish(['(i64Math' + (ASM_JS ? '_' : '.') + type + '(' + asmCoercion(low1, 'i32') + ',' + asmCoercion(high1, 'i32') + ',' + asmCoercion(low2, 'i32') + ',' + asmCoercion(high2, 'i32') +
(lastArg ? ',' + asmCoercion(+lastArg, 'i32') : '') + '),' + makeGetValue('tempDoublePtr', 0, 'i32') + ')', makeGetValue('tempDoublePtr', Runtime.getNativeTypeSize('i32'), 'i32')]);
}
+ function i64PreciseLib(type) {
+ Types.preciseI64MathUsed = true;
+ return finish(['_i64' + type[0].toUpperCase() + type.substr(1) + '(' + low1 + ',' + high1 + ',' + low2 + ',' + high2 + ')', 'tempRet0']);
+ }
switch (op) {
// basic integer ops
case 'or': {
@@ -2000,43 +1990,7 @@ function processMathop(item) {
case 'shl':
case 'ashr':
case 'lshr': {
- if (!isNumber(idents[1])) {
- return '(Runtime' + (ASM_JS ? '_' : '.') + 'bitshift64(' + idents[0] + '[0], ' + idents[0] + '[1],' + Runtime['BITSHIFT64_' + op.toUpperCase()] + ',' + stripCorrections(idents[1]) + '[0]|0),' +
- '[' + makeGetTempDouble(0, 'i32') + ',' + makeGetTempDouble(1, 'i32') + '])';
- }
- bits = parseInt(idents[1]);
- var ander = Math.pow(2, bits)-1;
- if (bits < 32) {
- switch (op) {
- case 'shl':
- return '[' + idents[0] + '[0] << ' + idents[1] + ', ' +
- '('+idents[0] + '[1] << ' + idents[1] + ') | ((' + idents[0] + '[0]&(' + ander + '<<' + (32 - bits) + ')) >>> (32-' + idents[1] + '))]';
- case 'ashr':
- return '[((('+idents[0] + '[0] >>> ' + idents[1] + ') | ((' + idents[0] + '[1]&' + ander + ')<<' + (32 - bits) + ')) >> 0) >>> 0,' +
- '(' + idents[0] + '[1] >> ' + idents[1] + ') >>> 0]';
- case 'lshr':
- return '[(('+idents[0] + '[0] >>> ' + idents[1] + ') | ((' + idents[0] + '[1]&' + ander + ')<<' + (32 - bits) + ')) >>> 0,' +
- idents[0] + '[1] >>> ' + idents[1] + ']';
- }
- } else if (bits == 32) {
- switch (op) {
- case 'shl':
- return '[0, ' + idents[0] + '[0]]';
- case 'ashr':
- return '[' + idents[0] + '[1], (' + idents[0] + '[1]|0) < 0 ? ' + ander + ' : 0]';
- case 'lshr':
- return '[' + idents[0] + '[1], 0]';
- }
- } else { // bits > 32
- switch (op) {
- case 'shl':
- return '[0, ' + idents[0] + '[0] << ' + (bits - 32) + ']';
- case 'ashr':
- return '[(' + idents[0] + '[1] >> ' + (bits - 32) + ') >>> 0, (' + idents[0] + '[1]|0) < 0 ? ' + ander + ' : 0]';
- case 'lshr':
- return '[' + idents[0] + '[1] >>> ' + (bits - 32) + ', 0]';
- }
- }
+ throw 'shifts should have been legalized!';
}
case 'uitofp': case 'sitofp': return RuntimeGenerator.makeBigInt(low1, high1, op[0] == 'u');
case 'fptoui': case 'fptosi': return finish(splitI64(idents[0], true));
@@ -2074,7 +2028,7 @@ function processMathop(item) {
// Dangerous, rounded operations. TODO: Fully emulate
case 'add': {
if (PRECISE_I64_MATH) {
- return i64PreciseOp('add');
+ return i64PreciseLib('add');
} else {
warnI64_1();
return finish(splitI64(mergeI64(idents[0]) + '+' + mergeI64(idents[1]), true));
@@ -2365,3 +2319,7 @@ function getImplementationType(varInfo) {
return varInfo.type;
}
+function charCode(char) {
+ return char.charCodeAt(0);
+}
+
diff --git a/src/preamble.js b/src/preamble.js
index 9bc68d8f..6f3a969c 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -503,6 +503,7 @@ function allocate(slab, types, allocator, ptr) {
Module['allocate'] = allocate;
function Pointer_stringify(ptr, /* optional */ length) {
+#if UTF_STRING_SUPPORT
var utf8 = new Runtime.UTF8Processor();
var nullTerminated = typeof(length) == "undefined";
var ret = "";
@@ -519,18 +520,16 @@ function Pointer_stringify(ptr, /* optional */ length) {
if (!nullTerminated && i == length) break;
}
return ret;
+#else
+#if USE_TYPED_ARRAYS == 2
+ return String.fromCharCode.apply(String, HEAPU8.subarray(ptr, ptr + (length || _strlen(ptr))));
+#else
+ throw 'unsupported combination';
+#endif
+#endif
}
Module['Pointer_stringify'] = Pointer_stringify;
-function Array_stringify(array) {
- var ret = "";
- for (var i = 0; i < array.length; i++) {
- ret += String.fromCharCode(array[i]);
- }
- return ret;
-}
-Module['Array_stringify'] = Array_stringify;
-
// Memory management
var PAGE_SIZE = 4096;
@@ -848,5 +847,22 @@ Module['removeRunDependency'] = removeRunDependency;
Module["preloadedImages"] = {}; // maps url to image data
Module["preloadedAudios"] = {}; // maps url to audio data
+#if PGO
+var PGOMonitor = {
+ called: {},
+ dump: function() {
+ var dead = [];
+ for (var i = 0; i < this.allGenerated.length; i++) {
+ var func = this.allGenerated[i];
+ if (!this.called[func]) dead.push(func);
+ }
+ Module.print('-s DEAD_FUNCTIONS=\'' + JSON.stringify(dead) + '\'\n');
+ }
+};
+__ATEXIT__.push({ func: function() { PGOMonitor.dump() } });
+if (!Module.preRun) Module.preRun = [];
+Module.preRun.push(function() { addRunDependency('pgo') });
+#endif
+
// === Body ===
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index 1a7acc15..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
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/test.txt b/src/relooper/test.txt
index b7c8794d..12d0ef39 100644
--- a/src/relooper/test.txt
+++ b/src/relooper/test.txt
@@ -54,7 +54,7 @@ while(1) {
// code 2
if (!($2)) {
var $x_1 = $x_0;
- label = 19;
+ label = 18;
break;
}
// code 3
@@ -64,7 +64,7 @@ while(1) {
var $i_0 = $7;var $x_0 = $5;
}
}
-if (label == 19) {
+if (label == 18) {
// code 7
}
// code 4
diff --git a/src/runtime.js b/src/runtime.js
index 7f97da35..2a26db28 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -27,7 +27,7 @@ var RuntimeGenerator = {
// The stack is always QUANTUM SIZE aligned, so we may not need to force alignment here
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)';
+ ret += sep + 'assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')';
}
return ret;
},
@@ -38,12 +38,12 @@ var RuntimeGenerator = {
if (initial > 0) ret += '; STACKTOP = (STACKTOP + ' + initial + ')|0';
if (USE_TYPED_ARRAYS == 2) {
assert(initial % QUANTUM_SIZE == 0);
- if (ASSERTIONS) {
- ret += '; assert(STACKTOP|0 % {{{ QUANTUM_SIZE }}} == 0)';
+ if (ASSERTIONS && QUANTUM_SIZE == 4) {
+ ret += '; assert(' + asmCoercion('!(STACKTOP&3)', 'i32') + ')';
}
}
if (ASSERTIONS) {
- ret += '; assert(STACKTOP < STACK_MAX)';
+ ret += '; assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')';
}
if (false) {
ret += '; _memset(' + asmCoercion('__stackBase__', 'i32') + ', 0, ' + initial + ')';
@@ -87,7 +87,7 @@ var RuntimeGenerator = {
};
function unInline(name_, params) {
- var src = '(function ' + name_ + '(' + params + ') { var ret = ' + RuntimeGenerator[name_].apply(null, params) + '; return ret; })';
+ var src = '(function(' + params + ') { var ret = ' + RuntimeGenerator[name_].apply(null, params) + '; return ret; })';
var ret = eval(src);
return ret;
}
@@ -122,57 +122,6 @@ var Runtime = {
INT_TYPES: set('i1', 'i8', 'i16', 'i32', 'i64'),
FLOAT_TYPES: set('float', 'double'),
- // Mirrors processMathop's treatment of constants (which we optimize directly)
- BITSHIFT64_SHL: 0,
- BITSHIFT64_ASHR: 1,
- BITSHIFT64_LSHR: 2,
- bitshift64: function(low, high, op, bits) {
- var ret;
- var ander = Math.pow(2, bits)-1;
- if (bits < 32) {
- switch (op) {
- case Runtime.BITSHIFT64_SHL:
- ret = [low << bits, (high << bits) | ((low&(ander << (32 - bits))) >>> (32 - bits))];
- break;
- case Runtime.BITSHIFT64_ASHR:
- ret = [(((low >>> bits ) | ((high&ander) << (32 - bits))) >> 0) >>> 0, (high >> bits) >>> 0];
- break;
- case Runtime.BITSHIFT64_LSHR:
- ret = [((low >>> bits) | ((high&ander) << (32 - bits))) >>> 0, high >>> bits];
- break;
- }
- } else if (bits == 32) {
- switch (op) {
- case Runtime.BITSHIFT64_SHL:
- ret = [0, low];
- break;
- case Runtime.BITSHIFT64_ASHR:
- ret = [high, (high|0) < 0 ? ander : 0];
- break;
- case Runtime.BITSHIFT64_LSHR:
- ret = [high, 0];
- break;
- }
- } else { // bits > 32
- switch (op) {
- case Runtime.BITSHIFT64_SHL:
- ret = [0, low << (bits - 32)];
- break;
- case Runtime.BITSHIFT64_ASHR:
- ret = [(high >> (bits - 32)) >>> 0, (high|0) < 0 ? ander : 0];
- break;
- case Runtime.BITSHIFT64_LSHR:
- ret = [high >>> (bits - 32) , 0];
- break;
- }
- }
-#if ASSERTIONS
- assert(ret);
-#endif
- HEAP32[tempDoublePtr>>2] = ret[0]; // cannot use utility functions since we are in runtime itself
- HEAP32[tempDoublePtr+4>>2] = ret[1];
- },
-
// Imprecise bitops utilities
or64: function(x, y) {
var l = (x | 0) | (y | 0);
@@ -365,6 +314,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]) {
diff --git a/src/settings.js b/src/settings.js
index 1bfcf92a..36f53c3c 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -15,7 +15,7 @@ var QUANTUM_SIZE = 4; // This is the size of an individual field in a structure.
// the normal value of 4 means all fields take 4 memory addresses,
// as per the norm on a 32-bit machine.
//
- // 1 is somewhat faster than 4, but dangerous.
+ // Changing this from the default of 4 is deprecated.
var CORRECT_SIGNS = 1; // Whether we make sure to convert unsigned values to signed values.
// Decreases performance with additional runtime checks. Might not be
@@ -63,7 +63,7 @@ var RELOOPER = 'relooper.js'; // Loads the relooper from this path relative to c
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),
- // and addresses there are a match for normal addresses.
+ // and addresses there are a match for normal addresses. This is deprecated.
// 2 is a single heap, accessible through views as int8, int32, etc. This is
// the recommended mode both for performance and for compatibility.
var USE_FHEAP = 1; // Relevant in USE_TYPED_ARRAYS == 1. If this is disabled, only IHEAP will be used, and FHEAP
@@ -171,6 +171,8 @@ var GL_UNSAFE_OPTS = 1; // Enables some potentially-unsafe optimizations in GL e
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 UTF_STRING_SUPPORT = 1; // Perform utf-8 conversion between C and JS strings (adds overhead in such conversions)
+
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
// compiler generates code for it, maybe because of stdlibc++ stuff),
@@ -230,13 +232,14 @@ var EXPORTED_FUNCTIONS = ['_main']; // Functions that are explicitly exported. T
// the generated code even after running closure compiler (on "Module").
// Note the necessary prefix of "_".
-var DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = ['memcpy', 'memset', 'malloc', 'free', '$Browser']; // JS library functions (C functions implemented in JS)
- // that we include by default. If you want to make sure
- // something is included by the JS compiler, add it here.
- // For example, if you do not use some emscripten_*
- // C API call from C, but you want to call it from JS,
- // add it here (and in EXPORTED FUNCTIONS with prefix
- // "_", for closure).
+// JS library functions (C functions implemented in JS)
+// that we include by default. If you want to make sure
+// something is included by the JS compiler, add it here.
+// For example, if you do not use some emscripten_*
+// C API call from C, but you want to call it from JS,
+// add it here (and in EXPORTED FUNCTIONS with prefix
+// "_", for closure).
+var DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = ['memcpy', 'memset', 'malloc', 'free', 'strlen', '$Browser'];
var LIBRARY_DEPS_TO_AUTOEXPORT = ['memcpy']; // This list is also used to determine
// auto-exporting of library dependencies (i.e., functions that
@@ -332,6 +335,14 @@ var ASM_JS = 0; // If 1, generate code in asm.js format. XXX This is highly expe
// and will not work on most codebases yet. It is NOT recommended that you
// try this yet.
+var PGO = 0; // Enables profile-guided optimization in the form of runtime checks for
+ // which functions are actually called. Emits a list during shutdown that you
+ // can pass to DEAD_FUNCTIONS (you can also emit the list manually by
+ // calling PGOMonitor.dump());
+var DEAD_FUNCTIONS = []; // A list of functions that no code will be emitted for, and
+ // a runtime abort will happen if they are called
+ // TODO: options to lazily load such functions
+
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.
diff --git a/src/shell.html b/src/shell.html
index 4f39b26a..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>
diff --git a/src/utility.js b/src/utility.js
index 8db37c61..19444675 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -267,6 +267,15 @@ function set() {
}
var unset = keys;
+function numberedSet() {
+ var args = typeof arguments[0] === 'object' ? arguments[0] : arguments;
+ var ret = {};
+ for (var i = 0; i < args.length; i++) {
+ ret[args[i]] = i;
+ }
+ return ret;
+}
+
function setSub(x, y) {
var ret = set(keys(x));
for (yy in y) {