diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/asm2wasm.h | 115 | ||||
-rw-r--r-- | src/js/wasm.js-post.js | 351 |
2 files changed, 83 insertions, 383 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 32b6af5b0..c25855712 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -613,8 +613,63 @@ private: return ret; } + Expression* makePotentiallyTrappingI32Binary(BinaryOp op, Expression* left, Expression* right) { + if (imprecise) return builder.makeBinary(op, left, right); + // we are precise, and the wasm operation might trap if done over 0, so generate a safe call + auto *call = allocator.alloc<Call>(); + switch (op) { + case BinaryOp::RemSInt32: call->target = I32S_REM; break; + case BinaryOp::RemUInt32: call->target = I32U_REM; break; + case BinaryOp::DivSInt32: call->target = I32S_DIV; break; + case BinaryOp::DivUInt32: call->target = I32U_DIV; break; + default: WASM_UNREACHABLE(); + } + call->operands.push_back(left); + call->operands.push_back(right); + call->type = i32; + static std::set<Name> addedFunctions; + if (addedFunctions.count(call->target) == 0) { + Expression* result = builder.makeBinary(op, + builder.makeGetLocal(0, i32), + builder.makeGetLocal(1, i32) + ); + if (op == DivSInt32) { + // guard against signed division overflow + result = builder.makeIf( + builder.makeBinary(AndInt32, + builder.makeBinary(EqInt32, + builder.makeGetLocal(0, i32), + builder.makeConst(Literal(std::numeric_limits<int32_t>::min())) + ), + builder.makeBinary(EqInt32, + builder.makeGetLocal(1, i32), + builder.makeConst(Literal(int32_t(-1))) + ) + ), + builder.makeConst(Literal(int32_t(0))), + result + ); + } + addedFunctions.insert(call->target); + auto func = new Function; + func->name = call->target; + func->params.push_back(i32); + func->params.push_back(i32); + func->result = i32; + func->body = builder.makeIf( + builder.makeUnary(EqZInt32, + builder.makeGetLocal(1, i32) + ), + builder.makeConst(Literal(int32_t(0))), + result + ); + wasm.addFunction(func); + } + return call; + } + // Some binary opts might trap, so emit them safely if we are precise - Expression* makeDangerousI64Binary(BinaryOp op, Expression* left, Expression* right) { + Expression* makePotentiallyTrappingI64Binary(BinaryOp op, Expression* left, Expression* right) { if (imprecise) return builder.makeBinary(op, left, right); // we are precise, and the wasm operation might trap if done over 0, so generate a safe call auto *call = allocator.alloc<Call>(); @@ -630,6 +685,27 @@ private: call->type = i64; static std::set<Name> addedFunctions; if (addedFunctions.count(call->target) == 0) { + Expression* result = builder.makeBinary(op, + builder.makeGetLocal(0, i64), + builder.makeGetLocal(1, i64) + ); + if (op == DivSInt64) { + // guard against signed division overflow + result = builder.makeIf( + builder.makeBinary(AndInt32, + builder.makeBinary(EqInt64, + builder.makeGetLocal(0, i64), + builder.makeConst(Literal(std::numeric_limits<int64_t>::min())) + ), + builder.makeBinary(EqInt64, + builder.makeGetLocal(1, i64), + builder.makeConst(Literal(int64_t(-1))) + ) + ), + builder.makeConst(Literal(int64_t(0))), + result + ); + } addedFunctions.insert(call->target); auto func = new Function; func->name = call->target; @@ -641,10 +717,7 @@ private: builder.makeGetLocal(1, i64) ), builder.makeConst(Literal(int64_t(0))), - builder.makeBinary(op, - builder.makeGetLocal(0, i64), - builder.makeGetLocal(1, i64) - ) + result ); wasm.addFunction(func); } @@ -1511,29 +1584,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } else if (!imprecise && (ret->op == BinaryOp::RemSInt32 || ret->op == BinaryOp::RemUInt32 || ret->op == BinaryOp::DivSInt32 || ret->op == BinaryOp::DivUInt32)) { // we are precise, and the wasm operation might trap if done over 0, so generate a safe call - CallImport *call = allocator.alloc<CallImport>(); - switch (ret->op) { - case BinaryOp::RemSInt32: call->target = I32S_REM; break; - case BinaryOp::RemUInt32: call->target = I32U_REM; break; - case BinaryOp::DivSInt32: call->target = I32S_DIV; break; - case BinaryOp::DivUInt32: call->target = I32U_DIV; break; - default: WASM_UNREACHABLE(); - } - call->operands.push_back(ret->left); - call->operands.push_back(ret->right); - call->type = i32; - static std::set<Name> addedImport; - if (addedImport.count(call->target) == 0) { - addedImport.insert(call->target); - auto import = new Import; - import->name = call->target; - import->module = ASM2WASM; - import->base = call->target; - import->functionType = ensureFunctionType("iii", &wasm); - import->kind = ExternalKind::Function; - wasm.addImport(import); - } - return call; + return makePotentiallyTrappingI32Binary(ret->op, ret->left, ret->right); } return ret; } else if (what == SUB) { @@ -1856,10 +1907,10 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { if (name == I64_ADD) return builder.makeBinary(BinaryOp::AddInt64, left, right); if (name == I64_SUB) return builder.makeBinary(BinaryOp::SubInt64, left, right); if (name == I64_MUL) return builder.makeBinary(BinaryOp::MulInt64, left, right); - if (name == I64_UDIV) return makeDangerousI64Binary(BinaryOp::DivUInt64, left, right); - if (name == I64_SDIV) return makeDangerousI64Binary(BinaryOp::DivSInt64, left, right); - if (name == I64_UREM) return makeDangerousI64Binary(BinaryOp::RemUInt64, left, right); - if (name == I64_SREM) return makeDangerousI64Binary(BinaryOp::RemSInt64, left, right); + if (name == I64_UDIV) return makePotentiallyTrappingI64Binary(BinaryOp::DivUInt64, left, right); + if (name == I64_SDIV) return makePotentiallyTrappingI64Binary(BinaryOp::DivSInt64, left, right); + if (name == I64_UREM) return makePotentiallyTrappingI64Binary(BinaryOp::RemUInt64, left, right); + if (name == I64_SREM) return makePotentiallyTrappingI64Binary(BinaryOp::RemSInt64, left, right); if (name == I64_AND) return builder.makeBinary(BinaryOp::AndInt64, left, right); if (name == I64_OR) return builder.makeBinary(BinaryOp::OrInt64, left, right); if (name == I64_XOR) return builder.makeBinary(BinaryOp::XorInt64, left, right); diff --git a/src/js/wasm.js-post.js b/src/js/wasm.js-post.js deleted file mode 100644 index e7a10f49a..000000000 --- a/src/js/wasm.js-post.js +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright 2015 WebAssembly Community Group participants - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -function integrateWasmJS(Module) { - // wasm.js has several methods for creating the compiled code module here: - // * 'native-wasm' : use native WebAssembly support in the browser - // * 'interpret-s-expr': load s-expression code from a .wast and interpret - // * 'interpret-binary': load binary wasm and interpret - // * 'interpret-asm2wasm': load asm.js code, translate to wasm, and interpret - // * 'asmjs': no wasm, just load the asm.js code and use that (good for testing) - // The method can be set at compile time (BINARYEN_METHOD), or runtime by setting Module['wasmJSMethod']. - // The method can be a comma-separated list, in which case, we will try the - // options one by one. Some of them can fail gracefully, and then we can try - // the next. - - // inputs - - var method = Module['wasmJSMethod'] || {{{ wasmJSMethod }}} || 'native-wasm'; // by default, use native support - Module['wasmJSMethod'] = method; - - var wasmTextFile = Module['wasmTextFile'] || {{{ wasmTextFile }}}; - var wasmBinaryFile = Module['wasmBinaryFile'] || {{{ wasmBinaryFile }}}; - var asmjsCodeFile = Module['asmjsCodeFile'] || {{{ asmjsCodeFile }}}; - - // utilities - - var wasmPageSize = 64*1024; - - var asm2wasmImports = { // special asm2wasm imports - "f64-rem": function(x, y) { - return x % y; - }, - "f64-to-int": function(x) { - return x | 0; - }, - "i32s-div": function(x, y) { - return ((x | 0) / (y | 0)) | 0; - }, - "i32u-div": function(x, y) { - return ((x >>> 0) / (y >>> 0)) >>> 0; - }, - "i32s-rem": function(x, y) { - return ((x | 0) % (y | 0)) | 0; - }, - "i32u-rem": function(x, y) { - return ((x >>> 0) % (y >>> 0)) >>> 0; - }, - "debugger": function() { - debugger; - }, - }; - - var info = { - 'global': null, - 'env': null, - 'asm2wasm': asm2wasmImports, - 'parent': Module // Module inside wasm-js.cpp refers to wasm-js.cpp; this allows access to the outside program. - }; - - var exports = null; - - function lookupImport(mod, base) { - var lookup = info; - if (mod.indexOf('.') < 0) { - lookup = (lookup || {})[mod]; - } else { - var parts = mod.split('.'); - lookup = (lookup || {})[parts[0]]; - lookup = (lookup || {})[parts[1]]; - } - if (base) { - lookup = (lookup || {})[base]; - } - if (lookup === undefined) { - abort('bad lookupImport to (' + mod + ').' + base); - } - return lookup; - } - - function mergeMemory(newBuffer) { - // The wasm instance creates its memory. But static init code might have written to - // buffer already, including the mem init file, and we must copy it over in a proper merge. - // TODO: avoid this copy, by avoiding such static init writes - // TODO: in shorter term, just copy up to the last static init write - var oldBuffer = Module['buffer']; - if (newBuffer.byteLength < oldBuffer.byteLength) { - Module['printErr']('the new buffer in mergeMemory is smaller than the previous one. in native wasm, we should grow memory here'); - } - var oldView = new Int8Array(oldBuffer); - var newView = new Int8Array(newBuffer); - - // If we have a mem init file, do not trample it - if (!memoryInitializer) { - oldView.set(newView.subarray(Module['STATIC_BASE'], Module['STATIC_BASE'] + Module['STATIC_BUMP']), Module['STATIC_BASE']); - } - - newView.set(oldView); - updateGlobalBuffer(newBuffer); - updateGlobalBufferViews(); - } - - var WasmTypes = { - none: 0, - i32: 1, - i64: 2, - f32: 3, - f64: 4 - }; - - function fixImports(imports) { - if (!{{{ WASM_BACKEND }}}) return imports; - var ret = {}; - for (var i in imports) { - var fixed = i; - if (fixed[0] == '_') fixed = fixed.substr(1); - ret[fixed] = imports[i]; - } - return ret; - } - - function getBinary() { - var binary; - if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { - binary = Module['wasmBinary']; - assert(binary, "on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)"); - binary = new Uint8Array(binary); - } else { - binary = Module['readBinary'](wasmBinaryFile); - } - return binary; - } - - // do-method functions - - function doJustAsm(global, env, providedBuffer) { - // if no Module.asm, or it's the method handler helper (see below), then apply - // the asmjs - if (typeof Module['asm'] !== 'function' || Module['asm'] === methodHandler) { - if (!Module['asmPreload']) { - // you can load the .asm.js file before this, to avoid this sync xhr and eval - eval(Module['read'](asmjsCodeFile)); // set Module.asm - } else { - Module['asm'] = Module['asmPreload']; - } - } - if (typeof Module['asm'] !== 'function') { - Module['printErr']('asm evalling did not set the module properly'); - return false; - } - return Module['asm'](global, env, providedBuffer); - } - - function doNativeWasm(global, env, providedBuffer) { - if (typeof WebAssembly !== 'object') { - Module['printErr']('no native wasm support detected'); - return false; - } - // prepare memory import - if (!(Module['wasmMemory'] instanceof WebAssembly.Memory)) { - Module['printErr']('no native wasm Memory in use'); - return false; - } - env['memory'] = Module['wasmMemory']; - // Load the wasm module and create an instance of using native support in the JS engine. - info['global'] = { - 'NaN': NaN, - 'Infinity': Infinity - }; - info['global.Math'] = global.Math; - info['env'] = env; - var instance; - try { - instance = new WebAssembly.Instance(new WebAssembly.Module(getBinary()), info) - } catch (e) { - Module['printErr']('failed to compile wasm module: ' + e); - if (e.toString().indexOf('imported Memory with incompatible size') >= 0) { - Module['printErr']('Memory size incompatibility issues may be due to changing TOTAL_MEMORY at runtime to something too large. Use ALLOW_MEMORY_GROWTH to allow any size memory (and also make sure not to set TOTAL_MEMORY at runtime to something smaller than it was at compile time).'); - } - return false; - } - exports = instance.exports; - if (exports.memory) mergeMemory(exports.memory); - - Module["usingWasm"] = true; - - return exports; - } - - function doWasmPolyfill(global, env, providedBuffer, method) { - if (typeof WasmJS !== 'function') { - Module['printErr']('WasmJS not detected - polyfill not bundled?'); - return false; - } - - // Use wasm.js to polyfill and execute code in a wasm interpreter. - var wasmJS = WasmJS({}); - - // XXX don't be confused. Module here is in the outside program. wasmJS is the inner wasm-js.cpp. - wasmJS['outside'] = Module; // Inside wasm-js.cpp, Module['outside'] reaches the outside module. - - // Information for the instance of the module. - wasmJS['info'] = info; - - wasmJS['lookupImport'] = lookupImport; - - assert(providedBuffer === Module['buffer']); // we should not even need to pass it as a 3rd arg for wasm, but that's the asm.js way. - - info.global = global; - info.env = env; - - // polyfill interpreter expects an ArrayBuffer - assert(providedBuffer === Module['buffer']); - env['memory'] = providedBuffer; - assert(env['memory'] instanceof ArrayBuffer); - - wasmJS['providedTotalMemory'] = Module['buffer'].byteLength; - - // Prepare to generate wasm, using either asm2wasm or s-exprs - var code; - if (method === 'interpret-binary') { - code = getBinary(); - } else { - code = Module['read'](method == 'interpret-asm2wasm' ? asmjsCodeFile : wasmTextFile); - } - var temp; - if (method == 'interpret-asm2wasm') { - temp = wasmJS['_malloc'](code.length + 1); - wasmJS['writeAsciiToMemory'](code, temp); - wasmJS['_load_asm2wasm'](temp); - } else if (method === 'interpret-s-expr') { - temp = wasmJS['_malloc'](code.length + 1); - wasmJS['writeAsciiToMemory'](code, temp); - wasmJS['_load_s_expr2wasm'](temp); - } else if (method === 'interpret-binary') { - temp = wasmJS['_malloc'](code.length); - wasmJS['HEAPU8'].set(code, temp); - wasmJS['_load_binary2wasm'](temp, code.length); - } else { - throw 'what? ' + method; - } - wasmJS['_free'](temp); - - wasmJS['_instantiate'](temp); - - if (Module['newBuffer']) { - mergeMemory(Module['newBuffer']); - Module['newBuffer'] = null; - } - - exports = wasmJS['asmExports']; - - return exports; - } - - // We may have a preloaded value in Module.asm, save it - Module['asmPreload'] = Module['asm']; - - // Memory growth integration code - Module['reallocBuffer'] = function(size) { - size = Math.ceil(size / wasmPageSize) * wasmPageSize; // round up to wasm page size - var old = Module['buffer']; - var result = exports['__growWasmMemory'](size / wasmPageSize); // tiny wasm method that just does grow_memory - if (Module["usingWasm"]) { - if (result !== (-1 | 0)) { - // success in native wasm memory growth, get the buffer from the memory - return Module['buffer'] = Module['wasmMemory'].buffer; - } else { - return null; - } - } else { - // in interpreter, we replace Module.buffer if we allocate - return Module['buffer'] !== old ? Module['buffer'] : null; // if it was reallocated, it changed - } - }; - - // Provide an "asm.js function" for the application, called to "link" the asm.js module. We instantiate - // the wasm module at that time, and it receives imports and provides exports and so forth, the app - // doesn't need to care that it is wasm or olyfilled wasm or asm.js. - - Module['asm'] = function(global, env, providedBuffer) { - global = fixImports(global); - env = fixImports(env); - - // import table - if (!env['table']) { - var TABLE_SIZE = Module['wasmTableSize']; - if (TABLE_SIZE === undefined) TABLE_SIZE = 1024; // works in binaryen interpreter at least - var MAX_TABLE_SIZE = Module['wasmMaxTableSize']; - if (typeof WebAssembly === 'object' && typeof WebAssembly.Table === 'function') { - if (MAX_TABLE_SIZE !== undefined) { - env['table'] = new WebAssembly.Table({ initial: TABLE_SIZE, maximum: MAX_TABLE_SIZE, element: 'anyfunc' }); - } else { - env['table'] = new WebAssembly.Table({ initial: TABLE_SIZE, element: 'anyfunc' }); - } - } else { - env['table'] = new Array(TABLE_SIZE); // works in binaryen interpreter at least - } - Module['wasmTable'] = env['table']; - } - - if (!env['memoryBase']) { - env['memoryBase'] = Module['STATIC_BASE']; // tell the memory segments where to place themselves - } - if (!env['tableBase']) { - env['tableBase'] = 0; // table starts at 0 by default, in dynamic linking this will change - } - - // try the methods. each should return the exports if it succeeded - - var exports; - var methods = method.split(','); - - for (var i = 0; i < methods.length; i++) { - var curr = methods[i]; - - Module['printErr']('trying binaryen method: ' + curr); - - if (curr === 'native-wasm') { - if (exports = doNativeWasm(global, env, providedBuffer)) break; - } else if (curr === 'asmjs') { - if (exports = doJustAsm(global, env, providedBuffer)) break; - } else if (curr === 'interpret-asm2wasm' || curr === 'interpret-s-expr' || curr === 'interpret-binary') { - if (exports = doWasmPolyfill(global, env, providedBuffer, curr)) break; - } else { - throw 'bad method: ' + curr; - } - } - - if (!exports) throw 'no binaryen method succeeded. consider enabling more options, like interpreting, if you want that: https://github.com/kripken/emscripten/wiki/WebAssembly#binaryen-methods'; - - Module['printErr']('binaryen method succeeded.'); - - return exports; - }; - - var methodHandler = Module['asm']; // note our method handler, as we may modify Module['asm'] later -} - |