aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/jsifier.js34
-rw-r--r--src/library.js87
-rw-r--r--src/library_sdl.js20
-rw-r--r--src/modules.js6
-rw-r--r--src/parseTools.js36
-rw-r--r--src/preamble.js30
-rw-r--r--src/settings.js9
7 files changed, 167 insertions, 55 deletions
diff --git a/src/jsifier.js b/src/jsifier.js
index 9c34ddf1..bd432001 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -42,6 +42,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
substrate = new Substrate('JSifyer');
var GLOBAL_VARIABLES = !mainPass ? givenGlobalVariables : data.globalVariables;
+ Variables.globals = GLOBAL_VARIABLES;
Functions.currFunctions = !mainPass ? givenFunctions.currFunctions : {};
Functions.currExternalFunctions = !mainPass ? givenFunctions.currExternalFunctions : {};
@@ -299,6 +300,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
substrate.addActor('FunctionStub', {
processItem: function(item) {
var ret = [item];
+ if (IGNORED_FUNCTIONS.indexOf(item.ident) >= 0) return null;
var shortident = item.ident.substr(1);
if (BUILD_AS_SHARED_LIB) {
// Shared libraries reuse the runtime of their parents.
@@ -409,6 +411,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
funcs: {},
seen: {},
processItem: function(item) {
+ if (IGNORED_FUNCTIONS.indexOf(item.ident) >= 0) return null;
if (this.seen[item.__uid__]) return null;
if (item.intertype == 'function') {
this.funcs[item.ident] = item;
@@ -831,28 +834,13 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
// We cannot compile assembly. See comment in intertyper.js:'Call'
assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!');
- // Special cases
- if (ident == '_llvm_va_start') {
- // varargs - we received a pointer to the varargs as a final 'extra' parameter
- var data = 'arguments[' + Framework.currItem.funcData.ident + '.length]';
- return makeSetValue(params[0].ident, 0, data, 'void*');
- } else if (ident == '_llvm_va_end') {
- return ';';
- } else if (ident == '_EMSCRIPTEN_COMMENT') {
- var param = finalizeParam(params[0]);
- if (param.indexOf('CHECK_OVERFLOW') >= 0) {
- param = param.split('(')[1].split(',')[0];
- }
- return '// ' + GLOBAL_VARIABLES[param].value.text.replace('\\00', '') + ' ';
- }
-
+ var shortident = LibraryManager.getRootIdent(ident.slice(1)) || ident.slice(1); // ident may not be in library, if all there is is ident__inline
var func = Functions.currFunctions[ident] || Functions.currExternalFunctions[ident];
-
var args = [];
var argsTypes = [];
var varargs = [];
var varargsTypes = [];
- var useJSArgs = (ident.slice(1) + '__jsargs') in LibraryManager.library;
+ var useJSArgs = (shortident + '__jsargs') in LibraryManager.library;
params.forEach(function(param, i) {
var val = finalizeParam(param);
@@ -878,11 +866,21 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
varargs = makePointer('[' + varargs + ']', 0, 'ALLOC_STACK', varargsTypes);
}
+ args = args.concat(varargs);
+ var argsText = args.join(', ');
+
+ // Inline if either we inline whenever we can (and we can), or if there is no noninlined version
+ var inline = LibraryManager.library[shortident + '__inline'];
+ var nonInlined = shortident in LibraryManager.library;
+ if (inline && (INLINE_LIBRARY_FUNCS || !nonInlined)) {
+ return inline.apply(null, args); // Warning: inlining does not prevent recalculation of the arguments. They should be simple identifiers
+ }
+
if (getVarData(funcData, ident)) {
ident = 'FUNCTION_TABLE[' + ident + ']';
}
- return ident + '(' + args.concat(varargs).join(', ') + ')';
+ return ident + '(' + args.join(', ') + ')';
}
makeFuncLineActor('getelementptr', function(item) { return finalizeLLVMFunctionCall(item) });
makeFuncLineActor('call', function(item) {
diff --git a/src/library.js b/src/library.js
index c753422d..faa86c68 100644
--- a/src/library.js
+++ b/src/library.js
@@ -24,7 +24,7 @@ LibraryManager.library = {
_impure_ptr: 0,
$FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'],
- $FS__postset: 'FS.init();',
+ $FS__postset: 'FS.init(); __ATEXIT__.push({ func: function() { FS.quit() } });',
$FS: {
// The path to the current folder.
currentPath: '/',
@@ -246,8 +246,7 @@ LibraryManager.library = {
// Makes sure a file's contents are loaded. Returns whether the file has
// been loaded successfully. No-op for files that have been loaded already.
forceLoadFile: function(obj) {
- if (obj.isDevice || obj.isFolder || obj.link ||
- 'contents' in obj) return true;
+ if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true;
var success = true;
if (typeof XMLHttpRequest !== 'undefined') {
// Browser.
@@ -323,19 +322,6 @@ LibraryManager.library = {
return input.cache.shift();
};
if (!output) output = function(val) {
- if (!output.printer) {
- if (typeof print == 'function') {
- // Either console or custom print function defined.
- output.printer = print;
- } else if (console && typeof console.log == 'function') {
- // Browser-like environment with a console.
- output.printer = console.log;
- } else {
- // Fallback to a harmless no-op.
- output.printer = function() {};
- }
- }
- if (!output.buffer) output.buffer = [];
if (val === null || val === '\n'.charCodeAt(0)) {
output.printer(output.buffer.join(''));
output.buffer = [];
@@ -343,6 +329,8 @@ LibraryManager.library = {
output.buffer.push(String.fromCharCode(val));
}
};
+ if (!output.printer) output.printer = print;
+ if (!output.buffer) output.buffer = [];
if (!error) error = output;
// Create the temporary folder.
@@ -403,6 +391,12 @@ LibraryManager.library = {
// Once initialized, permissions start having effect.
FS.ignorePermissions = false;
+ },
+
+ quit: function() {
+ // Flush any partially-printed lines in stdout and stderr
+ if (FS.streams[2].object.output.buffer.length > 0) FS.streams[2].object.output('\n'.charCodeAt(0));
+ if (FS.streams[3].object.output.buffer.length > 0) FS.streams[3].object.output('\n'.charCodeAt(0));
}
},
@@ -3353,6 +3347,7 @@ LibraryManager.library = {
qsort__deps: ['memcpy'],
qsort: function(base, num, size, comparator) {
+ if (num == 0 || size == 0) return;
// forward calls to the JavaScript sort method
// first, sort the items logically
comparator = FUNCTION_TABLE[comparator];
@@ -3572,13 +3567,21 @@ LibraryManager.library = {
// string.h
// ==========================================================================
+ memcpy__inline: function (dest, src, num, idunno) {
+ var ret = '';
+#if ASSERTIONS
+ ret += "assert(" + num + " % 1 === 0, 'memcpy given ' + " + num + " + ' bytes to copy. Problem with QUANTUM_SIZE=1 corrections perhaps?');";
+#endif
+ ret += makeCopyValues(dest, src, num, 'null');
+ return ret;
+ },
memcpy: function (dest, src, num, idunno) {
#if ASSERTIONS
assert(num % 1 === 0, 'memcpy given ' + num + ' bytes to copy. Problem with QUANTUM_SIZE=1 corrections perhaps?');
#endif
- // || 0, since memcpy sometimes copies uninitialized areas XXX: Investigate why initializing alloc'ed memory does not fix that too
- {{{ makeCopyValues('dest', 'src', 'num', 'null', ' || 0') }}};
+ {{{ makeCopyValues('dest', 'src', 'num', 'null') }}};
},
+
llvm_memcpy_i32: 'memcpy',
llvm_memcpy_i64: 'memcpy',
llvm_memcpy_p0i8_p0i8_i32: 'memcpy',
@@ -3598,6 +3601,9 @@ LibraryManager.library = {
llvm_memmove_p0i8_p0i8_i32: 'memmove',
llvm_memmove_p0i8_p0i8_i64: 'memmove',
+ memset__inline: function(ptr, value, num) {
+ return makeSetValues(ptr, 0, value, 'null', num);
+ },
memset: function(ptr, value, num) {
{{{ makeSetValues('ptr', '0', 'value', 'null', 'num') }}}
},
@@ -3978,6 +3984,14 @@ LibraryManager.library = {
// LLVM specifics
// ==========================================================================
+ llvm_va_start__inline: function(ptr) {
+ // varargs - we received a pointer to the varargs as a final 'extra' parameter
+ var data = 'arguments[' + Framework.currItem.funcData.ident + '.length]';
+ return makeSetValue(ptr, 0, data, 'void*');
+ },
+
+ llvm_va_end: function() {},
+
llvm_va_copy: function(ppdest, ppsrc) {
{{{ makeCopyValues('ppdest', 'ppsrc', QUANTUM_SIZE, 'null') }}}
/* Alternate implementation that copies the actual DATA; it assumes the va_list is prefixed by its size
@@ -4003,6 +4017,15 @@ LibraryManager.library = {
}
return ret;
},
+
+ llvm_ctlz_i32: function(x) {
+ for (var i=0; i<32; i++) {
+ if ( (x & (1 << (31-i))) != 0 ) {
+ return i;
+ }
+ }
+ return 32;
+ },
__assert_fail: function(condition, file, line) {
ABORT = true;
@@ -4158,8 +4181,8 @@ LibraryManager.library = {
return ret;
},
- llvm_expect_i32: function(x, y) {
- return x == y; // TODO: inline this
+ llvm_expect_i32__inline: function(x, y) {
+ return '((' + x + ')==(' + y + '))';
},
llvm_lifetime_start: function() {},
@@ -4169,10 +4192,20 @@ LibraryManager.library = {
// iostream.h
// ==========================================================================
- // TODO: Document; compile from real implementation.
+ // libc++
+
+ $libcxx__postset: 'try { __ZNSt3__14coutE = 1 } catch(e){}; try { __ZNSt3__14cerrE = 2 } catch(e){};',
+ $libcxx: {},
+
+ _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPKv__deps: ['fputs', '$libcxx'],
+ _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPKv: function(stream, str) {
+ _fputs(str, _stdout); // XXX stderr etc.
+ },
+
+ // glibc
_ZNSt8ios_base4InitC1Ev: function() {
- // need valid 'file descriptors'
+ // need valid 'file descriptors' for glibc
//__ZSt4cout = 1;
//__ZSt4cerr = 2;
},
@@ -4245,6 +4278,11 @@ LibraryManager.library = {
scalbnf: 'ldexp',
scalbln: 'ldexp',
scalblnf: 'ldexp',
+ cbrt: function(x) {
+ return Math.pow(x, 1/3);
+ },
+ cbrtf: 'cbrt',
+ cbrtl: 'cbrt',
modf: function(x, intpart) {
{{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'double') }}}
@@ -5374,6 +5412,11 @@ LibraryManager.library = {
_Z21emscripten_run_scriptPKc: function(ptr) {
eval(Pointer_stringify(ptr));
+ },
+
+ EMSCRIPTEN_COMMENT__inline: function(param) {
+ param = stripCorrections(param);
+ return '// ' + Variables.globals[param].value.text.replace('\\00', '') + ' ';
}
};
diff --git a/src/library_sdl.js b/src/library_sdl.js
index af94301a..5ca049fe 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -255,6 +255,8 @@ mergeInto(LibraryManager.library, {
},
SDL_SetVideoMode: function(width, height, depth, flags) {
+ Module['canvas'].width = width;
+ Module['canvas'].height = height;
return SDL.screen = SDL.makeSurface(width, height, flags);
},
@@ -268,7 +270,7 @@ mergeInto(LibraryManager.library, {
}
surfData.ctx.putImageData(surfData.image, 0, 0);
}
- _SDL_CloseAudio(); // make sure we don't leave our audio timer running
+ if (SDL.audio) _SDL_CloseAudio(); // make sure we don't leave our audio timer running
__shutdownRuntime__();
throw 'SDL_Quit!';
},
@@ -304,11 +306,27 @@ mergeInto(LibraryManager.library, {
if (!surfData.colors) {
var data = surfData.image.data;
var buffer = surfData.buffer;
+#if USE_TYPED_ARRAYS == 2
+ assert(buffer % 4 == 0, 'Invalid buffer offset: ' + buffer);
+ var src = buffer >> 2;
+ var dst = 0;
+ while (dst < num) {
+ var val = HEAP32[src]; // This is optimized. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
+ data[dst] = val & 0xff;
+ data[dst+1] = (val >> 8) & 0xff;
+ data[dst+2] = (val >> 16) & 0xff;
+ data[dst+3] = 0xff;
+ src++;
+ dst += 4;
+ }
+#else
for (var i = 0; i < num; i++) {
// We may need to correct signs here. Potentially you can hardcode a write of 255 to alpha, say, and
// the compiler may decide to write -1 in the llvm bitcode...
data[i] = {{{ makeGetValue('buffer', 'i', 'i8') + (CORRECT_SIGNS ? '&0xff' : '') }}};
+ if (i % 4 == 3) data[i] = 0xff;
}
+#endif
} else {
var width = Module['canvas'].width;
var height = Module['canvas'].height;
diff --git a/src/modules.js b/src/modules.js
index 2341b575..3b370878 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -186,6 +186,10 @@ var Debugging = {
}
};
+var Variables = {
+ globals: null
+};
+
var Types = {
types: {},
fatTypes: {}, // With QUANTUM_SIZE=1, we store the full-size type data here
@@ -255,7 +259,7 @@ var LibraryManager = {
load: function() {
assert(!this.library);
- for (var suffix in set('', '_sdl', '_gl', '_browser')) {
+ for (var suffix in set('', '_sdl', '_browser')) {
eval(processMacros(preprocess(read('library' + suffix + '.js'), CONSTANTS)));
}
},
diff --git a/src/parseTools.js b/src/parseTools.js
index f9ef419a..2e7e1970 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -793,14 +793,23 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore) {
}
}
+var UNROLL_LOOP_LIMIT = 5;
+
function makeSetValues(ptr, pos, value, type, num) {
function safety(where) {
where = where || getFastValue(ptr, '+', pos) + '+$mspi$';
return ';' + (SAFE_HEAP ? 'SAFE_HEAP_ACCESS(' + where + ', ' + type + ', 1)' : '');
}
if (USE_TYPED_ARRAYS in set(0, 1)) {
+ if (isNumber(num)) {
+ if (num < UNROLL_LOOP_LIMIT) {
+ return range(num).map(function(i) {
+ return makeSetValue(ptr, getFastValue(pos, '+', i), value, type);
+ }).join('; ');
+ }
+ }
return 'for (var $mspi$ = 0; $mspi$ < ' + num + '; $mspi$++) {\n' +
- makeSetValue(ptr, getFastValue(pos, '+', '$mspi$'), value, type) + ';\n}';
+ makeSetValue(ptr, getFastValue(pos, '+', '$mspi$'), value, type) + '\n}';
} else { // USE_TYPED_ARRAYS == 2
/*
return 'for (var $mspi$ = 0; $mspi$ < ' + num + '; $mspi$++) {\n' +
@@ -837,6 +846,17 @@ function makeCopyValues(dest, src, num, type, modifier) {
return (SAFE_HEAP ? 'SAFE_HEAP_COPY_HISTORY(' + to + ', ' + from + ')' : '');
}
if (USE_TYPED_ARRAYS in set(0, 1)) {
+ if (isNumber(num)) {
+ if (num < UNROLL_LOOP_LIMIT) {
+ return range(num).map(function(i) {
+ return type !== 'null' ? makeSetValue(dest, i, makeGetValue(src, i, type) + (modifier || ''), type)
+ : // Null is special-cased: We copy over all heaps
+ makeGetSlabs(dest, 'null', true).map(function(slab) {
+ return slab + '[' + dest + '+' + i + ']=' + slab + '[' + src + '+' + i + ']';
+ }).join('; ') + '; ' + safety(dest + '+' + i, src + '+' + i)
+ }).join('; ');
+ }
+ }
return 'for (var $mcpi$ = 0; $mcpi$ < ' + num + '; $mcpi$++) {\n' +
(type !== 'null' ? makeSetValue(dest, '$mcpi$', makeGetValue(src, '$mcpi$', type) + (modifier || ''), type)
: // Null is special-cased: We copy over all heaps
@@ -1362,3 +1382,17 @@ function finalizeBlockAddress(param) {
return Functions.currFunctions[param.func].labelIds[param.label]; // XXX We rely on currFunctions here...?
}
+function stripCorrections(param) {
+ var m;
+ if (m = /^\((.*)\)$/.exec(param)) {
+ param = m[1];
+ }
+ if (m = /^\((\w+)\)&\d+$/.exec(param)) {
+ param = m[1];
+ }
+ if (m = /CHECK_OVERFLOW\(([^,)]*),.*/.exec(param)) {
+ param = m[1];
+ }
+ return param;
+}
+
diff --git a/src/preamble.js b/src/preamble.js
index c8f93b56..faf7aa72 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -23,6 +23,9 @@ var ACCEPTABLE_SAFE_HEAP_ERRORS = 0;
function SAFE_HEAP_ACCESS(dest, type, store, ignore) {
//if (dest === A_NUMBER) print ([dest, type, store] + ' ' + new Error().stack); // Something like this may be useful, in debugging
+#if USE_TYPED_ARRAYS == 2
+ return; // It is legitimate to violate the load-store assumption in this case
+#endif
if (type && type[type.length-1] == '*') type = 'i32'; // pointers are ints, for our purposes here
// Note that this will pass even with unions: You can store X, load X, then store Y and load Y.
// You cannot, however, do the nonportable act of store X and load Y!
@@ -30,7 +33,7 @@ function SAFE_HEAP_ACCESS(dest, type, store, ignore) {
HEAP_HISTORY[dest] = ignore ? null : type;
} else {
#if USE_TYPED_ARRAYS == 0
- if (!HEAP[dest] && HEAP[dest] !== 0 && HEAP[dest] !== false) { // false can be the result of a mathop comparator
+ if (!HEAP[dest] && HEAP[dest] !== 0 && HEAP[dest] !== false && !ignore) { // false can be the result of a mathop comparator
var error = true;
try {
if (HEAP[dest].toString() === 'NaN') error = false; // NaN is acceptable, as a double value
@@ -98,11 +101,11 @@ function SAFE_HEAP_STORE(dest, value, type, ignore) {
if (type[type.length-1] === '*') type = 'i32'; // hardcoded pointers as 32-bit
switch(type) {
case 'i1': case 'i8': HEAP8[dest] = value; break;
- case 'i16': assert(dest % 2 === 0, type + ' stores must be aligned'); HEAP16[dest>>1] = value; break;
- case 'i32': assert(dest % 4 === 0, type + ' stores must be aligned'); HEAP32[dest>>2] = value; break;
- case 'i64': assert(dest % 4 === 0, type + ' stores must be aligned'); warn64(); HEAP32[dest>>2] = value; break; // XXX store int64 as int32
- case 'float': assert(dest % 4 === 0, type + ' stores must be aligned'); HEAPF32[dest>>2] = value; break;
- case 'double': assert(dest % 4 === 0, type + ' stores must be aligned'); warn64(); HEAPF32[dest>>2] = value; break; // XXX store doubles as floats
+ case 'i16': assert(dest % 2 === 0, type + ' stores must be aligned: ' + dest); HEAP16[dest>>1] = value; break;
+ case 'i32': assert(dest % 4 === 0, type + ' stores must be aligned: ' + dest); HEAP32[dest>>2] = value; break;
+ case 'i64': assert(dest % 4 === 0, type + ' stores must be aligned: ' + dest); warn64(); HEAP32[dest>>2] = value; break; // XXX store int64 as int32
+ case 'float': assert(dest % 4 === 0, type + ' stores must be aligned: ' + dest); HEAPF32[dest>>2] = value; break;
+ case 'double': assert(dest % 4 === 0, type + ' stores must be aligned: ' + dest); warn64(); HEAPF32[dest>>2] = value; break; // XXX store doubles as floats
default: throw 'weird type for typed array II: ' + type + new Error().stack;
}
#else
@@ -139,18 +142,18 @@ function SAFE_HEAP_LOAD(dest, type, unsigned, ignore) {
break;
}
case 'i16': {
- assert(dest % 2 === 0, type + ' loads must be aligned');
+ assert(dest % 2 === 0, type + ' loads must be aligned: ' + dest);
ret = (unsigned ? HEAPU16 : HEAP16)[dest>>1];
break;
}
case 'i32': case 'i64': { // XXX store int64 as int32
- assert(dest % 4 === 0, type + ' loads must be aligned');
+ assert(dest % 4 === 0, type + ' loads must be aligned: ' + dest);
if (type === 'i64') warn64();
ret = (unsigned ? HEAPU32 : HEAP32)[dest>>2];
break;
}
case 'float': case 'double': { // XXX store doubles as floats
- assert(dest % 4 === 0, type + ' loads must be aligned');
+ assert(dest % 4 === 0, type + ' loads must be aligned: ' + dest);
if (type === 'double') warn64();
ret = HEAPF32[dest>>2];
break;
@@ -503,7 +506,8 @@ Module['Array_stringify'] = Array_stringify;
// Memory management
-var FUNCTION_TABLE;
+var FUNCTION_TABLE; // XXX: In theory the indexes here can be equal to pointers to stacked or malloced memory. Such comparisons should
+ // be false, but can turn out true. We should probably set the top bit to prevent such issues.
var PAGE_SIZE = 4096;
function alignMemoryPage(x) {
@@ -655,8 +659,10 @@ Module['String_copy'] = String_copy;
// Tools
-if (typeof print === 'undefined') {
- this['print'] = console.log; // we are on the web
+if (typeof console === 'object' && typeof console.log === 'function') {
+ this['print'] = function(x) { console.log(x) }; // web console
+} else if (typeof print === 'undefined') {
+ this['print'] = function(){}; // harmless no-op
}
// This processes a JS string into a C-line array of numbers, 0-terminated.
diff --git a/src/settings.js b/src/settings.js
index 0cbe989b..0e70316f 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -53,6 +53,8 @@ SKIP_STACK_IN_SMALL = 1; // When enabled, does not push/pop the stack at all in
// may allocate stack later, and in a loop, this can be
// very bad. In particular, when debugging, printf()ing
// a lot can exhaust the stack very fast, with this option.
+ // In particular, be careful with the autodebugger!
+INLINE_LIBRARY_FUNCS = 1; // Will inline library functions that have __inline defined
// Generated code debugging options
SAFE_HEAP = 0; // Check each write to the heap against a list of blocked addresses
@@ -114,6 +116,13 @@ PROFILE = 0; // Enables runtime profiling. See test_profiling for a usage exampl
EXPORTED_FUNCTIONS = ['_main']; // Functions that are explicitly exported, so they are guaranteed to
// be accessible outside of the generated code.
+IGNORED_FUNCTIONS = []; // Functions that we should not generate, neither a stub nor a complete function.
+ // This is useful if your project code includes a function, and you want to replace
+ // that in the compiled code with your own handwritten JS. (Of course even without
+ // this option, you could just override the generated function at runtime. However,
+ // JS engines might optimize better if the function is defined once in a single
+ // place in your code.)
+
EXPORTED_GLOBALS = []; // Global non-function variables that are explicitly
// exported, so they are guaranteed to be
// accessible outside of the generated code.