diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler.html | 32 | ||||
-rw-r--r-- | src/compiler.js | 84 | ||||
-rw-r--r-- | src/intertyper.js | 2 | ||||
-rw-r--r-- | src/jsifier.js | 13 | ||||
-rw-r--r-- | src/library.js | 141 | ||||
-rw-r--r-- | src/library_browser.js | 19 | ||||
-rw-r--r-- | src/long.js | 45 | ||||
-rw-r--r-- | src/modules.js | 2 |
8 files changed, 272 insertions, 66 deletions
diff --git a/src/compiler.html b/src/compiler.html index fe045356..9ea1c0af 100644 --- a/src/compiler.html +++ b/src/compiler.html @@ -5,16 +5,44 @@ Open the web console to see stderr output <hr> <pre id="output"></pre> <script> - arguments = ['', '../freetype.ll']; - //arguments = ['', '../tests/cases/phicubed.ll']; + arguments = []; var outputElement = document.getElementById('output'); + var compilerOutput = ''; print = function(x) { //outputElement.innerHTML += x; + compilerOutput += x; + }; + + // For generated code + var Module = { + print: function(x) { + outputElement.innerHTML += x; + } }; </script> <script src="compiler.js"> </script> +<textarea id="the_input" cols=50 rows=30> +; ModuleID = 'tests/hello_world.bc' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] + +; [#uses=0] +define i32 @main() { +entry: + %retval = alloca i32, align 4 ; [#uses=1 type=i32*] + store i32 0, i32* %retval + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32] + ret i32 1 +} + +; [#uses=1] +declare i32 @printf(i8*, ...) +</textarea> +<input type="button" value="run!" onclick="compile(document.getElementById('the_input').value); eval(compilerOutput)"> </body> </html> diff --git a/src/compiler.js b/src/compiler.js index 3ce53b1e..35f746a5 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -76,10 +76,12 @@ if (ENVIRONMENT_IS_NODE) { } } else if (ENVIRONMENT_IS_WEB) { - this['print'] = printErr = function(x) { + printErr = function(x) { console.log(x); }; + if (!this['print']) this['print'] = printErr; + this['read'] = function(url) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); @@ -213,42 +215,66 @@ NECESSARY_BLOCKADDRS = temp; // Read llvm -var raw = read(ll_file); -if (FAKE_X86_FP80) { - raw = raw.replace(/x86_fp80/g, 'double'); -} -if (raw.search('\r\n') >= 0) { - raw = raw.replace(/\r\n/g, '\n'); // fix windows line endings -} -var lines = raw.split('\n'); -raw = null; +function compile(raw) { + if (FAKE_X86_FP80) { + raw = raw.replace(/x86_fp80/g, 'double'); + } + if (raw.search('\r\n') >= 0) { + raw = raw.replace(/\r\n/g, '\n'); // fix windows line endings + } + var lines = raw.split('\n'); + raw = null; + + // Pre-process the LLVM assembly + + Debugging.handleMetadata(lines); -// Pre-process the LLVM assembly + function runPhase(currPhase) { + //printErr('// JS compiler in action, phase ' + currPhase + typeof lines + (lines === null)); + phase = currPhase; + if (phase != 'pre') { + if (singlePhase) PassManager.load(read(forwardedDataFile)); -//printErr('JS compiler in action, phase ' + phase); + if (phase == 'funcs') { + PreProcessor.eliminateUnneededIntrinsics(lines); + } + } -Debugging.handleMetadata(lines); + // Do it -if (phase != 'pre') { - PassManager.load(read(forwardedDataFile)); + var intertyped = intertyper(lines); + if (singlePhase) lines = null; + var analyzed = analyzer(intertyped); + intertyped = null; + JSify(analyzed); - if (phase == 'funcs') { - PreProcessor.eliminateUnneededIntrinsics(lines); + phase = null; + + if (DEBUG_MEMORY) { + print('zzz. last gc: ' + gc()); + MemoryDebugger.dump(); + print('zzz. hanging now!'); + while(1){}; + } } -} -// Do it + // Normal operation is for each execution of compiler.js to run a single phase. The calling script sends us exactly the information we need, and it is easy to parallelize operation that way. However, it is also possible to run in an unoptimal multiphase mode, where a single invocation goes from ll to js directly. This is not recommended and will likely do a lot of duplicate processing. + singlePhase = !!phase; -var intertyped = intertyper(lines); -lines = null; -var analyzed = analyzer(intertyped); -intertyped = null; -JSify(analyzed); + if (singlePhase) { + runPhase(phase); + } else { + runPhase('pre'); + runPhase('funcs'); + runPhase('post'); + } +} -if (DEBUG_MEMORY) { - print('zzz. last gc: ' + gc()); - MemoryDebugger.dump(); - print('zzz. hanging now!'); - while(1){}; +if (ll_file) { + if (ll_file.indexOf(String.fromCharCode(10)) == -1) { + compile(read(ll_file)); + } else { + compile(ll_file); // we are given raw .ll + } } diff --git a/src/intertyper.js b/src/intertyper.js index 1d18c3cf..5af291c7 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -61,7 +61,7 @@ function intertyper(data, sidePass, baseLineNums) { var baseLineNumPosition = 0; for (var i = 0; i < lines.length; i++) { var line = lines[i]; - lines[i] = null; // lines may be very very large. Allow GCing to occur in the loop by releasing refs here + if (singlePhase) lines[i] = null; // lines may be very very large. Allow GCing to occur in the loop by releasing refs here while (baseLineNumPosition < baseLineNums.length-1 && i >= baseLineNums[baseLineNumPosition+1][0]) { baseLineNumPosition++; diff --git a/src/jsifier.js b/src/jsifier.js index 4192cd3f..70329b3f 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -417,8 +417,8 @@ function JSify(data, functionsOnly, givenFunctions) { // 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; }'; + snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.print("[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.print(" [ return:" + Runtime.prettyPrint(ret)); return ret; }'; } } @@ -1310,6 +1310,7 @@ function JSify(data, functionsOnly, givenFunctions) { // Print out global variables and postsets TODO: batching if (phase == 'pre') { + var legalizedI64sDefault = legalizedI64s; legalizedI64s = false; var globalsData = analyzer(intertyper(data.unparsedGlobalss[0].lines, true), true); @@ -1332,9 +1333,13 @@ function JSify(data, functionsOnly, givenFunctions) { var generated = itemsDict.functionStub.concat(itemsDict.GlobalVariablePostSet); generated.forEach(function(item) { print(indentify(item.JS || '', 2)); }); + + legalizedI64s = legalizedI64sDefault; } else { - assert(data.unparsedGlobalss[0].lines.length == 0, dump([phase, data.unparsedGlobalss])); - assert(itemsDict.functionStub.length == 0, dump([phase, itemsDict.functionStub])); + if (singlePhase) { + assert(data.unparsedGlobalss[0].lines.length == 0, dump([phase, data.unparsedGlobalss])); + assert(itemsDict.functionStub.length == 0, dump([phase, itemsDict.functionStub])); + } } if (phase == 'pre' || phase == 'funcs') { diff --git a/src/library.js b/src/library.js index 93d54d68..0b59b404 100644 --- a/src/library.js +++ b/src/library.js @@ -611,7 +611,7 @@ LibraryManager.library = { }, deleteFile: function(path) { - var path = FS.analyzePath(path); + path = FS.analyzePath(path); if (!path.parentExists || !path.exists) { throw 'Invalid path ' + path; } @@ -3548,21 +3548,42 @@ LibraryManager.library = { * this implementation simply uses malloc underneath the call to * mmap. */ + if (!_mmap.mappings) _mmap.mappings = {}; if (stream == -1) { var ptr = _malloc(num); - _memset(ptr, 0, num); - return ptr; + } else { + var info = FS.streams[stream]; + if (!info) return -1; + var contents = info.object.contents; + contents = Array.prototype.slice.call(contents, offset, offset+num); + ptr = allocate(contents, 'i8', ALLOC_NORMAL); + } + // align to page size + var ret = ptr; + if (ptr % PAGE_SIZE != 0) { + var old = ptr; + ptr = _malloc(num + PAGE_SIZE); + ret = alignMemoryPage(ptr); + _memcpy(ret, old, num); + _free(old); } - var info = FS.streams[stream]; - if (!info) return -1; - var contents = info.object.contents; - contents = Array.prototype.slice.call(contents, offset, offset+num); - return allocate(contents, 'i8', ALLOC_NORMAL); + if (stream == -1) { + _memset(ret, 0, num); + } + _mmap.mappings[ret] = { malloc: ptr, num: num }; + return ret; }, __01mmap64_: 'mmap', munmap: function(start, num) { - _free(start); + if (!_mmap.mappings) _mmap.mappings = {}; + // TODO: support unmmap'ing parts of allocations + var info = _mmap.mappings[start]; + if (!info) return 0; + if (num == info.num) { + _mmap.mappings[start] = null; + _free(info.malloc); + } return 0; }, @@ -3816,9 +3837,73 @@ LibraryManager.library = { return ret; }, - strtoll__deps: ['_parseInt'], +#if USE_TYPED_ARRAYS == 2 + _parseInt64__deps: ['isspace', '__setErrNo', '$ERRNO_CODES', function() { Types.preciseI64MathUsed = 1 }], + _parseInt64: function(str, endptr, base, min, max, unsign) { + var start = str; + // Skip space. + while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++; + + // Check for a plus/minus sign. + if ({{{ makeGetValue('str', 0, 'i8') }}} == '-'.charCodeAt(0)) { + str++; + } else if ({{{ makeGetValue('str', 0, 'i8') }}} == '+'.charCodeAt(0)) { + str++; + } + + // Find base. + 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)) { + finalBase = 16; + str += 2; + } else { + finalBase = 8; + str++; + ok = true; // we saw an initial zero, perhaps the entire thing is just "0" + } + } + } + if (!finalBase) finalBase = 10; + + // Get digits. + var chr; + while ((chr = {{{ makeGetValue('str', 0, 'i8') }}}) != 0) { + var digit = parseInt(String.fromCharCode(chr), finalBase); + if (isNaN(digit)) { + break; + } else { + str++; + ok = true; + } + } + if (!ok) { + ___setErrNo(ERRNO_CODES.EINVAL); + return [0, 0]; + } + + try { + i64Math.fromString(Pointer_stringify(start, str - start), finalBase, min, max, unsign); + } catch(e) { + ___setErrNo(ERRNO_CODES.ERANGE); // not quite correct + } + + // Set end pointer. + if (endptr) { + {{{ makeSetValue('endptr', 0, 'str', '*') }}} + } + + var ret = i64Math.result.slice(0); + + return ret; + }, +#endif + strtoll__deps: ['_parseInt64'], strtoll: function(str, endptr, base) { - return __parseInt(str, endptr, base, -9223372036854775200, 9223372036854775200, 64); // LLONG_MIN, LLONG_MAX; imprecise. + return __parseInt64(str, endptr, base, '-9223372036854775808', '9223372036854775807'); // LLONG_MIN, LLONG_MAX. }, strtoll_l: 'strtoll', // no locale support yet strtol__deps: ['_parseInt'], @@ -3831,9 +3916,9 @@ LibraryManager.library = { return __parseInt(str, endptr, base, 0, 4294967295, 32, true); // ULONG_MAX. }, strtoul_l: 'strtoul', // no locale support yet - strtoull__deps: ['_parseInt'], + strtoull__deps: ['_parseInt64'], strtoull: function(str, endptr, base) { - return __parseInt(str, endptr, base, 0, 18446744073709551615, 64, true); // ULONG_MAX; imprecise. + return __parseInt64(str, endptr, base, 0, '18446744073709551615', true); // ULONG_MAX. }, strtoull_l: 'strtoull', // no locale support yet @@ -4742,6 +4827,17 @@ LibraryManager.library = { return ((x&0xff)<<24) | (((x>>8)&0xff)<<16) | (((x>>16)&0xff)<<8) | (x>>>24); }, + llvm_bswap_i64__deps: ['llvm_bswap_i32'], + llvm_bswap_i64: function(l, h) { + var retl = _llvm_bswap_i32(h)>>>0; + var reth = _llvm_bswap_i32(l)>>>0; +#if USE_TYPED_ARRAYS == 2 + return [retl, reth]; +#else + throw 'unsupported'; +#endif + }, + llvm_ctlz_i32: function(x) { for (var i=0; i<32; i++) { if ( (x & (1 << (31-i))) != 0 ) { @@ -4751,17 +4847,28 @@ LibraryManager.library = { return 32; }, + llvm_ctlz_i64__deps: ['llvm_ctlz_i32'], + llvm_ctlz_i64: function(l, h) { + var ret = _llvm_ctlz_i32(h); + if (ret == 32) ret += _llvm_ctlz_i32(l); +#if USE_TYPED_ARRAYS == 2 + return [ret, 0]; +#else + return ret; +#endif + }, + llvm_trap: function() { throw 'trap! ' + new Error().stack; }, __assert_fail: function(condition, file, line) { ABORT = true; - throw 'Assertion failed: ' + Pointer_stringify(condition);//JSON.stringify(arguments)//condition; + throw 'Assertion failed: ' + Pointer_stringify(condition) + ' at ' + new Error().stack; }, __assert_func: function(filename, line, func, condition) { - throw 'Assertion failed: ' + (condition ? Pointer_stringify(condition) : 'unknown condition') + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function']; + throw 'Assertion failed: ' + (condition ? Pointer_stringify(condition) : 'unknown condition') + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + new Error().stack; }, __cxa_guard_acquire: function(variable) { @@ -5105,8 +5212,8 @@ LibraryManager.library = { return ret; }, - llvm_expect_i32__inline: function(x, y) { - return '((' + x + ')==(' + y + '))'; + llvm_expect_i32__inline: function(val, expected) { + return '(' + val + ')'; }, llvm_lifetime_start: function() {}, diff --git a/src/library_browser.js b/src/library_browser.js index 00ee158c..b14099f6 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -336,11 +336,11 @@ mergeInto(LibraryManager.library, { xhr.send(null); }, - asyncLoad: function(url, onload, onerror) { + asyncLoad: function(url, onload, onerror, noRunDep) { Browser.xhrLoad(url, function(arrayBuffer) { assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).'); onload(new Uint8Array(arrayBuffer)); - removeRunDependency('al ' + url); + if (!noRunDep) removeRunDependency('al ' + url); }, function(event) { if (onerror) { onerror(); @@ -348,7 +348,7 @@ mergeInto(LibraryManager.library, { throw 'Loading data file "' + url + '" failed.'; } }); - addRunDependency('al ' + url); + if (!noRunDep) addRunDependency('al ' + url); }, resizeListeners: [], @@ -381,10 +381,21 @@ mergeInto(LibraryManager.library, { }, function() { if (onerror) FUNCTION_TABLE[onerror](file); - } + } ); }, + emscripten_async_wget_data: function(url, arg, onload, onerror) { + Browser.asyncLoad(Pointer_stringify(url), function(byteArray) { + var buffer = _malloc(byteArray.length); + HEAPU8.set(byteArray, buffer); + FUNCTION_TABLE[onload](arg, buffer, byteArray.length); + _free(buffer); + }, function() { + if (onerror) FUNCTION_TABLE[onerror](arg); + }, true /* no need for run dependency, this is async but will not do any prepare etc. step */ ); + }, + emscripten_async_prepare: function(file, onload, onerror) { var _file = Pointer_stringify(file); var data = FS.analyzePath(_file); diff --git a/src/long.js b/src/long.js index d5770e48..fc67733f 100644 --- a/src/long.js +++ b/src/long.js @@ -1053,7 +1053,7 @@ var i64Math = (function() { // Emscripten wrapper if(r != 0) return r; var i = this.t; r = i-a.t; - if(r != 0) return r; + if(r != 0) return (this.s<0)?-r:r; while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; return 0; } @@ -1552,9 +1552,15 @@ var i64Math = (function() { // Emscripten wrapper Wrapper.result[0] = ret.low_; Wrapper.result[1] = ret.high_; }, - makeTwo32: function() { + ensureTemps: function() { + if (Wrapper.ensuredTemps) return; + Wrapper.ensuredTemps = true; Wrapper.two32 = new BigInteger(); Wrapper.two32.fromString('4294967296', 10); + Wrapper.two64 = new BigInteger(); + Wrapper.two64.fromString('18446744073709551616', 10); + Wrapper.temp1 = new BigInteger(); + Wrapper.temp2 = new BigInteger(); }, lh2bignum: function(l, h) { var a = new BigInteger(); @@ -1568,7 +1574,7 @@ var i64Math = (function() { // Emscripten wrapper return d; }, divide: function(xl, xh, yl, yh, unsigned) { - if (!Wrapper.two32) Wrapper.makeTwo32(); + Wrapper.ensureTemps(); if (!unsigned) { var x = new goog.math.Long(xl, xh); var y = new goog.math.Long(yl, yh); @@ -1589,7 +1595,7 @@ var i64Math = (function() { // Emscripten wrapper } }, modulo: function(xl, xh, yl, yh, unsigned) { - if (!Wrapper.two32) Wrapper.makeTwo32(); + Wrapper.ensureTemps(); if (!unsigned) { var x = new goog.math.Long(xl, xh); var y = new goog.math.Long(yl, yh); @@ -1613,10 +1619,7 @@ var i64Math = (function() { // Emscripten wrapper var ret = new goog.math.Long(l, h).toString(); if (unsigned && ret[0] == '-') { // unsign slowly using jsbn bignums - if (!Wrapper.two64) { - Wrapper.two64 = new BigInteger(); - Wrapper.two64.fromString('18446744073709551616', 10); - } + Wrapper.ensureTemps(); var bignum = new BigInteger(); bignum.fromString(ret, 10); ret = new BigInteger(); @@ -1624,6 +1627,32 @@ var i64Math = (function() { // Emscripten wrapper ret = ret.toString(10); } return ret; + }, + fromString: function(str, base, min, max, unsigned) { + Wrapper.ensureTemps(); + var bignum = new BigInteger(); + bignum.fromString(str, base); + var bigmin = new BigInteger(); + bigmin.fromString(min, 10); + var bigmax = new BigInteger(); + bigmax.fromString(max, 10); + if (unsigned && bignum.compareTo(BigInteger.ZERO) < 0) { + var temp = new BigInteger(); + bignum.addTo(Wrapper.two64, temp); + bignum = temp; + } + var error = false; + if (bignum.compareTo(bigmin) < 0) { + bignum = bigmin; + error = true; + } else if (bignum.compareTo(bigmax) > 0) { + bignum = bigmax; + error = true; + } + var ret = goog.math.Long.fromString(bignum.toString()); // min-max checks should have clamped this to a range goog.math.Long can handle well + Wrapper.result[0] = ret.low_; + Wrapper.result[1] = ret.high_; + if (error) throw 'range error'; } }; return Wrapper; diff --git a/src/modules.js b/src/modules.js index fd0ec35e..64638be1 100644 --- a/src/modules.js +++ b/src/modules.js @@ -269,7 +269,7 @@ var LibraryManager = { loaded: false, load: function() { - assert(!this.library); + 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); for (var i = 0; i < libraries.length; i++) { |