aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js83
-rw-r--r--src/compiler.js2
-rw-r--r--[-rwxr-xr-x]src/embind/embind.js0
-rw-r--r--[-rwxr-xr-x]src/embind/emval.js0
-rw-r--r--src/library.js133
-rw-r--r--src/library_sdl.js54
-rw-r--r--src/modules.js3
-rw-r--r--src/parseTools.js8
-rw-r--r--src/settings.js10
9 files changed, 235 insertions, 58 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 03d44cb7..a131406c 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -20,6 +20,7 @@ var BRANCH_INVOKE = set('branch', 'invoke');
var LABEL_ENDERS = set('branch', 'return', 'switch');
var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic');
var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam');
+var I64_DOUBLE_FLIP = { i64: 'double', double: 'i64' };
// Analyzer
@@ -99,7 +100,86 @@ function analyzer(data, sidePass) {
}
}
delete item.items;
+ this.forwardItem(item, 'CastAway');
+ }
+ });
+
+ // CastAway - try to remove bitcasts of double<-->i64, which LLVM sometimes generates unnecessarily
+ // (load a double, convert to i64, use as i64).
+ // We optimize this by checking if there are such bitcasts. If so we create a shadow
+ // variable that is of the other type, and use that in the relevant places. (As SSA, this is valid, and
+ // variable elimination later will remove the double load if it is no longer needed.)
+ //
+ // Note that aside from being an optimization, this is needed for correctness in some cases: If code
+ // assumes it can bitcast a double to an i64 and back and forth without loss, that may be violated
+ // due to NaN canonicalization.
+ substrate.addActor('CastAway', {
+ processItem: function(item) {
this.forwardItem(item, 'Legalizer');
+ if (USE_TYPED_ARRAYS != 2) return;
+
+ item.functions.forEach(function(func) {
+ var has = false;
+ func.labels.forEach(function(label) {
+ var lines = label.lines;
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (line.intertype == 'bitcast' && line.type in I64_DOUBLE_FLIP) {
+ has = true;
+ }
+ }
+ });
+ if (!has) return;
+ // there are i64<-->double bitcasts, create shadows for everything
+ var shadowed = {};
+ func.labels.forEach(function(label) {
+ var lines = label.lines;
+ var i = 0;
+ while (i < lines.length) {
+ var lines = label.lines;
+ var line = lines[i];
+ if (line.intertype == 'load' && line.type in I64_DOUBLE_FLIP) {
+ if (line.pointer.intertype != 'value') { i++; continue } // TODO
+ shadowed[line.assignTo] = 1;
+ var shadow = line.assignTo + '$$SHADOW';
+ var flip = I64_DOUBLE_FLIP[line.type];
+ lines.splice(i + 1, 0, { // if necessary this element will be legalized in the next phase
+ tokens: null,
+ indent: 2,
+ lineNum: line.lineNum + 0.5,
+ assignTo: shadow,
+ intertype: 'load',
+ pointerType: flip + '*',
+ type: flip,
+ valueType: flip,
+ pointer: {
+ intertype: 'value',
+ ident: line.pointer.ident,
+ type: flip + '*'
+ },
+ align: line.align,
+ ident: line.ident
+ });
+ // note: no need to update func.lines, it is generated in a later pass
+ i++;
+ }
+ i++;
+ }
+ });
+ // use shadows where possible
+ func.labels.forEach(function(label) {
+ var lines = label.lines;
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (line.intertype == 'bitcast' && line.type in I64_DOUBLE_FLIP && line.ident in shadowed) {
+ var shadow = line.ident + '$$SHADOW';
+ line.params[0].ident = shadow;
+ line.params[0].type = line.type;
+ line.type2 = line.type;
+ }
+ }
+ });
+ });
}
});
@@ -174,12 +254,15 @@ function analyzer(data, sidePass) {
var factor = (next - prev)/(4*toAdd.length+3);
for (var k = 0; k < toAdd.length; k++) {
toAdd[k].lineNum = prev + ((k+1)*factor);
+ assert(k == 0 || toAdd[k].lineNum > toAdd[k-1].lineNum);
}
}
function removeAndAdd(lines, i, toAdd) {
var item = lines[i];
interpLines(lines, i, toAdd);
Array.prototype.splice.apply(lines, [i, 1].concat(toAdd));
+ if (i > 0) assert(lines[i].lineNum > lines[i-1].lineNum);
+ if (i + toAdd.length < lines.length) assert(lines[i + toAdd.length - 1].lineNum < lines[i + toAdd.length].lineNum);
return toAdd.length;
}
function legalizeFunctionParameters(params) {
diff --git a/src/compiler.js b/src/compiler.js
index 8b9606f1..94e77e26 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -183,8 +183,8 @@ if (ASM_JS) {
assert(!ALLOW_MEMORY_GROWTH, 'Cannot grow asm.js heap');
assert((TOTAL_MEMORY&(TOTAL_MEMORY-1)) == 0, 'asm.js heap must be power of 2');
}
-assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB)); // shared libraries must have named globals
assert(!BUILD_AS_SHARED_LIB, 'shared libs are deprecated');
+//assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB), 'shared libraries must have named globals');
// Output some info and warnings based on settings
diff --git a/src/embind/embind.js b/src/embind/embind.js
index cadee700..cadee700 100755..100644
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
diff --git a/src/embind/emval.js b/src/embind/emval.js
index c02ffa92..c02ffa92 100755..100644
--- a/src/embind/emval.js
+++ b/src/embind/emval.js
diff --git a/src/library.js b/src/library.js
index cfe83c6e..84071b68 100644
--- a/src/library.js
+++ b/src/library.js
@@ -296,74 +296,97 @@ LibraryManager.library = {
if (typeof XMLHttpRequest !== 'undefined') {
if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';
// Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.
- var LazyUint8Array = function(chunkSize, length) {
- this.length = length;
- this.chunkSize = chunkSize;
+ var LazyUint8Array = function() {
+ this.lengthKnown = false;
this.chunks = []; // Loaded chunks. Index is the chunk number
}
LazyUint8Array.prototype.get = function(idx) {
if (idx > this.length-1 || idx < 0) {
return undefined;
}
- var chunkOffset = idx % chunkSize;
- var chunkNum = Math.floor(idx / chunkSize);
+ var chunkOffset = idx % this.chunkSize;
+ var chunkNum = Math.floor(idx / this.chunkSize);
return this.getter(chunkNum)[chunkOffset];
}
LazyUint8Array.prototype.setDataGetter = function(getter) {
this.getter = getter;
}
-
- // Find length
- var xhr = new XMLHttpRequest();
- xhr.open('HEAD', url, false);
- xhr.send(null);
- if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
- var datalength = Number(xhr.getResponseHeader("Content-length"));
- var header;
- var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
+
+ LazyUint8Array.prototype.cacheLength = function() {
+ // Find length
+ var xhr = new XMLHttpRequest();
+ xhr.open('HEAD', url, false);
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ var datalength = Number(xhr.getResponseHeader("Content-length"));
+ var header;
+ var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
#if SMALL_XHR_CHUNKS
- var chunkSize = 1024; // Chunk size in bytes
+ var chunkSize = 1024; // Chunk size in bytes
#else
- var chunkSize = 1024*1024; // Chunk size in bytes
+ var chunkSize = 1024*1024; // Chunk size in bytes
#endif
- if (!hasByteServing) chunkSize = datalength;
-
- // Function to get a range from the remote URL.
- var doXHR = (function(from, to) {
- if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
- if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
-
- // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
- var xhr = new XMLHttpRequest();
- xhr.open('GET', url, false);
- if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
-
- // Some hints to the browser that we want binary data.
- if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
- if (xhr.overrideMimeType) {
- xhr.overrideMimeType('text/plain; charset=x-user-defined');
- }
+ if (!hasByteServing) chunkSize = datalength;
+
+ // Function to get a range from the remote URL.
+ var doXHR = (function(from, to) {
+ if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
+ if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
+
+ // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, false);
+ if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
+
+ // Some hints to the browser that we want binary data.
+ if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType('text/plain; charset=x-user-defined');
+ }
+
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ if (xhr.response !== undefined) {
+ return new Uint8Array(xhr.response || []);
+ } else {
+ return intArrayFromString(xhr.responseText || '', true);
+ }
+ });
+ var lazyArray = this;
+ lazyArray.setDataGetter(function(chunkNum) {
+ var start = chunkNum * chunkSize;
+ var end = (chunkNum+1) * chunkSize - 1; // including this byte
+ end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
+ if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
+ lazyArray.chunks[chunkNum] = doXHR(start, end);
+ }
+ if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
+ return lazyArray.chunks[chunkNum];
+ });
+
+ this._length = datalength;
+ this._chunkSize = chunkSize;
+ this.lengthKnown = true;
+ }
- xhr.send(null);
- if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
- if (xhr.response !== undefined) {
- return new Uint8Array(xhr.response || []);
- } else {
- return intArrayFromString(xhr.responseText || '', true);
- }
+ var lazyArray = new LazyUint8Array();
+ Object.defineProperty(lazyArray, "length", {
+ get: function() {
+ if(!this.lengthKnown) {
+ this.cacheLength();
+ }
+ return this._length;
+ }
});
-
- var lazyArray = new LazyUint8Array(chunkSize, datalength);
- lazyArray.setDataGetter(function(chunkNum) {
- var start = chunkNum * lazyArray.chunkSize;
- var end = (chunkNum+1) * lazyArray.chunkSize - 1; // including this byte
- end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
- if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
- lazyArray.chunks[chunkNum] = doXHR(start, end);
- }
- if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
- return lazyArray.chunks[chunkNum];
+ Object.defineProperty(lazyArray, "chunkSize", {
+ get: function() {
+ if(!this.lengthKnown) {
+ this.cacheLength();
+ }
+ return this._chunkSize;
+ }
});
+
var properties = { isDevice: false, contents: lazyArray };
} else {
var properties = { isDevice: false, url: url };
@@ -2465,9 +2488,15 @@ LibraryManager.library = {
__scanString.whiteSpace[{{{ charCode(' ') }}}] = 1;
__scanString.whiteSpace[{{{ charCode('\t') }}}] = 1;
__scanString.whiteSpace[{{{ charCode('\n') }}}] = 1;
+ __scanString.whiteSpace[{{{ charCode('\v') }}}] = 1;
+ __scanString.whiteSpace[{{{ charCode('\f') }}}] = 1;
+ __scanString.whiteSpace[{{{ charCode('\r') }}}] = 1;
__scanString.whiteSpace[' '] = 1;
__scanString.whiteSpace['\t'] = 1;
__scanString.whiteSpace['\n'] = 1;
+ __scanString.whiteSpace['\v'] = 1;
+ __scanString.whiteSpace['\f'] = 1;
+ __scanString.whiteSpace['\r'] = 1;
}
// Supports %x, %4x, %d.%d, %lld, %s, %f, %lf.
// TODO: Support all format specifiers.
@@ -2840,7 +2869,7 @@ LibraryManager.library = {
} else if (next == {{{ charCode('o') }}}) {
argText = (flagAlternative ? '0' : '') + currAbsArg.toString(8);
} else if (next == {{{ charCode('x') }}} || next == {{{ charCode('X') }}}) {
- prefix = flagAlternative ? '0x' : '';
+ prefix = (flagAlternative && currArg != 0) ? '0x' : '';
#if PRECISE_I64_MATH
if (argSize == 8 && i64Math) {
if (origArg[1]) {
diff --git a/src/library_sdl.js b/src/library_sdl.js
index a1fb871f..37feb744 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -1705,6 +1705,60 @@ var LibrarySDL = {
SDL_GL_SwapBuffers: function() {},
+ // SDL 2
+
+ SDL_GL_ExtensionSupported: function(extension) {
+ return Module.ctx.getExtension(extension) | 0;
+ },
+
+ SDL_DestroyWindow: function(window) {},
+
+ SDL_DestroyRenderer: function(renderer) {},
+
+ SDL_GetWindowFlags: function(x, y) {
+ if (Browser.isFullScreen) {
+ return 1;
+ }
+
+ return 0;
+ },
+
+ SDL_GL_SwapWindow: function(window) {},
+
+ SDL_GL_MakeCurrent: function(window, context) {},
+
+ SDL_GL_DeleteContext: function(context) {},
+
+ SDL_GL_SetSwapInterval: function(state) {},
+
+ SDL_SetWindowTitle: function(window, title) {
+ if (title) document.title = Pointer_stringify(title);
+ },
+
+ SDL_GetWindowSize: function(window, width, height){
+ var w = Module['canvas'].width;
+ var h = Module['canvas'].height;
+ if (width) {{{ makeSetValue('width', '0', 'w', 'i32') }}};
+ if (height) {{{ makeSetValue('height', '0', 'h', 'i32') }}};
+ },
+
+ SDL_LogSetOutputFunction: function(callback, userdata) {},
+
+ SDL_SetWindowFullscreen: function(window, fullscreen) {
+ if (Browser.isFullScreen) {
+ Module['canvas'].cancelFullScreen();
+ return 1;
+ } else {
+ return 0;
+ }
+ },
+
+ SDL_GetWindowFlags: function() {},
+
+ SDL_ClearError: function() {},
+
+ SDL_getenv: 'getenv',
+
// TODO
SDL_SetGamma: function(r, g, b) {
diff --git a/src/modules.js b/src/modules.js
index 9cbe88aa..6b3c5ffb 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -259,7 +259,7 @@ var Functions = {
}
if (phase != 'post' && singlePhase) {
if (!doNotCreate) this.indexedFunctions[ident] = 0; // tell python we need this indexized
- return "'{{ FI_" + ident + " }}'"; // something python will replace later
+ return "'{{ FI_" + toNiceIdent(ident) + " }}'"; // something python will replace later
} else {
var ret = this.indexedFunctions[ident];
if (!ret) {
@@ -318,6 +318,7 @@ var Functions = {
if (ASM_JS) {
var curr = table[i];
if (curr && curr != '0' && !Functions.implementedFunctions[curr]) {
+ curr = toNiceIdent(curr); // fix Math.* to Math_*
// 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 = '';
diff --git a/src/parseTools.js b/src/parseTools.js
index db834206..fa0f251e 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1118,6 +1118,8 @@ var asmPrintCounter = 0;
// See makeSetValue
function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSafe, forceAsm) {
if (UNALIGNED_MEMORY) align = 1;
+ else if (FORCE_ALIGNED_MEMORY && !isIllegalType(type)) align = 8;
+
if (isStructType(type)) {
var typeData = Types.types[type];
var ret = [];
@@ -1220,6 +1222,8 @@ function indexizeFunctions(value, type) {
//! @param noNeedFirst Whether to ignore the offset in the pointer itself.
function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign, forceAsm) {
if (UNALIGNED_MEMORY && !forcedAlign) align = 1;
+ else if (FORCE_ALIGNED_MEMORY && !isIllegalType(type)) align = 8;
+
sep = sep || ';';
if (isStructType(type)) {
var typeData = Types.types[type];
@@ -1678,10 +1682,10 @@ function checkBitcast(item) {
function showWarning() {
if (warned) return;
warned = true;
- if (VERBOSE || ASM_JS) {
+ if (VERBOSE) {
warnOnce('Casting potentially incompatible function pointer ' + oldType + ' to ' + newType + ', for ' + item.params[0].ident.slice(1));
} else {
- warnOnce('Casting a function pointer type to a potentially incompatible one (use VERBOSE=1 to see more)');
+ warnOnce('Casting a function pointer type to a potentially incompatible one (use -s VERBOSE=1 to see more)');
}
warnOnce('See https://github.com/kripken/emscripten/wiki/CodeGuidlinesAndLimitations#function-pointer-issues for more information on dangerous function pointer casts');
if (ASM_JS) warnOnce('Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these');
diff --git a/src/settings.js b/src/settings.js
index ba1f6a83..8766277b 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -17,8 +17,8 @@ var QUANTUM_SIZE = 4; // This is the size of an individual field in a structure.
//
// Changing this from the default of 4 is deprecated.
-var TARGET_X86 = 1; // For le32-unknown-nacl
-var TARGET_LE32 = 0; // For i386-pc-linux-gnu
+var TARGET_X86 = 0; // For i386-pc-linux-gnu
+var TARGET_LE32 = 1; // For le32-unknown-nacl
var CORRECT_SIGNS = 1; // Whether we make sure to convert unsigned values to signed values.
// Decreases performance with additional runtime checks. Might not be
@@ -89,6 +89,12 @@ var UNALIGNED_MEMORY = 0; // If enabled, all memory accesses are assumed to be u
// typed arrays mode 2 where alignment is relevant.) In unaligned memory mode, you
// can run nonportable code that typically would break in JS (or on ARM for that
// matter, which also cannot do unaligned reads/writes), at the cost of slowness
+var FORCE_ALIGNED_MEMORY = 0; // If enabled, assumes all reads and writes are fully aligned for the type they
+ // use. This is true in proper C code (no undefined behavior), but is sadly
+ // common enough that we can't do it by default. See SAFE_HEAP and CHECK_HEAP_ALIGN
+ // for ways to help find places in your code where unaligned reads/writes are done -
+ // you might be able to refactor your codebase to prevent them, which leads to
+ // smaller and faster code, or even the option to turn this flag on.
var PRECISE_I64_MATH = 1; // If enabled, i64 addition etc. is emulated - which is slow but precise. If disabled,
// we use the 'double trick' which is fast but incurs rounding at high values.
// Note that we do not catch 32-bit multiplication by default (which must be done in