diff options
author | Alon Zakai <alonzakai@gmail.com> | 2016-03-13 22:05:35 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2016-03-13 22:05:35 -0700 |
commit | 48a12f0cf134a15434432954c56c893ea1fc6eca (patch) | |
tree | eceaab691e14a7a88f2d2c532a87d589f3dcff19 /src | |
parent | a1287af38f12b5ba3d2874424f67289fe1ea49e7 (diff) | |
parent | 8de76ffa8588a72788f3967806df43d4d4e43453 (diff) | |
download | binaryen-48a12f0cf134a15434432954c56c893ea1fc6eca.tar.gz binaryen-48a12f0cf134a15434432954c56c893ea1fc6eca.tar.bz2 binaryen-48a12f0cf134a15434432954c56c893ea1fc6eca.zip |
Merge pull request #244 from WebAssembly/interpret-binary
wasm-binary method
Diffstat (limited to 'src')
-rw-r--r-- | src/js/wasm.js-post.js | 68 | ||||
-rw-r--r-- | src/wasm-binary.h | 75 | ||||
-rw-r--r-- | src/wasm-js.cpp | 43 |
3 files changed, 131 insertions, 55 deletions
diff --git a/src/js/wasm.js-post.js b/src/js/wasm.js-post.js index e3ab2bb4f..12e9e5315 100644 --- a/src/js/wasm.js-post.js +++ b/src/js/wasm.js-post.js @@ -17,8 +17,9 @@ function integrateWasmJS(Module) { // wasm.js has several methods for creating the compiled code module here: // * 'native-wasm' : use native WebAssembly support in the browser - // * 'wasm-s-parser': load s-expression code from a .wast and create wasm - // * 'asm2wasm': load asm.js code and translate to wasm + // * 'wasm-s-parser': load s-expression code from a .wast and interpret + // * 'wasm-binary': load binary wasm and interpret + // * 'asm2wasm': load asm.js code, translate to wasm, and interpret // * 'just-asm': 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 @@ -29,8 +30,8 @@ function integrateWasmJS(Module) { var method = Module['wasmJSMethod'] || {{{ wasmJSMethod }}} || 'native-wasm,wasm-s-parser'; // by default, try native and then .wast - var wasmCodeFile = Module['wasmCodeFile'] || {{{ wasmCodeFile }}}; - + var wasmTextFile = Module['wasmTextFile'] || {{{ wasmTextFile }}}; + var wasmBinaryFile = Module['wasmBinaryFile'] || {{{ wasmBinaryFile }}}; var asmjsCodeFile = Module['asmjsCodeFile'] || {{{ asmjsCodeFile }}}; // utilities @@ -109,8 +110,8 @@ function integrateWasmJS(Module) { // be present in the wasm output of asm2wasm, so we store it in a side file. If we load asm2wasm // output, either generated ahead of time or on the client, we need to apply those mapped // globals after loading the module. - function applyMappedGlobals() { - var mappedGlobals = JSON.parse(Module['read'](wasmCodeFile + '.mappedGlobals')); + function applyMappedGlobals(globalsFileBase) { + var mappedGlobals = JSON.parse(Module['read'](globalsFileBase + '.mappedGlobals')); for (var name in mappedGlobals) { var global = mappedGlobals[name]; if (!global.import) continue; // non-imports are initialized to zero in the typed array anyhow, so nothing to do here @@ -136,6 +137,18 @@ function integrateWasmJS(Module) { 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() { @@ -161,16 +174,7 @@ function integrateWasmJS(Module) { global = fixImports(global); env = fixImports(env); - // Load the wasm module - 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'](wasmCodeFile); - } - // Create an instance of the module using native support in the JS engine. + // Load the wasm module and create an instance of using native support in the JS engine. info['global'] = { 'NaN': NaN, 'Infinity': Infinity @@ -178,10 +182,10 @@ function integrateWasmJS(Module) { info['global.Math'] = global.Math; info['env'] = env; var instance; - instance = Wasm.instantiateModule(binary, info); + instance = Wasm.instantiateModule(getBinary(), info); mergeMemory(instance.exports.memory); - applyMappedGlobals(); + applyMappedGlobals(wasmBinaryFile); return instance.exports; }; @@ -226,13 +230,27 @@ function integrateWasmJS(Module) { wasmJS['providedTotalMemory'] = Module['buffer'].byteLength; // Prepare to generate wasm, using either asm2wasm or wasm-s-parser - var code = Module['read'](method == 'asm2wasm' ? asmjsCodeFile : wasmCodeFile); - var temp = wasmJS['_malloc'](code.length + 1); - wasmJS['writeAsciiToMemory'](code, temp); + var code; + if (method === 'wasm-binary') { + code = getBinary(); + } else { + code = Module['read'](method == 'asm2wasm' ? asmjsCodeFile : wasmTextFile); + } + var temp; if (method == 'asm2wasm') { + temp = wasmJS['_malloc'](code.length + 1); + wasmJS['writeAsciiToMemory'](code, temp); wasmJS['_load_asm2wasm'](temp); - } else { + } else if (method === 'wasm-s-parser') { + temp = wasmJS['_malloc'](code.length + 1); + wasmJS['writeAsciiToMemory'](code, temp); wasmJS['_load_s_expr2wasm'](temp); + } else if (method === 'wasm-binary') { + temp = wasmJS['_malloc'](code.length); + wasmJS['HEAPU8'].set(code, temp); + wasmJS['_load_binary2wasm'](temp, code.length); + } else { + throw 'what? ' + method; } wasmJS['_free'](temp); @@ -244,7 +262,9 @@ function integrateWasmJS(Module) { } if (method == 'wasm-s-parser') { - applyMappedGlobals(); + applyMappedGlobals(wasmTextFile); + } else if (method == 'wasm-binary') { + applyMappedGlobals(wasmBinaryFile); } return wasmJS['asmExports']; @@ -263,7 +283,7 @@ function integrateWasmJS(Module) { if (doNativeWasm()) return; } else if (curr === 'just-asm') { if (doJustAsm()) return; - } else if (curr === 'asm2wasm' || curr === 'wasm-s-parser') { + } else if (curr === 'asm2wasm' || curr === 'wasm-s-parser' || curr === 'wasm-binary') { if (doWasmPolyfill(curr)) return; } else { throw 'bad method: ' + curr; diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 715c06287..081924ba0 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -522,7 +522,7 @@ public: o << int8_t(BinaryConsts::EndMarker); assert(depth == 0); size_t size = o.size() - start; - assert(size <= std::numeric_limits<uint16_t>::max()); + assert(size <= std::numeric_limits<uint32_t>::max()); if (debug) std::cerr << "body size: " << size << ", writing at " << sizePos << ", next starts at " << o.size() << std::endl; o.writeAt(sizePos, uint32_t(size)); // XXX int32, diverge from v8 format, to get more code to compile } else { @@ -650,7 +650,7 @@ public: o << int8_t(BinaryConsts::EndMarker); } - int getBreakIndex(Name name) { // -1 if not found + int32_t getBreakIndex(Name name) { // -1 if not found for (int i = breakStack.size() - 1; i >= 0; i--) { if (breakStack[i] == name) { return breakStack.size() - 1 - i; @@ -669,15 +669,15 @@ public: } if (curr->condition) recurse(curr->condition); o << int8_t(curr->condition ? BinaryConsts::BrIf : BinaryConsts::Br) - << int8_t(getBreakIndex(curr->name)); + << int32_t(getBreakIndex(curr->name)); } void visitSwitch(Switch *curr) { if (debug) std::cerr << "zz node: Switch" << std::endl; o << int8_t(BinaryConsts::TableSwitch) << int16_t(curr->targets.size() + 1) << int8_t(curr->value != nullptr); for (auto target : curr->targets) { - o << (int16_t)getBreakIndex(target); + o << (int32_t)getBreakIndex(target); } - o << (int16_t)getBreakIndex(curr->default_); + o << (int32_t)getBreakIndex(curr->default_); recurse(curr->condition); o << int8_t(BinaryConsts::EndMarker); if (curr->value) { @@ -1079,6 +1079,12 @@ public: assert(x == y); } + void ungetInt8() { + assert(pos > 0); + if (debug) std::cerr << "ungetInt8 (at " << pos << ")" << std::endl; + pos--; + } + void readStart() { if (debug) std::cerr << "== readStart" << std::endl; wasm.start = getString(); @@ -1335,19 +1341,44 @@ public: void visitBlock(Block *curr) { if (debug) std::cerr << "zz node: Block" << std::endl; - curr->name = getNextLabel(); - breakStack.push_back(curr->name); - size_t start = expressionStack.size(); // everything after this, that is left when we see the marker, is ours - processExpressions(); - size_t end = expressionStack.size(); - assert(end >= start); - for (size_t i = start; i < end; i++) { - if (debug) std::cerr << " " << size_t(expressionStack[i]) << "\n zz Block element " << curr->list.size() << std::endl; - curr->list.push_back(expressionStack[i]); + // special-case Block and de-recurse nested blocks in their first position, as that is + // a common pattern that can be very highly nested. + std::vector<Block*> stack; + while (1) { + curr->name = getNextLabel(); + breakStack.push_back(curr->name); + stack.push_back(curr); + if (getInt8() == BinaryConsts::Block) { + // a recursion + curr = allocator.alloc<Block>(); + continue; + } else { + // end of recursion + ungetInt8(); + break; + } + } + Block* last = nullptr; + while (stack.size() > 0) { + curr = stack.back(); + stack.pop_back(); + size_t start = expressionStack.size(); // everything after this, that is left when we see the marker, is ours + if (last) { + // the previous block is our first-position element + expressionStack.push_back(last); + } + last = curr; + processExpressions(); + size_t end = expressionStack.size(); + assert(end >= start); + for (size_t i = start; i < end; i++) { + if (debug) std::cerr << " " << size_t(expressionStack[i]) << "\n zz Block element " << curr->list.size() << std::endl; + curr->list.push_back(expressionStack[i]); + } + expressionStack.resize(start); + curr->finalize(); + breakStack.pop_back(); } - expressionStack.resize(start); - curr->finalize(); - breakStack.pop_back(); } void visitIf(If *curr, uint8_t code) { if (debug) std::cerr << "zz node: If" << std::endl; @@ -1374,14 +1405,14 @@ public: curr->finalize(); } - Name getBreakName(int offset) { + Name getBreakName(int32_t offset) { assert(breakStack.size() - 1 - offset < breakStack.size()); return breakStack[breakStack.size() - 1 - offset]; } void visitBreak(Break *curr, uint8_t code) { if (debug) std::cerr << "zz node: Break" << std::endl; - curr->name = getBreakName(getInt8()); + curr->name = getBreakName(getInt32()); if (code == BinaryConsts::BrIf) curr->condition = popExpression(); curr->value = popExpression(); } @@ -1390,9 +1421,9 @@ public: auto numTargets = getInt16(); auto hasValue = getInt8(); for (auto i = 0; i < numTargets - 1; i++) { - curr->targets.push_back(getBreakName(getInt16())); + curr->targets.push_back(getBreakName(getInt32())); } - curr->default_ = getBreakName(getInt16()); + curr->default_ = getBreakName(getInt32()); processExpressions(); curr->condition = popExpression(); if (hasValue) { @@ -1624,6 +1655,7 @@ public: if (debug) std::cerr << "zz node: Binary" << std::endl; curr->right = popExpression(); curr->left = popExpression(); + curr->finalize(); return true; #undef TYPED_CODE #undef INT_TYPED_CODE @@ -1656,6 +1688,7 @@ public: default: return false; } if (debug) std::cerr << "zz node: Host" << std::endl; + curr->finalize(); return true; } void visitNop(Nop *curr) { diff --git a/src/wasm-js.cpp b/src/wasm-js.cpp index 000092da8..3083b3b9e 100644 --- a/src/wasm-js.cpp +++ b/src/wasm-js.cpp @@ -26,6 +26,7 @@ #include "asm2wasm.h" #include "wasm-interpreter.h" #include "wasm-s-parser.h" +#include "wasm-binary.h" #include "wasm-printing.h" using namespace cashew; @@ -100,8 +101,22 @@ extern "C" void EMSCRIPTEN_KEEPALIVE load_asm2wasm(char *input) { } } +void finalizeModule() { + uint32_t providedMemory = EM_ASM_INT_V({ + return Module['providedTotalMemory']; // we receive the size of memory from emscripten + }); + if (providedMemory & ~Memory::kPageMask) { + std::cerr << "Error: provided memory is not a multiple of the 64k wasm page size\n"; + exit(EXIT_FAILURE); + } + module->memory.initial = providedMemory / Memory::kPageSize; + module->memory.max = (module->exportsMap.find(GROW_WASM_MEMORY) != module->exportsMap.end()) ? -1 : module->memory.initial; + + // global mapping is done in js in post.js +} + // loads wasm code in s-expression format -extern "C" void EMSCRIPTEN_KEEPALIVE load_s_expr2wasm(char *input, char *mappedGlobals) { +extern "C" void EMSCRIPTEN_KEEPALIVE load_s_expr2wasm(char *input) { prepare2wasm(); if (wasmJSDebug) std::cerr << "wasm-s-expression parsing...\n"; @@ -119,17 +134,25 @@ extern "C" void EMSCRIPTEN_KEEPALIVE load_s_expr2wasm(char *input, char *mappedG abort(); }); - uint32_t providedMemory = EM_ASM_INT_V({ - return Module['providedTotalMemory']; // we receive the size of memory from emscripten - }); - if (providedMemory & ~Memory::kPageMask) { - std::cerr << "Error: provided memory is not a multiple of the 64k wasm page size\n"; - exit(EXIT_FAILURE); + finalizeModule(); +} + +// loads wasm code in binary format +extern "C" void EMSCRIPTEN_KEEPALIVE load_binary2wasm(char *raw, int32_t size) { + prepare2wasm(); + + if (wasmJSDebug) std::cerr << "wasm-binary parsing...\n"; + + module = new AllocatingModule(); + std::vector<char> input; + input.resize(size); + for (int32_t i = 0; i < size; i++) { + input[i] = raw[i]; } - module->memory.initial = providedMemory / Memory::kPageSize; - module->memory.max = (module->exportsMap.find(GROW_WASM_MEMORY) != module->exportsMap.end()) ? -1 : module->memory.initial; + WasmBinaryBuilder parser(*module, input, debug); + parser.read(); - // global mapping is done in js in post.js + finalizeModule(); } // instantiates the loaded wasm (which might be from asm2wasm, or |