summaryrefslogtreecommitdiff
path: root/src/wasm-js.cpp
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2019-01-08 14:59:51 -0800
committerGitHub <noreply@github.com>2019-01-08 14:59:51 -0800
commitaf5adae23e7cfcc2c933d72142a3d58576af769d (patch)
tree3c37dfdcfac20fb61f95ffedeb653dfa249e12fd /src/wasm-js.cpp
parent34fbbb3dde9f17a83ed63264d1165ebb7a66ddc7 (diff)
downloadbinaryen-af5adae23e7cfcc2c933d72142a3d58576af769d.tar.gz
binaryen-af5adae23e7cfcc2c933d72142a3d58576af769d.tar.bz2
binaryen-af5adae23e7cfcc2c933d72142a3d58576af769d.zip
Remove interp and fix tests (#1858)
Updates tests to the latest notation changes, and also remove wasm.js (see kripken/emscripten#7831 ) as we'd need to either rebuild it or update it for the new notation as well, and it's not used at this point.
Diffstat (limited to 'src/wasm-js.cpp')
-rw-r--r--src/wasm-js.cpp548
1 files changed, 0 insertions, 548 deletions
diff --git a/src/wasm-js.cpp b/src/wasm-js.cpp
deleted file mode 100644
index 0ea00d9f9..000000000
--- a/src/wasm-js.cpp
+++ /dev/null
@@ -1,548 +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.
- */
-
-//
-// WebAssembly intepreter for asm2wasm output, in a js environment.
-//
-// Receives asm.js, generates a runnable module that executes the code in a WebAssembly
-// interpreter. This is suitable as a polyfill for WebAssembly support in browsers.
-//
-
-#include <emscripten.h>
-
-#include "asm2wasm.h"
-#include "wasm-interpreter.h"
-#include "wasm-s-parser.h"
-#include "wasm-binary.h"
-#include "wasm-printing.h"
-#include "ir/module-utils.h"
-
-using namespace cashew;
-using namespace wasm;
-
-namespace wasm {
-
-int debug = 0;
-
-}
-
-// global singletons
-Asm2WasmBuilder* asm2wasm = nullptr;
-SExpressionParser* sExpressionParser = nullptr;
-SExpressionWasmBuilder* sExpressionWasmBuilder = nullptr;
-ModuleInstance* instance = nullptr;
-Module* module = nullptr;
-bool wasmJSDebug = false;
-
-static void prepare2wasm() {
- assert(asm2wasm == nullptr && sExpressionParser == nullptr && sExpressionWasmBuilder == nullptr && instance == nullptr); // singletons
-#if WASM_JS_DEBUG
- wasmJSDebug = 1;
-#else
- wasmJSDebug = EM_ASM_INT_V({ return !!Module['outside']['WASM_JS_DEBUG'] }); // Set WASM_JS_DEBUG on the outside Module to get debugging
-#endif
-}
-
-// receives asm.js code, parses into wasm.
-// note: this modifies the input.
-extern "C" void EMSCRIPTEN_KEEPALIVE load_asm2wasm(char *input) {
- prepare2wasm();
-
- Asm2WasmPreProcessor pre;
- pre.debugInfo = true; // FIXME: we must do this, as the input asm.js might have debug info
- input = pre.process(input);
-
- // proceed to parse and wasmify
- if (wasmJSDebug) std::cerr << "asm parsing...\n";
-
- cashew::Parser<Ref, DotZeroValueBuilder> builder;
- Ref asmjs = builder.parseToplevel(input);
-
- module = new Module();
- 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 = Address(providedMemory / Memory::kPageSize);
- module->memory.max = pre.memoryGrowth ? Address(Memory::kUnlimitedSize) : module->memory.initial;
-
- if (wasmJSDebug) std::cerr << "wasming...\n";
- asm2wasm = new Asm2WasmBuilder(*module, pre, debug, TrapMode::JS, PassOptions(), true /* runJSFFIPass */, false /* TODO: support optimizing? */, false /* TODO: support asm2wasm-i64? */);
- asm2wasm->processAsm(asmjs);
-}
-
-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 = Address(providedMemory / Memory::kPageSize);
- module->memory.max = module->getExportOrNull(GROW_WASM_MEMORY) ? Address(Memory::kUnlimitedSize) : 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) {
- prepare2wasm();
-
- if (wasmJSDebug) std::cerr << "wasm-s-expression parsing...\n";
-
- sExpressionParser = new SExpressionParser(input);
- Element& root = *sExpressionParser->root;
- if (wasmJSDebug) std::cout << root << '\n';
-
- if (wasmJSDebug) std::cerr << "wasming...\n";
-
- module = new Module();
- // A .wast may have multiple modules, with some asserts after them, but we just read the first here.
- sExpressionWasmBuilder = new SExpressionWasmBuilder(*module, *root[0]);
-
- 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 Module();
- std::vector<char> input;
- input.resize(size);
- for (int32_t i = 0; i < size; i++) {
- input[i] = raw[i];
- }
- WasmBinaryBuilder parser(*module, input, debug);
- parser.read();
-
- finalizeModule();
-}
-
-// instantiates the loaded wasm (which might be from asm2wasm, or
-// s-expressions, or something else) with a JS external interface.
-extern "C" void EMSCRIPTEN_KEEPALIVE instantiate() {
- if (wasmJSDebug) std::cerr << "instantiating module: \n" << module << '\n';
-
- if (wasmJSDebug) std::cerr << "generating exports...\n";
-
- EM_ASM({
- Module['asmExports'] = {};
- });
- for (auto& curr : module->exports) {
- if (curr->kind == ExternalKind::Function) {
- EM_ASM_({
- var name = Pointer_stringify($0);
- Module['asmExports'][name] = function() {
- Module['tempArguments'] = Array.prototype.slice.call(arguments);
- Module['_call_from_js']($0);
- return Module['tempReturn'];
- };
- }, curr->name.str);
- }
- }
-
- auto verifyImportIsProvided = [&](Importable* import) {
- EM_ASM_({
- var mod = Pointer_stringify($0);
- var base = Pointer_stringify($1);
- assert(Module['lookupImport'](mod, base) !== undefined, 'checking import ' + mod + '.' + base);
- }, import->module.str, import->base.str);
- };
- ModuleUtils::iterImportedFunctions(*module, verifyImportIsProvided);
- ModuleUtils::iterImportedGlobals(*module, verifyImportIsProvided);
-
- if (wasmJSDebug) std::cerr << "creating instance...\n";
-
- struct JSExternalInterface : ModuleInstance::ExternalInterface {
- Module* module = nullptr;
-
- void init(Module& wasm, ModuleInstance& instance) override {
- module = &wasm;
- // look for imported memory
- if (wasm.memory.imported()) {
- EM_ASM({
- Module['asmExports']['memory'] = Module['lookupImport']('env', 'memory');
- });
- } else {
- // no memory import; create a new buffer here, just like native wasm support would.
- EM_ASM_({
- Module['asmExports']['memory'] = Module['outside']['newBuffer'] = new ArrayBuffer($0);
- }, wasm.memory.initial * Memory::kPageSize);
- }
- for (auto segment : wasm.memory.segments) {
- EM_ASM_({
- var source = Module['HEAP8'].subarray($1, $1 + $2);
- var target = new Int8Array(Module['asmExports']['memory']);
- target.set(source, $0);
- }, ConstantExpressionRunner<TrivialGlobalManager>(instance.globals).visit(segment.offset).value.geti32(), &segment.data[0], segment.data.size());
- }
- // look for imported table
- if (wasm.table.imported()) {
- EM_ASM({
- Module['outside']['wasmTable'] = Module['lookupImport']('env', 'table');
- });
- } else {
- // no table import; create a new one here, just like native wasm support would.
- EM_ASM_({
- Module['outside']['wasmTable'] = new Array($0);
- }, wasm.table.initial);
- }
- EM_ASM({
- Module['asmExports']['table'] = Module['outside']['wasmTable'];
- });
- // Emulated table support is in a JS array. If the entry is a number, it's a function pointer. If not, it's a JS method to be called directly
- // TODO: make them all JS methods, wrapping a dynCall where necessary?
- for (auto segment : wasm.table.segments) {
- Address offset = ConstantExpressionRunner<TrivialGlobalManager>(instance.globals).visit(segment.offset).value.geti32();
- assert(offset + segment.data.size() <= wasm.table.initial);
- for (size_t i = 0; i != segment.data.size(); ++i) {
- Name name = segment.data[i];
- auto* func = wasm.getFunction(name);
- if (!func->imported()) {
- EM_ASM_({
- Module['outside']['wasmTable'][$0] = $1;
- }, offset + i, func);
- } else {
- EM_ASM_({
- Module['outside']['wasmTable'][$0] = Module['lookupImport'](Pointer_stringify($1), Pointer_stringify($2));
- }, offset + i, func->module.str, func->base.str);
- }
- }
- }
- }
-
- void prepareTempArgments(LiteralList& arguments) {
- EM_ASM({
- Module['tempArguments'] = [];
- });
- for (auto& argument : arguments) {
- if (argument.type == i32) {
- EM_ASM_({ Module['tempArguments'].push($0) }, argument.geti32());
- } else if (argument.type == f32) {
- EM_ASM_({ Module['tempArguments'].push($0) }, argument.getf32());
- } else if (argument.type == f64) {
- EM_ASM_({ Module['tempArguments'].push($0) }, argument.getf64());
- } else {
- abort();
- }
- }
- }
-
- Literal getResultFromJS(double ret, Type type) {
- switch (type) {
- case none: return Literal();
- case i32: return Literal((int32_t)ret);
- case i64: WASM_UNREACHABLE();
- case f32: return Literal((float)ret);
- case f64: return Literal((double)ret);
- case v128: assert(false && "v128 not implemented yet");
- case unreachable: WASM_UNREACHABLE();
- }
- WASM_UNREACHABLE();
- }
-
- void importGlobals(std::map<Name, Literal>& globals, Module& wasm) override {
- ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) {
- double ret = EM_ASM_DOUBLE({
- var mod = Pointer_stringify($0);
- var base = Pointer_stringify($1);
- var lookup = Module['lookupImport'](mod, base);
- return lookup;
- }, import->module.str, import->base.str);
-
- if (wasmJSDebug) std::cout << "calling importGlobal for " << import->name << " returning " << ret << '\n';
-
- globals[import->name] = getResultFromJS(ret, import->type);
- });
- }
-
- Literal callImport(Function *import, LiteralList& arguments) override {
- if (wasmJSDebug) std::cout << "calling import " << import->name.str << '\n';
- prepareTempArgments(arguments);
- double ret = EM_ASM_DOUBLE({
- var mod = Pointer_stringify($0);
- var base = Pointer_stringify($1);
- var tempArguments = Module['tempArguments'];
- Module['tempArguments'] = null;
- var lookup = Module['lookupImport'](mod, base);
- return lookup.apply(null, tempArguments);
- }, import->module.str, import->base.str);
-
- if (wasmJSDebug) std::cout << "calling import returning " << ret << " and function type is " << module->getFunctionType(import->type)->result << '\n';
-
- return getResultFromJS(ret, module->getFunctionType(import->type)->result);
- }
-
- Literal callTable(Index index, LiteralList& arguments, Type result, ModuleInstance& instance) override {
- void* ptr = (void*)EM_ASM_INT({
- var value = Module['outside']['wasmTable'][$0];
- return typeof value === "number" ? value : -1;
- }, index);
- if (ptr == nullptr) trap("callTable overflow");
- if (ptr != (void*)-1) {
- // a Function we can call
- Function* func = (Function*)ptr;
- if (func->params.size() != arguments.size()) trap("callIndirect: bad # of arguments");
- for (size_t i = 0; i < func->params.size(); i++) {
- if (func->params[i] != arguments[i].type) {
- trap("callIndirect: bad argument type");
- }
- }
- return instance.callFunctionInternal(func->name, arguments);
- } else {
- // A JS function JS can call
- prepareTempArgments(arguments);
- double ret = EM_ASM_DOUBLE({
- var func = Module['outside']['wasmTable'][$0];
- var tempArguments = Module['tempArguments'];
- Module['tempArguments'] = null;
- return func.apply(null, tempArguments);
- }, index);
- return getResultFromJS(ret, result);
- }
- }
-
- Literal load(Load* load, Address address) override {
- uint32_t addr = address;
- if (load->align < load->bytes || (addr & (load->bytes-1))) {
- int64_t out64;
- double ret = EM_ASM_DOUBLE({
- var addr = $0;
- var bytes = $1;
- var isFloat = $2;
- var isSigned = $3;
- var out64 = $4;
- var save0 = HEAP32[0];
- var save1 = HEAP32[1];
- for (var i = 0; i < bytes; i++) {
- HEAPU8[i] = Module["info"].parent["HEAPU8"][addr + i];
- }
- var ret;
- if (!isFloat) {
- if (bytes === 1) ret = isSigned ? HEAP8[0] : HEAPU8[0];
- else if (bytes === 2) ret = isSigned ? HEAP16[0] : HEAPU16[0];
- else if (bytes === 4) ret = isSigned ? HEAP32[0] : HEAPU32[0];
- else if (bytes === 8) {
- for (var i = 0; i < bytes; i++) {
- HEAPU8[out64 + i] = HEAPU8[i];
- }
- } else abort();
- } else {
- if (bytes === 4) ret = HEAPF32[0];
- else if (bytes === 8) ret = HEAPF64[0];
- else abort();
- }
- HEAP32[0] = save0; HEAP32[1] = save1;
- return ret;
- }, (uint32_t)addr, load->bytes, isFloatType(load->type), load->signed_, &out64);
- if (!isFloatType(load->type)) {
- if (load->type == i64) {
- if (load->bytes == 8) {
- return Literal(out64);
- } else {
- if (load->signed_) {
- return Literal(int64_t(int32_t(ret)));
- } else {
- return Literal(int64_t(uint32_t(ret)));
- }
- }
- }
- return Literal((int32_t)ret);
- } else if (load->bytes == 4) {
- return Literal((float)ret);
- } else if (load->bytes == 8) {
- return Literal((double)ret);
- }
- abort();
- }
- // nicely aligned
- if (!isFloatType(load->type)) {
- int64_t ret;
- if (load->bytes == 1) {
- if (load->signed_) {
- ret = EM_ASM_INT({ return Module['info'].parent['HEAP8'][$0] }, addr);
- } else {
- ret = EM_ASM_INT({ return Module['info'].parent['HEAPU8'][$0] }, addr);
- }
- } else if (load->bytes == 2) {
- if (load->signed_) {
- ret = EM_ASM_INT({ return Module['info'].parent['HEAP16'][$0 >> 1] }, addr);
- } else {
- ret = EM_ASM_INT({ return Module['info'].parent['HEAPU16'][$0 >> 1] }, addr);
- }
- } else if (load->bytes == 4) {
- if (load->signed_) {
- ret = EM_ASM_INT({ return Module['info'].parent['HEAP32'][$0 >> 2] }, addr);
- } else {
- ret = uint32_t(EM_ASM_INT({ return Module['info'].parent['HEAPU32'][$0 >> 2] }, addr));
- }
- } else if (load->bytes == 8) {
- uint32_t low = EM_ASM_INT({ return Module['info'].parent['HEAP32'][$0 >> 2] }, addr);
- uint32_t high = EM_ASM_INT({ return Module['info'].parent['HEAP32'][$0 >> 2] }, addr + 4);
- ret = uint64_t(low) | (uint64_t(high) << 32);
- } else abort();
- return load->type == i32 ? Literal(int32_t(ret)) : Literal(ret);
- } else {
- if (load->bytes == 4) {
- return Literal((float)EM_ASM_DOUBLE({ return Module['info'].parent['HEAPF32'][$0 >> 2] }, addr));
- } else if (load->bytes == 8) {
- return Literal(EM_ASM_DOUBLE({ return Module['info'].parent['HEAPF64'][$0 >> 3] }, addr));
- }
- abort();
- }
- }
-
- void store(Store* store_, Address address, Literal value) override {
- uint32_t addr = address;
- // support int64 stores
- if (value.type == Type::i64 && store_->bytes == 8) {
- Store fake = *store_;
- fake.bytes = 4;
- fake.type = i32;
- uint64_t v = value.geti64();
- store(&fake, addr, Literal(uint32_t(v)));
- v >>= 32;
- store(&fake, addr + 4, Literal(uint32_t(v)));
- return;
- }
- // normal non-int64 value
- if (store_->align < store_->bytes || (addr & (store_->bytes-1))) {
- EM_ASM_DOUBLE({
- var addr = $0;
- var bytes = $1;
- var isFloat = $2;
- var value = $3;
- var save0 = HEAP32[0];
- var save1 = HEAP32[1];
- if (!isFloat) {
- if (bytes === 1) HEAPU8[0] = value;
- else if (bytes === 2) HEAPU16[0] = value;
- else if (bytes === 4) HEAPU32[0] = value;
- else abort();
- } else {
- if (bytes === 4) HEAPF32[0] = value;
- else if (bytes === 8) HEAPF64[0] = value;
- else abort();
- }
- for (var i = 0; i < bytes; i++) {
- Module["info"].parent["HEAPU8"][addr + i] = HEAPU8[i];
- }
- HEAP32[0] = save0; HEAP32[1] = save1;
- }, (uint32_t)addr, store_->bytes, isFloatType(store_->valueType), isFloatType(store_->valueType) ? value.getFloat() : (double)value.getInteger());
- return;
- }
- // nicely aligned
- if (!isFloatType(store_->valueType)) {
- if (store_->bytes == 1) {
- EM_ASM_INT({ Module['info'].parent['HEAP8'][$0] = $1 }, addr, (uint32_t)value.getInteger());
- } else if (store_->bytes == 2) {
- EM_ASM_INT({ Module['info'].parent['HEAP16'][$0 >> 1] = $1 }, addr, (uint32_t)value.getInteger());
- } else if (store_->bytes == 4) {
- EM_ASM_INT({ Module['info'].parent['HEAP32'][$0 >> 2] = $1 }, addr, (uint32_t)value.getInteger());
- } else {
- abort();
- }
- } else {
- if (store_->bytes == 4) {
- EM_ASM_DOUBLE({ Module['info'].parent['HEAPF32'][$0 >> 2] = $1 }, addr, value.getf32());
- } else if (store_->bytes == 8) {
- EM_ASM_DOUBLE({ Module['info'].parent['HEAPF64'][$0 >> 3] = $1 }, addr, value.getf64());
- } else {
- abort();
- }
- }
- }
-
- void growMemory(Address oldSize, Address newSize) override {
- EM_ASM_({
- var size = $0;
- var buffer;
- try {
- buffer = new ArrayBuffer(size);
- } catch(e) {
- // fail to grow memory. post.js notices this since the buffer is unchanged
- return;
- }
- var oldHEAP8 = Module['outside']['HEAP8'];
- var temp = new Int8Array(buffer);
- temp.set(oldHEAP8);
- Module['outside']['buffer'] = buffer;
- }, (uint32_t)newSize);
- }
-
- void trap(const char* why) override {
- EM_ASM_({
- abort("wasm trap: " + Pointer_stringify($0));
- }, why);
- }
- };
-
- instance = new ModuleInstance(*module, new JSExternalInterface());
-
- // stack trace hooks
- EM_ASM({
- Module['outside']['extraStackTrace'] = function() {
- return Pointer_stringify(Module['_interpreter_stack_trace']());
- };
- });
-}
-
-extern "C" int EMSCRIPTEN_KEEPALIVE interpreter_stack_trace() {
- std::string stack = instance->printFunctionStack();
- return (int)strdup(stack.c_str()); // XXX leak
-}
-
-// Does a call from js into an export of the module.
-extern "C" void EMSCRIPTEN_KEEPALIVE call_from_js(const char *target) {
- if (wasmJSDebug) std::cout << "call_from_js " << target << '\n';
-
- IString exportName(target);
- IString functionName = instance->wasm.getExport(exportName)->value;
- Function *function = instance->wasm.getFunction(functionName);
- assert(function);
- size_t seen = EM_ASM_INT_V({ return Module['tempArguments'].length });
- size_t actual = function->params.size();
- LiteralList arguments;
- for (size_t i = 0; i < actual; i++) {
- Type type = function->params[i];
- // add the parameter, with a zero value if JS did not provide it.
- if (type == i32) {
- arguments.push_back(Literal(i < seen ? EM_ASM_INT({ return Module['tempArguments'][$0] }, i) : (int32_t)0));
- } else if (type == f32) {
- arguments.push_back(Literal(i < seen ? (float)EM_ASM_DOUBLE({ return Module['tempArguments'][$0] }, i) : (float)0.0));
- } else if (type == f64) {
- arguments.push_back(Literal(i < seen ? EM_ASM_DOUBLE({ return Module['tempArguments'][$0] }, i) : (double)0.0));
- } else {
- abort();
- }
- }
- Literal ret = instance->callExport(exportName, arguments);
-
- if (wasmJSDebug) std::cout << "call_from_js returning " << ret << '\n';
-
- if (ret.type == none) EM_ASM({ Module['tempReturn'] = undefined });
- else if (ret.type == i32) EM_ASM_({ Module['tempReturn'] = $0 }, ret.geti32());
- else if (ret.type == f32) EM_ASM_({ Module['tempReturn'] = $0 }, ret.getf32());
- else if (ret.type == f64) EM_ASM_({ Module['tempReturn'] = $0 }, ret.getf64());
- else abort();
-}