diff options
author | jgravelle-google <jgravelle@google.com> | 2017-03-10 13:10:17 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-10 13:10:17 -0800 |
commit | 62e9f5d881e2d7e7f9f5da845ed2dbc176bc0bc5 (patch) | |
tree | 9ed29d721cafc8c3c70046ea4e072c7483cd66bd /src | |
parent | d54c03e99f9a43bde1b6cec94f05b0af412d0e4f (diff) | |
download | binaryen-62e9f5d881e2d7e7f9f5da845ed2dbc176bc0bc5.tar.gz binaryen-62e9f5d881e2d7e7f9f5da845ed2dbc176bc0bc5.tar.bz2 binaryen-62e9f5d881e2d7e7f9f5da845ed2dbc176bc0bc5.zip |
Wasm h to cpp (#926)
* Move WasmType function implementations to wasm.cpp
* Move Literal methods to wasm.cpp
* Reorder wasm.cpp shared constants back to top
* Move expression functions to wasm.cpp
* Finish moving things to wasm.cpp
* Split out Literal into its own .h/.cpp. Also factor out common wasm-type module
* Remove unneeded/transitive includes from wasm.h
* Add comment to try/check methods
* Rename tryX/checkX methods to getXOrNull
* Add missing include that should fix appveyor build breakage
* More appveyor
Diffstat (limited to 'src')
-rw-r--r-- | src/asm2wasm.h | 10 | ||||
-rw-r--r-- | src/asmjs/asm_v_wasm.cpp | 2 | ||||
-rw-r--r-- | src/literal.h | 151 | ||||
-rw-r--r-- | src/parsing.h | 2 | ||||
-rw-r--r-- | src/passes/LegalizeJSInterface.cpp | 6 | ||||
-rw-r--r-- | src/passes/Metrics.cpp | 1 | ||||
-rw-r--r-- | src/passes/NameManager.cpp | 2 | ||||
-rw-r--r-- | src/passes/Print.cpp | 3 | ||||
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 4 | ||||
-rw-r--r-- | src/s2wasm.h | 2 | ||||
-rw-r--r-- | src/shell-interface.h | 2 | ||||
-rw-r--r-- | src/tools/wasm-shell.cpp | 2 | ||||
-rw-r--r-- | src/wasm-binary.h | 1 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 6 | ||||
-rw-r--r-- | src/wasm-js.cpp | 4 | ||||
-rw-r--r-- | src/wasm-linker.cpp | 8 | ||||
-rw-r--r-- | src/wasm-linker.h | 4 | ||||
-rw-r--r-- | src/wasm-type.h | 42 | ||||
-rw-r--r-- | src/wasm-validator.h | 11 | ||||
-rw-r--r-- | src/wasm.h | 1040 | ||||
-rw-r--r-- | src/wasm/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/wasm/literal.cpp | 649 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 7 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 28 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 70 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 386 |
26 files changed, 1410 insertions, 1035 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 284fa0b7f..5adf82762 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -1128,7 +1128,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) { IString value = pair[1]->getIString(); if (key == Name("_emscripten_replace_memory")) { // asm.js memory growth provides this special non-asm function, which we don't need (we use grow_memory) - assert(!wasm.checkFunction(value)); + assert(!wasm.getFunctionOrNull(value)); continue; } else if (key == UDIVMODDI4) { udivmoddi4 = value; @@ -1211,7 +1211,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) { } void visitCall(Call* curr) { - if (!getModule()->checkFunction(curr->target)) { + if (!getModule()->getFunctionOrNull(curr->target)) { std::cerr << "invalid call target: " << curr->target << '\n'; WASM_UNREACHABLE(); } @@ -1386,7 +1386,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) { wasm.table.imported = true; // Import memory offset, if not already there - if (!wasm.checkImport("memoryBase") && !wasm.checkGlobal("memoryBase")) { + if (!wasm.getImportOrNull("memoryBase") && !wasm.getGlobalOrNull("memoryBase")) { auto* import = new Import; import->name = Name("memoryBase"); import->module = Name("env"); @@ -1397,7 +1397,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) { } // Import table offset, if not already there - if (!wasm.checkImport("tableBase") && !wasm.checkGlobal("tableBase")) { + if (!wasm.getImportOrNull("tableBase") && !wasm.getGlobalOrNull("tableBase")) { auto* import = new Import; import->name = Name("tableBase"); import->module = Name("env"); @@ -2019,7 +2019,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { firstOperand = 1; operands = &specific->operands; ret = specific; - } else if (wasm.checkImport(name)) { + } else if (wasm.getImportOrNull(name)) { callImport = allocator.alloc<CallImport>(); callImport->target = name; operands = &callImport->operands; diff --git a/src/asmjs/asm_v_wasm.cpp b/src/asmjs/asm_v_wasm.cpp index 2bf2b2bd3..ae7d320ca 100644 --- a/src/asmjs/asm_v_wasm.cpp +++ b/src/asmjs/asm_v_wasm.cpp @@ -95,7 +95,7 @@ FunctionType* sigToFunctionType(std::string sig) { FunctionType* ensureFunctionType(std::string sig, Module* wasm) { cashew::IString name(("FUNCSIG$" + sig).c_str(), false); - if (wasm->checkFunctionType(name)) { + if (wasm->getFunctionTypeOrNull(name)) { return wasm->getFunctionType(name); } // add new type diff --git a/src/literal.h b/src/literal.h new file mode 100644 index 000000000..af87e620d --- /dev/null +++ b/src/literal.h @@ -0,0 +1,151 @@ +/* + * Copyright 2017 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. + */ + +#ifndef wasm_literal_h +#define wasm_literal_h + +#include <iostream> +#include "support/utilities.h" +#include "compiler-support.h" +#include "wasm-type.h" + +namespace wasm { + +class Literal { +public: + WasmType type; + +private: + // store only integers, whose bits are deterministic. floats + // can have their signalling bit set, for example. + union { + int32_t i32; + int64_t i64; + }; + + // The RHS of shl/shru/shrs must be masked by bitwidth. + template <typename T> + static T shiftMask(T val) { + return val & (sizeof(T) * 8 - 1); + } + + public: + Literal() : type(WasmType::none), i64(0) {} + explicit Literal(WasmType type) : type(type), i64(0) {} + explicit Literal(int32_t init) : type(WasmType::i32), i32(init) {} + explicit Literal(uint32_t init) : type(WasmType::i32), i32(init) {} + explicit Literal(int64_t init) : type(WasmType::i64), i64(init) {} + explicit Literal(uint64_t init) : type(WasmType::i64), i64(init) {} + explicit Literal(float init) : type(WasmType::f32), i32(bit_cast<int32_t>(init)) {} + explicit Literal(double init) : type(WasmType::f64), i64(bit_cast<int64_t>(init)) {} + + Literal castToF32(); + Literal castToF64(); + Literal castToI32(); + Literal castToI64(); + + int32_t geti32() const { assert(type == WasmType::i32); return i32; } + int64_t geti64() const { assert(type == WasmType::i64); return i64; } + float getf32() const { assert(type == WasmType::f32); return bit_cast<float>(i32); } + double getf64() const { assert(type == WasmType::f64); return bit_cast<double>(i64); } + + int32_t* geti32Ptr() { assert(type == WasmType::i32); return &i32; } // careful! + + int32_t reinterpreti32() const { assert(type == WasmType::f32); return i32; } + int64_t reinterpreti64() const { assert(type == WasmType::f64); return i64; } + float reinterpretf32() const { assert(type == WasmType::i32); return bit_cast<float>(i32); } + double reinterpretf64() const { assert(type == WasmType::i64); return bit_cast<double>(i64); } + + int64_t getInteger(); + double getFloat(); + int64_t getBits(); + bool operator==(const Literal& other) const; + bool operator!=(const Literal& other) const; + + static uint32_t NaNPayload(float f); + static uint64_t NaNPayload(double f); + static float setQuietNaN(float f); + static double setQuietNaN(double f); + + static void printFloat(std::ostream &o, float f); + static void printDouble(std::ostream& o, double d); + + friend std::ostream& operator<<(std::ostream& o, Literal literal); + + Literal countLeadingZeroes() const; + Literal countTrailingZeroes() const; + Literal popCount() const; + + Literal extendToSI64() const; + Literal extendToUI64() const; + Literal extendToF64() const; + Literal truncateToI32() const; + Literal truncateToF32() const; + + Literal convertSToF32() const; + Literal convertUToF32() const; + Literal convertSToF64() const; + Literal convertUToF64() const; + + Literal neg() const; + Literal abs() const; + Literal ceil() const; + Literal floor() const; + Literal trunc() const; + Literal nearbyint() const; + Literal sqrt() const; + + Literal add(const Literal& other) const; + Literal sub(const Literal& other) const; + Literal mul(const Literal& other) const; + Literal div(const Literal& other) const; + Literal divS(const Literal& other) const; + Literal divU(const Literal& other) const; + Literal remS(const Literal& other) const; + Literal remU(const Literal& other) const; + Literal and_(const Literal& other) const; + Literal or_(const Literal& other) const; + Literal xor_(const Literal& other) const; + Literal shl(const Literal& other) const; + Literal shrS(const Literal& other) const; + Literal shrU(const Literal& other) const; + Literal rotL(const Literal& other) const; + Literal rotR(const Literal& other) const; + + Literal eq(const Literal& other) const; + Literal ne(const Literal& other) const; + Literal ltS(const Literal& other) const; + Literal ltU(const Literal& other) const; + Literal lt(const Literal& other) const; + Literal leS(const Literal& other) const; + Literal leU(const Literal& other) const; + Literal le(const Literal& other) const; + + Literal gtS(const Literal& other) const; + Literal gtU(const Literal& other) const; + Literal gt(const Literal& other) const; + Literal geS(const Literal& other) const; + Literal geU(const Literal& other) const; + Literal ge(const Literal& other) const; + + Literal min(const Literal& other) const; + Literal max(const Literal& other) const; + Literal copysign(const Literal& other) const; +}; + +} // namespace wasm + +#endif // wasm_literal_h diff --git a/src/parsing.h b/src/parsing.h index 1485dc6eb..d4df3c1c2 100644 --- a/src/parsing.h +++ b/src/parsing.h @@ -17,6 +17,7 @@ #ifndef wasm_parsing_h #define wasm_parsing_h +#include <cmath> #include <ostream> #include <sstream> #include <string> @@ -24,6 +25,7 @@ #include "shared-constants.h" #include "asmjs/shared-constants.h" #include "mixed_arena.h" +#include "support/colors.h" #include "support/utilities.h" #include "wasm.h" #include "wasm-printing.h" diff --git a/src/passes/LegalizeJSInterface.cpp b/src/passes/LegalizeJSInterface.cpp index 4bb25361f..b52c1d330 100644 --- a/src/passes/LegalizeJSInterface.cpp +++ b/src/passes/LegalizeJSInterface.cpp @@ -41,7 +41,7 @@ struct LegalizeJSInterface : public Pass { for (auto& ex : module->exports) { if (ex->kind == ExternalKind::Function) { // if it's an import, ignore it - if (auto* func = module->checkFunction(ex->value)) { + if (auto* func = module->getFunctionOrNull(ex->value)) { if (isIllegal(func)) { auto legalName = makeLegalStub(func, module); ex->value = legalName; @@ -159,7 +159,7 @@ private: } // a method may be exported multiple times - if (!module->checkFunction(legal->name)) { + if (!module->getFunctionOrNull(legal->name)) { module->addFunction(legal); } return legal->name; @@ -225,7 +225,7 @@ private: } void ensureTempRet0(Module* module) { - if (!module->checkGlobal(TEMP_RET_0)) { + if (!module->getGlobalOrNull(TEMP_RET_0)) { Global* global = new Global; global->name = TEMP_RET_0; global->type = i32; diff --git a/src/passes/Metrics.cpp b/src/passes/Metrics.cpp index 4dd8b1955..fe14135a4 100644 --- a/src/passes/Metrics.cpp +++ b/src/passes/Metrics.cpp @@ -17,6 +17,7 @@ #include <algorithm> #include <iomanip> #include <pass.h> +#include <support/colors.h> #include <wasm.h> namespace wasm { diff --git a/src/passes/NameManager.cpp b/src/passes/NameManager.cpp index 9f0198c2f..035586a77 100644 --- a/src/passes/NameManager.cpp +++ b/src/passes/NameManager.cpp @@ -60,7 +60,7 @@ void NameManager::visitFunctionType(FunctionType* curr) { void NameManager::visitFunction(Function* curr) { names.insert(curr->name); for (Index i = 0; i < curr->getNumLocals(); i++) { - Name name = curr->tryLocalName(i); + Name name = curr->getLocalNameOrDefault(i); if (name.is()) { names.insert(name); } diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index c46a0621d..04bf74529 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -21,6 +21,7 @@ #include <wasm.h> #include <wasm-printing.h> #include <pass.h> +#include <pretty_printing.h> namespace wasm { @@ -90,7 +91,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { Name printableLocal(Index index) { Name name; if (currFunction) { - name = currFunction->tryLocalName(index); + name = currFunction->getLocalNameOrDefault(index); } if (!name.is()) { name = Name::fromInt(index); diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 52a3ffc08..392096b72 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -59,13 +59,13 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { reachable.insert(curr); if (curr.first == ModuleElementKind::Function) { // if not an import, walk it - auto* func = module->checkFunction(curr.second); + auto* func = module->getFunctionOrNull(curr.second); if (func) { walk(func->body); } } else { // if not imported, it has an init expression we need to walk - auto* glob = module->checkGlobal(curr.second); + auto* glob = module->getGlobalOrNull(curr.second); if (glob) { walk(glob->init); } diff --git a/src/s2wasm.h b/src/s2wasm.h index faa31ae92..31632c9dc 100644 --- a/src/s2wasm.h +++ b/src/s2wasm.h @@ -628,7 +628,7 @@ class S2WasmBuilder { std::string sig = getSig(decl.get()); decl->name = "FUNCSIG$" + sig; - FunctionType *ty = wasm->checkFunctionType(decl->name); + FunctionType *ty = wasm->getFunctionTypeOrNull(decl->name); Name name = fixEmEHSjLjNames(rawName, sig); if (!ty) { // The wasm module takes ownership of the FunctionType if we insert it. diff --git a/src/shell-interface.h b/src/shell-interface.h index 26e0c1742..3fb2da2b0 100644 --- a/src/shell-interface.h +++ b/src/shell-interface.h @@ -148,7 +148,7 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { Literal callTable(Index index, LiteralList& arguments, WasmType result, ModuleInstance& instance) override { if (index >= table.size()) trap("callTable overflow"); - auto* func = instance.wasm.checkFunction(table[index]); + auto* func = instance.wasm.getFunctionOrNull(table[index]); if (!func) trap("uninitialized table element"); if (func->params.size() != arguments.size()) trap("callIndirect: bad # of arguments"); for (size_t i = 0; i < func->params.size(); i++) { diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp index bd8d987bd..e5736a951 100644 --- a/src/tools/wasm-shell.cpp +++ b/src/tools/wasm-shell.cpp @@ -174,7 +174,7 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm, for (auto& segment : wasm.table.segments) { for (auto name : segment.data) { // spec tests consider it illegal to use spectest.print in a table - if (auto* import = wasm.checkImport(name)) { + if (auto* import = wasm.getImportOrNull(name)) { if (import->module == SPECTEST && import->base == PRINT) { std::cerr << "cannot put spectest.print in table\n"; invalid = true; diff --git a/src/wasm-binary.h b/src/wasm-binary.h index f57d351c3..e24848d8e 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -22,7 +22,6 @@ #define wasm_wasm_binary_h #include <cassert> -#include <istream> #include <ostream> #include <type_traits> diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index f2c75cbc1..9558a3c38 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -23,10 +23,12 @@ #ifndef wasm_wasm_interpreter_h #define wasm_wasm_interpreter_h +#include <cmath> #include <limits.h> #include <sstream> #include "support/bits.h" +#include "support/safe_integer.h" #include "wasm.h" #include "wasm-traversal.h" @@ -579,14 +581,14 @@ public: // call an exported function Literal callExport(Name name, LiteralList& arguments) { - Export *export_ = wasm.checkExport(name); + Export *export_ = wasm.getExportOrNull(name); if (!export_) externalInterface->trap("callExport not found"); return callFunction(export_->value, arguments); } // get an exported global Literal getExport(Name name) { - Export *export_ = wasm.checkExport(name); + Export *export_ = wasm.getExportOrNull(name); if (!export_) externalInterface->trap("getExport external not found"); Name internalName = export_->value; auto iter = globals.find(internalName); diff --git a/src/wasm-js.cpp b/src/wasm-js.cpp index 970214833..22f1f1a18 100644 --- a/src/wasm-js.cpp +++ b/src/wasm-js.cpp @@ -93,7 +93,7 @@ void finalizeModule() { exit(EXIT_FAILURE); } module->memory.initial = Address(providedMemory / Memory::kPageSize); - module->memory.max = module->checkExport(GROW_WASM_MEMORY) ? Address(Memory::kMaxSize) : module->memory.initial; + module->memory.max = module->getExportOrNull(GROW_WASM_MEMORY) ? Address(Memory::kMaxSize) : module->memory.initial; // global mapping is done in js in post.js } @@ -229,7 +229,7 @@ extern "C" void EMSCRIPTEN_KEEPALIVE instantiate() { 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.checkFunction(name); + auto* func = wasm.getFunctionOrNull(name); if (func) { EM_ASM_({ Module['outside']['wasmTable'][$0] = $1; diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp index 961e703b4..28fb7c22f 100644 --- a/src/wasm-linker.cpp +++ b/src/wasm-linker.cpp @@ -49,7 +49,7 @@ void Linker::placeStackPointer(Address stackAllocation) { } void Linker::ensureFunctionImport(Name target, std::string signature) { - if (!out.wasm.checkImport(target)) { + if (!out.wasm.getImportOrNull(target)) { auto import = new Import; import->name = import->base = target; import->module = ENV; @@ -60,7 +60,7 @@ void Linker::ensureFunctionImport(Name target, std::string signature) { } void Linker::ensureObjectImport(Name target) { - if (!out.wasm.checkImport(target)) { + if (!out.wasm.getImportOrNull(target)) { auto import = new Import; import->name = import->base = target; import->module = ENV; @@ -195,7 +195,7 @@ void Linker::layout() { if (debug) std::cerr << " ==> " << *(relocation->data) << '\n'; } else { // function address - if (!out.wasm.checkFunction(name)) { + if (!out.wasm.getFunctionOrNull(name)) { if (FunctionType* f = out.getExternType(name)) { // Address of an imported function is taken, but imports do not have addresses in wasm. // Generate a thunk to forward to the call_import. @@ -398,7 +398,7 @@ void Linker::makeDummyFunction() { Function* Linker::getImportThunk(Name name, const FunctionType* funcType) { Name thunkName = std::string("__importThunk_") + name.c_str(); - if (Function* thunk = out.wasm.checkFunction(thunkName)) return thunk; + if (Function* thunk = out.wasm.getFunctionOrNull(thunkName)) return thunk; ensureFunctionImport(name, getSig(funcType)); wasm::Builder wasmBuilder(out.wasm); std::vector<NameType> params; diff --git a/src/wasm-linker.h b/src/wasm-linker.h index 663bb9e78..3700bb3ac 100644 --- a/src/wasm-linker.h +++ b/src/wasm-linker.h @@ -295,11 +295,11 @@ class Linker { } void exportFunction(Name name, bool must_export) { - if (!out.wasm.checkFunction(name)) { + if (!out.wasm.getFunctionOrNull(name)) { assert(!must_export); return; } - if (out.wasm.checkExport(name)) return; // Already exported + if (out.wasm.getExportOrNull(name)) return; // Already exported auto exp = new Export; exp->name = exp->value = name; exp->kind = ExternalKind::Function; diff --git a/src/wasm-type.h b/src/wasm-type.h new file mode 100644 index 000000000..560a846ae --- /dev/null +++ b/src/wasm-type.h @@ -0,0 +1,42 @@ +/* + * Copyright 2017 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. + */ + +#ifndef wasm_wasm_type_h +#define wasm_wasm_type_h + +namespace wasm { + +enum WasmType { + none, + i32, + i64, + f32, + f64, + unreachable // none means no type, e.g. a block can have no return type. but + // unreachable is different, as it can be "ignored" when doing + // type checking across branches +}; + +const char* printWasmType(WasmType type); +unsigned getWasmTypeSize(WasmType type); +bool isWasmTypeFloat(WasmType type); +WasmType getWasmType(unsigned size, bool float_); +WasmType getReachableWasmType(WasmType a, WasmType b); +bool isConcreteWasmType(WasmType type); + +} // namespace wasm + +#endif // wasm_wasm_type_h diff --git a/src/wasm-validator.h b/src/wasm-validator.h index 866c1c3db..e9ce5911d 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -39,6 +39,7 @@ #include <set> +#include "support/colors.h" #include "wasm.h" #include "wasm-printing.h" @@ -213,7 +214,7 @@ public: } void visitCall(Call *curr) { if (!validateGlobally) return; - auto* target = getModule()->checkFunction(curr->target); + auto* target = getModule()->getFunctionOrNull(curr->target); if (!shouldBeTrue(!!target, curr, "call target must exist")) return; if (!shouldBeTrue(curr->operands.size() == target->params.size(), curr, "call param number must match")) return; for (size_t i = 0; i < curr->operands.size(); i++) { @@ -224,7 +225,7 @@ public: } void visitCallImport(CallImport *curr) { if (!validateGlobally) return; - auto* import = getModule()->checkImport(curr->target); + auto* import = getModule()->getImportOrNull(curr->target); if (!shouldBeTrue(!!import, curr, "call_import target must exist")) return; if (!shouldBeTrue(!!import->functionType.is(), curr, "called import must be function")) return; auto* type = getModule()->getFunctionType(import->functionType); @@ -237,7 +238,7 @@ public: } void visitCallIndirect(CallIndirect *curr) { if (!validateGlobally) return; - auto* type = getModule()->checkFunctionType(curr->fullType); + auto* type = getModule()->getFunctionTypeOrNull(curr->fullType); if (!shouldBeTrue(!!type, curr, "call_indirect type must exist")) return; shouldBeEqualOrFirstIsUnreachable(curr->target->type, i32, curr, "indirect call target must be an i32"); if (!shouldBeTrue(curr->operands.size() == type->params.size(), curr, "call param number must match")) return; @@ -477,7 +478,7 @@ public: } shouldBeTrue(found, name, "module function exports must be found"); } else if (exp->kind == ExternalKind::Global) { - shouldBeTrue(curr->checkGlobal(name), name, "module global exports must be found"); + shouldBeTrue(curr->getGlobalOrNull(name), name, "module global exports must be found"); } else if (exp->kind == ExternalKind::Table) { shouldBeTrue(name == Name("0") || name == curr->table.name, name, "module table exports must be found"); } else if (exp->kind == ExternalKind::Memory) { @@ -491,7 +492,7 @@ public: } // start if (curr->start.is()) { - auto func = curr->checkFunction(curr->start); + auto func = curr->getFunctionOrNull(curr->start); if (shouldBeTrue(func != nullptr, curr->start, "start must be found")) { shouldBeTrue(func->params.size() == 0, curr, "start must have 0 params"); shouldBeTrue(func->result == none, curr, "start must not return a value"); diff --git a/src/wasm.h b/src/wasm.h index 21f7b420e..9cfcb4ca8 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -43,23 +43,16 @@ #ifndef wasm_wasm_h #define wasm_wasm_h +#include <algorithm> #include <cassert> -#include <cmath> -#include <cstddef> -#include <cstdint> -#include <cstring> -#include <fstream> #include <map> #include <string> #include <vector> -#include "compiler-support.h" -#include "emscripten-optimizer/simple_ast.h" +#include "literal.h" #include "mixed_arena.h" -#include "pretty_printing.h" -#include "support/bits.h" #include "support/name.h" -#include "support/utilities.h" +#include "wasm-type.h" namespace wasm { @@ -88,679 +81,6 @@ typedef int32_t Offset; // Types -enum WasmType { - none, - i32, - i64, - f32, - f64, - unreachable // none means no type, e.g. a block can have no return type. but - // unreachable is different, as it can be "ignored" when doing - // type checking across branches -}; - -inline const char* printWasmType(WasmType type) { - switch (type) { - case WasmType::none: return "none"; - case WasmType::i32: return "i32"; - case WasmType::i64: return "i64"; - case WasmType::f32: return "f32"; - case WasmType::f64: return "f64"; - case WasmType::unreachable: return "unreachable"; - default: WASM_UNREACHABLE(); - } -} - -inline unsigned getWasmTypeSize(WasmType type) { - switch (type) { - case WasmType::none: abort(); - case WasmType::i32: return 4; - case WasmType::i64: return 8; - case WasmType::f32: return 4; - case WasmType::f64: return 8; - default: WASM_UNREACHABLE(); - } -} - -inline bool isWasmTypeFloat(WasmType type) { - switch (type) { - case f32: - case f64: return true; - default: return false; - } -} - -inline WasmType getWasmType(unsigned size, bool float_) { - if (size < 4) return WasmType::i32; - if (size == 4) return float_ ? WasmType::f32 : WasmType::i32; - if (size == 8) return float_ ? WasmType::f64 : WasmType::i64; - abort(); -} - -inline WasmType getReachableWasmType(WasmType a, WasmType b) { - return a != unreachable ? a : b; -} - -inline bool isConcreteWasmType(WasmType type) { - return type != none && type != unreachable; -} - -// Literals - -class Literal { -public: - WasmType type; - -private: - // store only integers, whose bits are deterministic. floats - // can have their signalling bit set, for example. - union { - int32_t i32; - int64_t i64; - }; - - // The RHS of shl/shru/shrs must be masked by bitwidth. - template <typename T> - static T shiftMask(T val) { - return val & (sizeof(T) * 8 - 1); - } - - public: - Literal() : type(WasmType::none), i64(0) {} - explicit Literal(WasmType type) : type(type), i64(0) {} - explicit Literal(int32_t init) : type(WasmType::i32), i32(init) {} - explicit Literal(uint32_t init) : type(WasmType::i32), i32(init) {} - explicit Literal(int64_t init) : type(WasmType::i64), i64(init) {} - explicit Literal(uint64_t init) : type(WasmType::i64), i64(init) {} - explicit Literal(float init) : type(WasmType::f32), i32(bit_cast<int32_t>(init)) {} - explicit Literal(double init) : type(WasmType::f64), i64(bit_cast<int64_t>(init)) {} - - Literal castToF32() { - assert(type == WasmType::i32); - Literal ret(i32); - ret.type = WasmType::f32; - return ret; - } - Literal castToF64() { - assert(type == WasmType::i64); - Literal ret(i64); - ret.type = WasmType::f64; - return ret; - } - Literal castToI32() { - assert(type == WasmType::f32); - Literal ret(i32); - ret.type = WasmType::i32; - return ret; - } - Literal castToI64() { - assert(type == WasmType::f64); - Literal ret(i64); - ret.type = WasmType::i64; - return ret; - } - - int32_t geti32() const { assert(type == WasmType::i32); return i32; } - int64_t geti64() const { assert(type == WasmType::i64); return i64; } - float getf32() const { assert(type == WasmType::f32); return bit_cast<float>(i32); } - double getf64() const { assert(type == WasmType::f64); return bit_cast<double>(i64); } - - int32_t* geti32Ptr() { assert(type == WasmType::i32); return &i32; } // careful! - - int32_t reinterpreti32() const { assert(type == WasmType::f32); return i32; } - int64_t reinterpreti64() const { assert(type == WasmType::f64); return i64; } - float reinterpretf32() const { assert(type == WasmType::i32); return bit_cast<float>(i32); } - double reinterpretf64() const { assert(type == WasmType::i64); return bit_cast<double>(i64); } - - int64_t getInteger() { - switch (type) { - case WasmType::i32: return i32; - case WasmType::i64: return i64; - default: abort(); - } - } - - double getFloat() { - switch (type) { - case WasmType::f32: return getf32(); - case WasmType::f64: return getf64(); - default: abort(); - } - } - - int64_t getBits() { - switch (type) { - case WasmType::i32: case WasmType::f32: return i32; - case WasmType::i64: case WasmType::f64: return i64; - default: abort(); - } - } - - bool operator==(const Literal& other) const { - if (type != other.type) return false; - switch (type) { - case WasmType::none: return true; - case WasmType::i32: return i32 == other.i32; - case WasmType::f32: return getf32() == other.getf32(); - case WasmType::i64: return i64 == other.i64; - case WasmType::f64: return getf64() == other.getf64(); - default: abort(); - } - } - - bool operator!=(const Literal& other) const { - return !(*this == other); - } - - static uint32_t NaNPayload(float f) { - assert(std::isnan(f) && "expected a NaN"); - // SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF - // NaN has all-one exponent and non-zero fraction. - return ~0xff800000u & bit_cast<uint32_t>(f); - } - - static uint64_t NaNPayload(double f) { - assert(std::isnan(f) && "expected a NaN"); - // SEEEEEEE EEEEFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF - // NaN has all-one exponent and non-zero fraction. - return ~0xfff0000000000000ull & bit_cast<uint64_t>(f); - } - - static float setQuietNaN(float f) { - assert(std::isnan(f) && "expected a NaN"); - // An SNaN is a NaN with the most significant fraction bit clear. - return bit_cast<float>(0x00400000u | bit_cast<uint32_t>(f)); - } - - static double setQuietNaN(double f) { - assert(std::isnan(f) && "expected a NaN"); - // An SNaN is a NaN with the most significant fraction bit clear. - return bit_cast<double>(0x0008000000000000ull | bit_cast<uint64_t>(f)); - } - - static void printFloat(std::ostream &o, float f) { - if (std::isnan(f)) { - const char* sign = std::signbit(f) ? "-" : ""; - o << sign << "nan"; - if (uint32_t payload = NaNPayload(f)) { - o << ":0x" << std::hex << payload << std::dec; - } - return; - } - printDouble(o, f); - } - - static void printDouble(std::ostream& o, double d) { - if (d == 0 && std::signbit(d)) { - o << "-0"; - return; - } - if (std::isnan(d)) { - const char* sign = std::signbit(d) ? "-" : ""; - o << sign << "nan"; - if (uint64_t payload = NaNPayload(d)) { - o << ":0x" << std::hex << payload << std::dec; - } - return; - } - if (!std::isfinite(d)) { - o << (std::signbit(d) ? "-infinity" : "infinity"); - return; - } - const char* text = cashew::JSPrinter::numToString(d); - // spec interpreter hates floats starting with '.' - if (text[0] == '.') { - o << '0'; - } else if (text[0] == '-' && text[1] == '.') { - o << "-0"; - text++; - } - o << text; - } - - friend std::ostream& operator<<(std::ostream& o, Literal literal) { - o << '('; - prepareMinorColor(o) << printWasmType(literal.type) << ".const "; - switch (literal.type) { - case none: o << "?"; break; - case WasmType::i32: o << literal.i32; break; - case WasmType::i64: o << literal.i64; break; - case WasmType::f32: literal.printFloat(o, literal.getf32()); break; - case WasmType::f64: literal.printDouble(o, literal.getf64()); break; - default: WASM_UNREACHABLE(); - } - restoreNormalColor(o); - return o << ')'; - } - - Literal countLeadingZeroes() const { - if (type == WasmType::i32) return Literal((int32_t)CountLeadingZeroes(i32)); - if (type == WasmType::i64) return Literal((int64_t)CountLeadingZeroes(i64)); - WASM_UNREACHABLE(); - } - Literal countTrailingZeroes() const { - if (type == WasmType::i32) return Literal((int32_t)CountTrailingZeroes(i32)); - if (type == WasmType::i64) return Literal((int64_t)CountTrailingZeroes(i64)); - WASM_UNREACHABLE(); - } - Literal popCount() const { - if (type == WasmType::i32) return Literal((int32_t)PopCount(i32)); - if (type == WasmType::i64) return Literal((int64_t)PopCount(i64)); - WASM_UNREACHABLE(); - } - - Literal extendToSI64() const { - assert(type == WasmType::i32); - return Literal((int64_t)i32); - } - Literal extendToUI64() const { - assert(type == WasmType::i32); - return Literal((uint64_t)(uint32_t)i32); - } - Literal extendToF64() const { - assert(type == WasmType::f32); - return Literal(double(getf32())); - } - Literal truncateToI32() const { - assert(type == WasmType::i64); - return Literal((int32_t)i64); - } - Literal truncateToF32() const { - assert(type == WasmType::f64); - return Literal(float(getf64())); - } - - Literal convertSToF32() const { - if (type == WasmType::i32) return Literal(float(i32)); - if (type == WasmType::i64) return Literal(float(i64)); - WASM_UNREACHABLE(); - } - Literal convertUToF32() const { - if (type == WasmType::i32) return Literal(float(uint32_t(i32))); - if (type == WasmType::i64) return Literal(float(uint64_t(i64))); - WASM_UNREACHABLE(); - } - Literal convertSToF64() const { - if (type == WasmType::i32) return Literal(double(i32)); - if (type == WasmType::i64) return Literal(double(i64)); - WASM_UNREACHABLE(); - } - Literal convertUToF64() const { - if (type == WasmType::i32) return Literal(double(uint32_t(i32))); - if (type == WasmType::i64) return Literal(double(uint64_t(i64))); - WASM_UNREACHABLE(); - } - - Literal neg() const { - switch (type) { - case WasmType::i32: return Literal(i32 ^ 0x80000000); - case WasmType::i64: return Literal(int64_t(i64 ^ 0x8000000000000000ULL)); - case WasmType::f32: return Literal(i32 ^ 0x80000000).castToF32(); - case WasmType::f64: return Literal(int64_t(i64 ^ 0x8000000000000000ULL)).castToF64(); - default: WASM_UNREACHABLE(); - } - } - Literal abs() const { - switch (type) { - case WasmType::i32: return Literal(i32 & 0x7fffffff); - case WasmType::i64: return Literal(int64_t(i64 & 0x7fffffffffffffffULL)); - case WasmType::f32: return Literal(i32 & 0x7fffffff).castToF32(); - case WasmType::f64: return Literal(int64_t(i64 & 0x7fffffffffffffffULL)).castToF64(); - default: WASM_UNREACHABLE(); - } - } - Literal ceil() const { - switch (type) { - case WasmType::f32: return Literal(std::ceil(getf32())); - case WasmType::f64: return Literal(std::ceil(getf64())); - default: WASM_UNREACHABLE(); - } - } - Literal floor() const { - switch (type) { - case WasmType::f32: return Literal(std::floor(getf32())); - case WasmType::f64: return Literal(std::floor(getf64())); - default: WASM_UNREACHABLE(); - } - } - Literal trunc() const { - switch (type) { - case WasmType::f32: return Literal(std::trunc(getf32())); - case WasmType::f64: return Literal(std::trunc(getf64())); - default: WASM_UNREACHABLE(); - } - } - Literal nearbyint() const { - switch (type) { - case WasmType::f32: return Literal(std::nearbyint(getf32())); - case WasmType::f64: return Literal(std::nearbyint(getf64())); - default: WASM_UNREACHABLE(); - } - } - Literal sqrt() const { - switch (type) { - case WasmType::f32: return Literal(std::sqrt(getf32())); - case WasmType::f64: return Literal(std::sqrt(getf64())); - default: WASM_UNREACHABLE(); - } - } - - Literal add(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(uint32_t(i32) + uint32_t(other.i32)); - case WasmType::i64: return Literal(uint64_t(i64) + uint64_t(other.i64)); - case WasmType::f32: return Literal(getf32() + other.getf32()); - case WasmType::f64: return Literal(getf64() + other.getf64()); - default: WASM_UNREACHABLE(); - } - } - Literal sub(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(uint32_t(i32) - uint32_t(other.i32)); - case WasmType::i64: return Literal(uint64_t(i64) - uint64_t(other.i64)); - case WasmType::f32: return Literal(getf32() - other.getf32()); - case WasmType::f64: return Literal(getf64() - other.getf64()); - default: WASM_UNREACHABLE(); - } - } - Literal mul(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(uint32_t(i32) * uint32_t(other.i32)); - case WasmType::i64: return Literal(uint64_t(i64) * uint64_t(other.i64)); - case WasmType::f32: return Literal(getf32() * other.getf32()); - case WasmType::f64: return Literal(getf64() * other.getf64()); - default: WASM_UNREACHABLE(); - } - } - Literal div(const Literal& other) const { - switch (type) { - case WasmType::f32: { - float lhs = getf32(), rhs = other.getf32(); - float sign = std::signbit(lhs) == std::signbit(rhs) ? 0.f : -0.f; - switch (std::fpclassify(rhs)) { - case FP_ZERO: - switch (std::fpclassify(lhs)) { - case FP_NAN: return Literal(setQuietNaN(lhs)); - case FP_ZERO: return Literal(std::copysign(std::numeric_limits<float>::quiet_NaN(), sign)); - case FP_NORMAL: // fallthrough - case FP_SUBNORMAL: // fallthrough - case FP_INFINITE: return Literal(std::copysign(std::numeric_limits<float>::infinity(), sign)); - default: WASM_UNREACHABLE(); - } - case FP_NAN: // fallthrough - case FP_INFINITE: // fallthrough - case FP_NORMAL: // fallthrough - case FP_SUBNORMAL: return Literal(lhs / rhs); - default: WASM_UNREACHABLE(); - } - } - case WasmType::f64: { - double lhs = getf64(), rhs = other.getf64(); - double sign = std::signbit(lhs) == std::signbit(rhs) ? 0. : -0.; - switch (std::fpclassify(rhs)) { - case FP_ZERO: - switch (std::fpclassify(lhs)) { - case FP_NAN: return Literal(setQuietNaN(lhs)); - case FP_ZERO: return Literal(std::copysign(std::numeric_limits<double>::quiet_NaN(), sign)); - case FP_NORMAL: // fallthrough - case FP_SUBNORMAL: // fallthrough - case FP_INFINITE: return Literal(std::copysign(std::numeric_limits<double>::infinity(), sign)); - default: WASM_UNREACHABLE(); - } - case FP_NAN: // fallthrough - case FP_INFINITE: // fallthrough - case FP_NORMAL: // fallthrough - case FP_SUBNORMAL: return Literal(lhs / rhs); - default: WASM_UNREACHABLE(); - } - } - default: WASM_UNREACHABLE(); - } - } - Literal divS(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(i32 / other.i32); - case WasmType::i64: return Literal(i64 / other.i64); - default: WASM_UNREACHABLE(); - } - } - Literal divU(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(uint32_t(i32) / uint32_t(other.i32)); - case WasmType::i64: return Literal(uint64_t(i64) / uint64_t(other.i64)); - default: WASM_UNREACHABLE(); - } - } - Literal remS(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(i32 % other.i32); - case WasmType::i64: return Literal(i64 % other.i64); - default: WASM_UNREACHABLE(); - } - } - Literal remU(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(uint32_t(i32) % uint32_t(other.i32)); - case WasmType::i64: return Literal(uint64_t(i64) % uint64_t(other.i64)); - default: WASM_UNREACHABLE(); - } - } - Literal and_(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(i32 & other.i32); - case WasmType::i64: return Literal(i64 & other.i64); - default: WASM_UNREACHABLE(); - } - } - Literal or_(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(i32 | other.i32); - case WasmType::i64: return Literal(i64 | other.i64); - default: WASM_UNREACHABLE(); - } - } - Literal xor_(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(i32 ^ other.i32); - case WasmType::i64: return Literal(i64 ^ other.i64); - default: WASM_UNREACHABLE(); - } - } - Literal shl(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(uint32_t(i32) << shiftMask(other.i32)); - case WasmType::i64: return Literal(uint64_t(i64) << shiftMask(other.i64)); - default: WASM_UNREACHABLE(); - } - } - Literal shrS(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(i32 >> shiftMask(other.i32)); - case WasmType::i64: return Literal(i64 >> shiftMask(other.i64)); - default: WASM_UNREACHABLE(); - } - } - Literal shrU(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(uint32_t(i32) >> shiftMask(other.i32)); - case WasmType::i64: return Literal(uint64_t(i64) >> shiftMask(other.i64)); - default: WASM_UNREACHABLE(); - } - } - Literal rotL(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(RotateLeft(uint32_t(i32), uint32_t(other.i32))); - case WasmType::i64: return Literal(RotateLeft(uint64_t(i64), uint64_t(other.i64))); - default: WASM_UNREACHABLE(); - } - } - Literal rotR(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(RotateRight(uint32_t(i32), uint32_t(other.i32))); - case WasmType::i64: return Literal(RotateRight(uint64_t(i64), uint64_t(other.i64))); - default: WASM_UNREACHABLE(); - } - } - - Literal eq(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(i32 == other.i32); - case WasmType::i64: return Literal(i64 == other.i64); - case WasmType::f32: return Literal(getf32() == other.getf32()); - case WasmType::f64: return Literal(getf64() == other.getf64()); - default: WASM_UNREACHABLE(); - } - } - Literal ne(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(i32 != other.i32); - case WasmType::i64: return Literal(i64 != other.i64); - case WasmType::f32: return Literal(getf32() != other.getf32()); - case WasmType::f64: return Literal(getf64() != other.getf64()); - default: WASM_UNREACHABLE(); - } - } - Literal ltS(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(i32 < other.i32); - case WasmType::i64: return Literal(i64 < other.i64); - default: WASM_UNREACHABLE(); - } - } - Literal ltU(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(uint32_t(i32) < uint32_t(other.i32)); - case WasmType::i64: return Literal(uint64_t(i64) < uint64_t(other.i64)); - default: WASM_UNREACHABLE(); - } - } - Literal lt(const Literal& other) const { - switch (type) { - case WasmType::f32: return Literal(getf32() < other.getf32()); - case WasmType::f64: return Literal(getf64() < other.getf64()); - default: WASM_UNREACHABLE(); - } - } - Literal leS(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(i32 <= other.i32); - case WasmType::i64: return Literal(i64 <= other.i64); - default: WASM_UNREACHABLE(); - } - } - Literal leU(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(uint32_t(i32) <= uint32_t(other.i32)); - case WasmType::i64: return Literal(uint64_t(i64) <= uint64_t(other.i64)); - default: WASM_UNREACHABLE(); - } - } - Literal le(const Literal& other) const { - switch (type) { - case WasmType::f32: return Literal(getf32() <= other.getf32()); - case WasmType::f64: return Literal(getf64() <= other.getf64()); - default: WASM_UNREACHABLE(); - } - } - - Literal gtS(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(i32 > other.i32); - case WasmType::i64: return Literal(i64 > other.i64); - default: WASM_UNREACHABLE(); - } - } - Literal gtU(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(uint32_t(i32) > uint32_t(other.i32)); - case WasmType::i64: return Literal(uint64_t(i64) > uint64_t(other.i64)); - default: WASM_UNREACHABLE(); - } - } - Literal gt(const Literal& other) const { - switch (type) { - case WasmType::f32: return Literal(getf32() > other.getf32()); - case WasmType::f64: return Literal(getf64() > other.getf64()); - default: WASM_UNREACHABLE(); - } - } - Literal geS(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(i32 >= other.i32); - case WasmType::i64: return Literal(i64 >= other.i64); - default: WASM_UNREACHABLE(); - } - } - Literal geU(const Literal& other) const { - switch (type) { - case WasmType::i32: return Literal(uint32_t(i32) >= uint32_t(other.i32)); - case WasmType::i64: return Literal(uint64_t(i64) >= uint64_t(other.i64)); - default: WASM_UNREACHABLE(); - } - } - Literal ge(const Literal& other) const { - switch (type) { - case WasmType::f32: return Literal(getf32() >= other.getf32()); - case WasmType::f64: return Literal(getf64() >= other.getf64()); - default: WASM_UNREACHABLE(); - } - } - - Literal min(const Literal& other) const { - switch (type) { - case WasmType::f32: { - auto l = getf32(), r = other.getf32(); - if (l == r && l == 0) return Literal(std::signbit(l) ? l : r); - auto result = std::min(l, r); - bool lnan = std::isnan(l), rnan = std::isnan(r); - if (!std::isnan(result) && !lnan && !rnan) return Literal(result); - if (!lnan && !rnan) return Literal((int32_t)0x7fc00000).castToF32(); - return Literal(lnan ? l : r).castToI32().or_(Literal(0xc00000)).castToF32(); - } - case WasmType::f64: { - auto l = getf64(), r = other.getf64(); - if (l == r && l == 0) return Literal(std::signbit(l) ? l : r); - auto result = std::min(l, r); - bool lnan = std::isnan(l), rnan = std::isnan(r); - if (!std::isnan(result) && !lnan && !rnan) return Literal(result); - if (!lnan && !rnan) return Literal((int64_t)0x7ff8000000000000LL).castToF64(); - return Literal(lnan ? l : r).castToI64().or_(Literal(int64_t(0x8000000000000LL))).castToF64(); - } - default: WASM_UNREACHABLE(); - } - } - Literal max(const Literal& other) const { - switch (type) { - case WasmType::f32: { - auto l = getf32(), r = other.getf32(); - if (l == r && l == 0) return Literal(std::signbit(l) ? r : l); - auto result = std::max(l, r); - bool lnan = std::isnan(l), rnan = std::isnan(r); - if (!std::isnan(result) && !lnan && !rnan) return Literal(result); - if (!lnan && !rnan) return Literal((int32_t)0x7fc00000).castToF32(); - return Literal(lnan ? l : r).castToI32().or_(Literal(0xc00000)).castToF32(); - } - case WasmType::f64: { - auto l = getf64(), r = other.getf64(); - if (l == r && l == 0) return Literal(std::signbit(l) ? r : l); - auto result = std::max(l, r); - bool lnan = std::isnan(l), rnan = std::isnan(r); - if (!std::isnan(result) && !lnan && !rnan) return Literal(result); - if (!lnan && !rnan) return Literal((int64_t)0x7ff8000000000000LL).castToF64(); - return Literal(lnan ? l : r).castToI64().or_(Literal(int64_t(0x8000000000000LL))).castToF64(); - } - default: WASM_UNREACHABLE(); - } - } - Literal copysign(const Literal& other) const { - // operate on bits directly, to avoid signalling bit being set on a float - switch (type) { - case WasmType::f32: return Literal((i32 & 0x7fffffff) | (other.i32 & 0x80000000)).castToF32(); break; - case WasmType::f64: return Literal((i64 & 0x7fffffffffffffffUL) | (other.i64 & 0x8000000000000000UL)).castToF64(); break; - default: WASM_UNREACHABLE(); - } - } -}; // Operators @@ -884,35 +204,7 @@ public: } }; -inline const char* getExpressionName(Expression* curr) { - switch (curr->_id) { - case Expression::Id::InvalidId: abort(); - case Expression::Id::BlockId: return "block"; - case Expression::Id::IfId: return "if"; - case Expression::Id::LoopId: return "loop"; - case Expression::Id::BreakId: return "break"; - case Expression::Id::SwitchId: return "switch"; - case Expression::Id::CallId: return "call"; - case Expression::Id::CallImportId: return "call_import"; - case Expression::Id::CallIndirectId: return "call_indirect"; - case Expression::Id::GetLocalId: return "get_local"; - case Expression::Id::SetLocalId: return "set_local"; - case Expression::Id::GetGlobalId: return "get_global"; - case Expression::Id::SetGlobalId: return "set_global"; - case Expression::Id::LoadId: return "load"; - case Expression::Id::StoreId: return "store"; - case Expression::Id::ConstId: return "const"; - case Expression::Id::UnaryId: return "unary"; - case Expression::Id::BinaryId: return "binary"; - case Expression::Id::SelectId: return "select"; - case Expression::Id::DropId: return "drop"; - case Expression::Id::ReturnId: return "return"; - case Expression::Id::HostId: return "host"; - case Expression::Id::NopId: return "nop"; - case Expression::Id::UnreachableId: return "unreachable"; - default: WASM_UNREACHABLE(); - } -} +const char* getExpressionName(Expression* curr); typedef ArenaVector<Expression*> ExpressionList; @@ -994,17 +286,7 @@ public: Expression* value; Expression* condition; - void finalize() { - if (condition) { - if (value) { - type = value->type; - } else { - type = none; - } - } else { - type = unreachable; - } - } + void finalize(); }; class Switch : public SpecificExpression<Expression::SwitchId> { @@ -1043,22 +325,10 @@ public: FunctionType() : result(none) {} - bool structuralComparison(FunctionType& b) { - if (result != b.result) return false; - if (params.size() != b.params.size()) return false; - for (size_t i = 0; i < params.size(); i++) { - if (params[i] != b.params[i]) return false; - } - return true; - } + bool structuralComparison(FunctionType& b); - bool operator==(FunctionType& b) { - if (name != b.name) return false; - return structuralComparison(b); - } - bool operator!=(FunctionType& b) { - return !(*this == b); - } + bool operator==(FunctionType& b); + bool operator!=(FunctionType& b); }; class CallIndirect : public SpecificExpression<Expression::CallIndirectId> { @@ -1086,14 +356,8 @@ public: Index index; Expression* value; - bool isTee() { - return type != none; - } - - void setTee(bool is) { - if (is) type = value->type; - else type = none; - } + bool isTee(); + void setTee(bool is); }; class GetGlobal : public SpecificExpression<Expression::GetGlobalId> { @@ -1139,9 +403,7 @@ public: Expression* value; WasmType valueType; // the store never returns a value - void finalize() { - assert(valueType != none); // must be set - } + void finalize(); }; class Const : public SpecificExpression<Expression::ConstId> { @@ -1151,11 +413,7 @@ public: Literal value; - Const* set(Literal value_) { - value = value_; - type = value.type; - return this; - } + Const* set(Literal value_); }; class Unary : public SpecificExpression<Expression::UnaryId> { @@ -1166,59 +424,9 @@ public: UnaryOp op; Expression* value; - bool isRelational() { return op == EqZInt32 || op == EqZInt64; } - - void finalize() { - switch (op) { - case ClzInt32: - case CtzInt32: - case PopcntInt32: - case NegFloat32: - case AbsFloat32: - case CeilFloat32: - case FloorFloat32: - case TruncFloat32: - case NearestFloat32: - case SqrtFloat32: - case ClzInt64: - case CtzInt64: - case PopcntInt64: - case NegFloat64: - case AbsFloat64: - case CeilFloat64: - case FloorFloat64: - case TruncFloat64: - case NearestFloat64: - case SqrtFloat64: type = value->type; break; - case EqZInt32: - case EqZInt64: type = i32; break; - case ExtendSInt32: case ExtendUInt32: type = i64; break; - case WrapInt64: type = i32; break; - case PromoteFloat32: type = f64; break; - case DemoteFloat64: type = f32; break; - case TruncSFloat32ToInt32: - case TruncUFloat32ToInt32: - case TruncSFloat64ToInt32: - case TruncUFloat64ToInt32: - case ReinterpretFloat32: type = i32; break; - case TruncSFloat32ToInt64: - case TruncUFloat32ToInt64: - case TruncSFloat64ToInt64: - case TruncUFloat64ToInt64: - case ReinterpretFloat64: type = i64; break; - case ReinterpretInt32: - case ConvertSInt32ToFloat32: - case ConvertUInt32ToFloat32: - case ConvertSInt64ToFloat32: - case ConvertUInt64ToFloat32: type = f32; break; - case ReinterpretInt64: - case ConvertSInt32ToFloat64: - case ConvertUInt32ToFloat64: - case ConvertSInt64ToFloat64: - case ConvertUInt64ToFloat64: type = f64; break; - default: std::cerr << "waka " << op << '\n'; WASM_UNREACHABLE(); - } - } + bool isRelational(); + + void finalize(); }; class Binary : public SpecificExpression<Expression::BinaryId> { @@ -1233,52 +441,9 @@ public: // the type is always the type of the operands, // except for relationals - bool isRelational() { - switch (op) { - case EqFloat64: - case NeFloat64: - case LtFloat64: - case LeFloat64: - case GtFloat64: - case GeFloat64: - case EqInt32: - case NeInt32: - case LtSInt32: - case LtUInt32: - case LeSInt32: - case LeUInt32: - case GtSInt32: - case GtUInt32: - case GeSInt32: - case GeUInt32: - case EqInt64: - case NeInt64: - case LtSInt64: - case LtUInt64: - case LeSInt64: - case LeUInt64: - case GtSInt64: - case GtUInt64: - case GeSInt64: - case GeUInt64: - case EqFloat32: - case NeFloat32: - case LtFloat32: - case LeFloat32: - case GtFloat32: - case GeFloat32: return true; - default: return false; - } - } + bool isRelational(); - void finalize() { - assert(left && right); - if (isRelational()) { - type = i32; - } else { - type = getReachableWasmType(left->type, right->type); - } - } + void finalize(); }; class Select : public SpecificExpression<Expression::SelectId> { @@ -1290,10 +455,7 @@ public: Expression* ifFalse; Expression* condition; - void finalize() { - assert(ifTrue && ifFalse); - type = getReachableWasmType(ifTrue->type, ifFalse->type); - } + void finalize(); }; class Drop : public SpecificExpression<Expression::DropId> { @@ -1322,19 +484,7 @@ public: Name nameOperand; ExpressionList operands; - void finalize() { - switch (op) { - case PageSize: case CurrentMemory: case HasFeature: { - type = i32; - break; - } - case GrowMemory: { - type = i32; - break; - } - default: abort(); - } - } + void finalize(); }; class Unreachable : public SpecificExpression<Expression::UnreachableId> { @@ -1365,50 +515,22 @@ public: Function() : result(none) {} - size_t getNumParams() { - return params.size(); - } - size_t getNumVars() { - return vars.size(); - } - size_t getNumLocals() { - return params.size() + vars.size(); - } + size_t getNumParams(); + size_t getNumVars(); + size_t getNumLocals(); - bool isParam(Index index) { - return index < params.size(); - } - bool isVar(Index index) { - return index >= params.size(); - } + bool isParam(Index index); + bool isVar(Index index); - Name getLocalName(Index index) { - assert(index < localNames.size() && localNames[index].is()); - return localNames[index]; - } - Name tryLocalName(Index index) { - if (index < localNames.size() && localNames[index].is()) { - return localNames[index]; - } - // this is an unnamed local - return Name(); - } - Index getLocalIndex(Name name) { - assert(localIndices.count(name) > 0); - return localIndices[name]; - } - Index getVarIndexBase() { - return params.size(); - } - WasmType getLocalType(Index index) { - if (isParam(index)) { - return params[index]; - } else if (isVar(index)) { - return vars[index - getVarIndexBase()]; - } else { - WASM_UNREACHABLE(); - } - } + Name getLocalName(Index index); + Index getLocalIndex(Name name); + Index getVarIndexBase(); + WasmType getLocalType(Index index); + + Name getLocalNameOrDefault(Index index); + +private: + bool hasLocalName(Index index) const; }; enum class ExternalKind { @@ -1539,86 +661,30 @@ private: public: Module() {}; - FunctionType* getFunctionType(Name name) { assert(functionTypesMap.count(name)); return functionTypesMap[name]; } - Import* getImport(Name name) { assert(importsMap.count(name)); return importsMap[name]; } - Export* getExport(Name name) { assert(exportsMap.count(name)); return exportsMap[name]; } - Function* getFunction(Name name) { assert(functionsMap.count(name)); return functionsMap[name]; } - Global* getGlobal(Name name) { assert(globalsMap.count(name)); return globalsMap[name]; } - - FunctionType* checkFunctionType(Name name) { if (!functionTypesMap.count(name)) return nullptr; return functionTypesMap[name]; } - Import* checkImport(Name name) { if (!importsMap.count(name)) return nullptr; return importsMap[name]; } - Export* checkExport(Name name) { if (!exportsMap.count(name)) return nullptr; return exportsMap[name]; } - Function* checkFunction(Name name) { if (!functionsMap.count(name)) return nullptr; return functionsMap[name]; } - Global* checkGlobal(Name name) { if (!globalsMap.count(name)) return nullptr; return globalsMap[name]; } - - void addFunctionType(FunctionType* curr) { - assert(curr->name.is()); - functionTypes.push_back(std::unique_ptr<FunctionType>(curr)); - assert(functionTypesMap.find(curr->name) == functionTypesMap.end()); - functionTypesMap[curr->name] = curr; - } - void addImport(Import* curr) { - assert(curr->name.is()); - imports.push_back(std::unique_ptr<Import>(curr)); - assert(importsMap.find(curr->name) == importsMap.end()); - importsMap[curr->name] = curr; - } - void addExport(Export* curr) { - assert(curr->name.is()); - exports.push_back(std::unique_ptr<Export>(curr)); - assert(exportsMap.find(curr->name) == exportsMap.end()); - exportsMap[curr->name] = curr; - } - void addFunction(Function* curr) { - assert(curr->name.is()); - functions.push_back(std::unique_ptr<Function>(curr)); - assert(functionsMap.find(curr->name) == functionsMap.end()); - functionsMap[curr->name] = curr; - } - void addGlobal(Global* curr) { - assert(curr->name.is()); - globals.push_back(std::unique_ptr<Global>(curr)); - assert(globalsMap.find(curr->name) == globalsMap.end()); - globalsMap[curr->name] = curr; - } + FunctionType* getFunctionType(Name name); + Import* getImport(Name name); + Export* getExport(Name name); + Function* getFunction(Name name); + Global* getGlobal(Name name); - void addStart(const Name& s) { - start = s; - } + FunctionType* getFunctionTypeOrNull(Name name); + Import* getImportOrNull(Name name); + Export* getExportOrNull(Name name); + Function* getFunctionOrNull(Name name); + Global* getGlobalOrNull(Name name); - void removeImport(Name name) { - for (size_t i = 0; i < imports.size(); i++) { - if (imports[i]->name == name) { - imports.erase(imports.begin() + i); - break; - } - } - importsMap.erase(name); - } + void addFunctionType(FunctionType* curr); + void addImport(Import* curr); + void addExport(Export* curr); + void addFunction(Function* curr); + void addGlobal(Global* curr); + + void addStart(const Name& s); + + void removeImport(Name name); // TODO: remove* for other elements - void updateMaps() { - functionsMap.clear(); - for (auto& curr : functions) { - functionsMap[curr->name] = curr.get(); - } - functionTypesMap.clear(); - for (auto& curr : functionTypes) { - functionTypesMap[curr->name] = curr.get(); - } - importsMap.clear(); - for (auto& curr : imports) { - importsMap[curr->name] = curr.get(); - } - exportsMap.clear(); - for (auto& curr : exports) { - exportsMap[curr->name] = curr.get(); - } - globalsMap.clear(); - for (auto& curr : globals) { - globalsMap[curr->name] = curr.get(); - } - } + void updateMaps(); }; } // namespace wasm diff --git a/src/wasm/CMakeLists.txt b/src/wasm/CMakeLists.txt index e1b1aac33..b4b607934 100644 --- a/src/wasm/CMakeLists.txt +++ b/src/wasm/CMakeLists.txt @@ -1,7 +1,9 @@ SET(wasm_SOURCES + literal.cpp wasm.cpp wasm-binary.cpp wasm-io.cpp wasm-s-parser.cpp + wasm-type.cpp ) ADD_LIBRARY(wasm STATIC ${wasm_SOURCES}) diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp new file mode 100644 index 000000000..1cc083220 --- /dev/null +++ b/src/wasm/literal.cpp @@ -0,0 +1,649 @@ +/* + * Copyright 2016 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. + */ + +#include "literal.h" + +#include <cassert> +#include <cmath> + +#include "emscripten-optimizer/simple_ast.h" +#include "pretty_printing.h" +#include "support/bits.h" + +namespace wasm { + +Literal Literal::castToF32() { + assert(type == WasmType::i32); + Literal ret(i32); + ret.type = WasmType::f32; + return ret; +} + +Literal Literal::castToF64() { + assert(type == WasmType::i64); + Literal ret(i64); + ret.type = WasmType::f64; + return ret; +} + +Literal Literal::castToI32() { + assert(type == WasmType::f32); + Literal ret(i32); + ret.type = WasmType::i32; + return ret; +} + +Literal Literal::castToI64() { + assert(type == WasmType::f64); + Literal ret(i64); + ret.type = WasmType::i64; + return ret; +} + +int64_t Literal::getInteger() { + switch (type) { + case WasmType::i32: return i32; + case WasmType::i64: return i64; + default: abort(); + } +} + +double Literal::getFloat() { + switch (type) { + case WasmType::f32: return getf32(); + case WasmType::f64: return getf64(); + default: abort(); + } +} + +int64_t Literal::getBits() { + switch (type) { + case WasmType::i32: case WasmType::f32: return i32; + case WasmType::i64: case WasmType::f64: return i64; + default: abort(); + } +} + +bool Literal::operator==(const Literal& other) const { + if (type != other.type) return false; + switch (type) { + case WasmType::none: return true; + case WasmType::i32: return i32 == other.i32; + case WasmType::f32: return getf32() == other.getf32(); + case WasmType::i64: return i64 == other.i64; + case WasmType::f64: return getf64() == other.getf64(); + default: abort(); + } +} + +bool Literal::operator!=(const Literal& other) const { + return !(*this == other); +} + +uint32_t Literal::NaNPayload(float f) { + assert(std::isnan(f) && "expected a NaN"); + // SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF + // NaN has all-one exponent and non-zero fraction. + return ~0xff800000u & bit_cast<uint32_t>(f); +} + +uint64_t Literal::NaNPayload(double f) { + assert(std::isnan(f) && "expected a NaN"); + // SEEEEEEE EEEEFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF + // NaN has all-one exponent and non-zero fraction. + return ~0xfff0000000000000ull & bit_cast<uint64_t>(f); +} + +float Literal::setQuietNaN(float f) { + assert(std::isnan(f) && "expected a NaN"); + // An SNaN is a NaN with the most significant fraction bit clear. + return bit_cast<float>(0x00400000u | bit_cast<uint32_t>(f)); +} + +double Literal::setQuietNaN(double f) { + assert(std::isnan(f) && "expected a NaN"); + // An SNaN is a NaN with the most significant fraction bit clear. + return bit_cast<double>(0x0008000000000000ull | bit_cast<uint64_t>(f)); +} + +void Literal::printFloat(std::ostream &o, float f) { + if (std::isnan(f)) { + const char* sign = std::signbit(f) ? "-" : ""; + o << sign << "nan"; + if (uint32_t payload = NaNPayload(f)) { + o << ":0x" << std::hex << payload << std::dec; + } + return; + } + printDouble(o, f); +} + +void Literal::printDouble(std::ostream& o, double d) { + if (d == 0 && std::signbit(d)) { + o << "-0"; + return; + } + if (std::isnan(d)) { + const char* sign = std::signbit(d) ? "-" : ""; + o << sign << "nan"; + if (uint64_t payload = NaNPayload(d)) { + o << ":0x" << std::hex << payload << std::dec; + } + return; + } + if (!std::isfinite(d)) { + o << (std::signbit(d) ? "-infinity" : "infinity"); + return; + } + const char* text = cashew::JSPrinter::numToString(d); + // spec interpreter hates floats starting with '.' + if (text[0] == '.') { + o << '0'; + } else if (text[0] == '-' && text[1] == '.') { + o << "-0"; + text++; + } + o << text; +} + +std::ostream& operator<<(std::ostream& o, Literal literal) { + o << '('; + prepareMinorColor(o) << printWasmType(literal.type) << ".const "; + switch (literal.type) { + case none: o << "?"; break; + case WasmType::i32: o << literal.i32; break; + case WasmType::i64: o << literal.i64; break; + case WasmType::f32: literal.printFloat(o, literal.getf32()); break; + case WasmType::f64: literal.printDouble(o, literal.getf64()); break; + default: WASM_UNREACHABLE(); + } + restoreNormalColor(o); + return o << ')'; +} + +Literal Literal::countLeadingZeroes() const { + if (type == WasmType::i32) return Literal((int32_t)CountLeadingZeroes(i32)); + if (type == WasmType::i64) return Literal((int64_t)CountLeadingZeroes(i64)); + WASM_UNREACHABLE(); +} + +Literal Literal::countTrailingZeroes() const { + if (type == WasmType::i32) return Literal((int32_t)CountTrailingZeroes(i32)); + if (type == WasmType::i64) return Literal((int64_t)CountTrailingZeroes(i64)); + WASM_UNREACHABLE(); +} + +Literal Literal::popCount() const { + if (type == WasmType::i32) return Literal((int32_t)PopCount(i32)); + if (type == WasmType::i64) return Literal((int64_t)PopCount(i64)); + WASM_UNREACHABLE(); +} + +Literal Literal::extendToSI64() const { + assert(type == WasmType::i32); + return Literal((int64_t)i32); +} + +Literal Literal::extendToUI64() const { + assert(type == WasmType::i32); + return Literal((uint64_t)(uint32_t)i32); +} + +Literal Literal::extendToF64() const { + assert(type == WasmType::f32); + return Literal(double(getf32())); +} + +Literal Literal::truncateToI32() const { + assert(type == WasmType::i64); + return Literal((int32_t)i64); +} + +Literal Literal::truncateToF32() const { + assert(type == WasmType::f64); + return Literal(float(getf64())); +} + +Literal Literal::convertSToF32() const { + if (type == WasmType::i32) return Literal(float(i32)); + if (type == WasmType::i64) return Literal(float(i64)); + WASM_UNREACHABLE(); +} + +Literal Literal::convertUToF32() const { + if (type == WasmType::i32) return Literal(float(uint32_t(i32))); + if (type == WasmType::i64) return Literal(float(uint64_t(i64))); + WASM_UNREACHABLE(); +} + +Literal Literal::convertSToF64() const { + if (type == WasmType::i32) return Literal(double(i32)); + if (type == WasmType::i64) return Literal(double(i64)); + WASM_UNREACHABLE(); +} + +Literal Literal::convertUToF64() const { + if (type == WasmType::i32) return Literal(double(uint32_t(i32))); + if (type == WasmType::i64) return Literal(double(uint64_t(i64))); + WASM_UNREACHABLE(); +} + +Literal Literal::neg() const { + switch (type) { + case WasmType::i32: return Literal(i32 ^ 0x80000000); + case WasmType::i64: return Literal(int64_t(i64 ^ 0x8000000000000000ULL)); + case WasmType::f32: return Literal(i32 ^ 0x80000000).castToF32(); + case WasmType::f64: return Literal(int64_t(i64 ^ 0x8000000000000000ULL)).castToF64(); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::abs() const { + switch (type) { + case WasmType::i32: return Literal(i32 & 0x7fffffff); + case WasmType::i64: return Literal(int64_t(i64 & 0x7fffffffffffffffULL)); + case WasmType::f32: return Literal(i32 & 0x7fffffff).castToF32(); + case WasmType::f64: return Literal(int64_t(i64 & 0x7fffffffffffffffULL)).castToF64(); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::ceil() const { + switch (type) { + case WasmType::f32: return Literal(std::ceil(getf32())); + case WasmType::f64: return Literal(std::ceil(getf64())); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::floor() const { + switch (type) { + case WasmType::f32: return Literal(std::floor(getf32())); + case WasmType::f64: return Literal(std::floor(getf64())); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::trunc() const { + switch (type) { + case WasmType::f32: return Literal(std::trunc(getf32())); + case WasmType::f64: return Literal(std::trunc(getf64())); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::nearbyint() const { + switch (type) { + case WasmType::f32: return Literal(std::nearbyint(getf32())); + case WasmType::f64: return Literal(std::nearbyint(getf64())); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::sqrt() const { + switch (type) { + case WasmType::f32: return Literal(std::sqrt(getf32())); + case WasmType::f64: return Literal(std::sqrt(getf64())); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::add(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(uint32_t(i32) + uint32_t(other.i32)); + case WasmType::i64: return Literal(uint64_t(i64) + uint64_t(other.i64)); + case WasmType::f32: return Literal(getf32() + other.getf32()); + case WasmType::f64: return Literal(getf64() + other.getf64()); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::sub(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(uint32_t(i32) - uint32_t(other.i32)); + case WasmType::i64: return Literal(uint64_t(i64) - uint64_t(other.i64)); + case WasmType::f32: return Literal(getf32() - other.getf32()); + case WasmType::f64: return Literal(getf64() - other.getf64()); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::mul(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(uint32_t(i32) * uint32_t(other.i32)); + case WasmType::i64: return Literal(uint64_t(i64) * uint64_t(other.i64)); + case WasmType::f32: return Literal(getf32() * other.getf32()); + case WasmType::f64: return Literal(getf64() * other.getf64()); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::div(const Literal& other) const { + switch (type) { + case WasmType::f32: { + float lhs = getf32(), rhs = other.getf32(); + float sign = std::signbit(lhs) == std::signbit(rhs) ? 0.f : -0.f; + switch (std::fpclassify(rhs)) { + case FP_ZERO: + switch (std::fpclassify(lhs)) { + case FP_NAN: return Literal(setQuietNaN(lhs)); + case FP_ZERO: return Literal(std::copysign(std::numeric_limits<float>::quiet_NaN(), sign)); + case FP_NORMAL: // fallthrough + case FP_SUBNORMAL: // fallthrough + case FP_INFINITE: return Literal(std::copysign(std::numeric_limits<float>::infinity(), sign)); + default: WASM_UNREACHABLE(); + } + case FP_NAN: // fallthrough + case FP_INFINITE: // fallthrough + case FP_NORMAL: // fallthrough + case FP_SUBNORMAL: return Literal(lhs / rhs); + default: WASM_UNREACHABLE(); + } + } + case WasmType::f64: { + double lhs = getf64(), rhs = other.getf64(); + double sign = std::signbit(lhs) == std::signbit(rhs) ? 0. : -0.; + switch (std::fpclassify(rhs)) { + case FP_ZERO: + switch (std::fpclassify(lhs)) { + case FP_NAN: return Literal(setQuietNaN(lhs)); + case FP_ZERO: return Literal(std::copysign(std::numeric_limits<double>::quiet_NaN(), sign)); + case FP_NORMAL: // fallthrough + case FP_SUBNORMAL: // fallthrough + case FP_INFINITE: return Literal(std::copysign(std::numeric_limits<double>::infinity(), sign)); + default: WASM_UNREACHABLE(); + } + case FP_NAN: // fallthrough + case FP_INFINITE: // fallthrough + case FP_NORMAL: // fallthrough + case FP_SUBNORMAL: return Literal(lhs / rhs); + default: WASM_UNREACHABLE(); + } + } + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::divS(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(i32 / other.i32); + case WasmType::i64: return Literal(i64 / other.i64); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::divU(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(uint32_t(i32) / uint32_t(other.i32)); + case WasmType::i64: return Literal(uint64_t(i64) / uint64_t(other.i64)); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::remS(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(i32 % other.i32); + case WasmType::i64: return Literal(i64 % other.i64); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::remU(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(uint32_t(i32) % uint32_t(other.i32)); + case WasmType::i64: return Literal(uint64_t(i64) % uint64_t(other.i64)); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::and_(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(i32 & other.i32); + case WasmType::i64: return Literal(i64 & other.i64); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::or_(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(i32 | other.i32); + case WasmType::i64: return Literal(i64 | other.i64); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::xor_(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(i32 ^ other.i32); + case WasmType::i64: return Literal(i64 ^ other.i64); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::shl(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(uint32_t(i32) << shiftMask(other.i32)); + case WasmType::i64: return Literal(uint64_t(i64) << shiftMask(other.i64)); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::shrS(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(i32 >> shiftMask(other.i32)); + case WasmType::i64: return Literal(i64 >> shiftMask(other.i64)); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::shrU(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(uint32_t(i32) >> shiftMask(other.i32)); + case WasmType::i64: return Literal(uint64_t(i64) >> shiftMask(other.i64)); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::rotL(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(RotateLeft(uint32_t(i32), uint32_t(other.i32))); + case WasmType::i64: return Literal(RotateLeft(uint64_t(i64), uint64_t(other.i64))); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::rotR(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(RotateRight(uint32_t(i32), uint32_t(other.i32))); + case WasmType::i64: return Literal(RotateRight(uint64_t(i64), uint64_t(other.i64))); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::eq(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(i32 == other.i32); + case WasmType::i64: return Literal(i64 == other.i64); + case WasmType::f32: return Literal(getf32() == other.getf32()); + case WasmType::f64: return Literal(getf64() == other.getf64()); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::ne(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(i32 != other.i32); + case WasmType::i64: return Literal(i64 != other.i64); + case WasmType::f32: return Literal(getf32() != other.getf32()); + case WasmType::f64: return Literal(getf64() != other.getf64()); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::ltS(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(i32 < other.i32); + case WasmType::i64: return Literal(i64 < other.i64); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::ltU(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(uint32_t(i32) < uint32_t(other.i32)); + case WasmType::i64: return Literal(uint64_t(i64) < uint64_t(other.i64)); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::lt(const Literal& other) const { + switch (type) { + case WasmType::f32: return Literal(getf32() < other.getf32()); + case WasmType::f64: return Literal(getf64() < other.getf64()); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::leS(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(i32 <= other.i32); + case WasmType::i64: return Literal(i64 <= other.i64); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::leU(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(uint32_t(i32) <= uint32_t(other.i32)); + case WasmType::i64: return Literal(uint64_t(i64) <= uint64_t(other.i64)); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::le(const Literal& other) const { + switch (type) { + case WasmType::f32: return Literal(getf32() <= other.getf32()); + case WasmType::f64: return Literal(getf64() <= other.getf64()); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::gtS(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(i32 > other.i32); + case WasmType::i64: return Literal(i64 > other.i64); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::gtU(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(uint32_t(i32) > uint32_t(other.i32)); + case WasmType::i64: return Literal(uint64_t(i64) > uint64_t(other.i64)); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::gt(const Literal& other) const { + switch (type) { + case WasmType::f32: return Literal(getf32() > other.getf32()); + case WasmType::f64: return Literal(getf64() > other.getf64()); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::geS(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(i32 >= other.i32); + case WasmType::i64: return Literal(i64 >= other.i64); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::geU(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(uint32_t(i32) >= uint32_t(other.i32)); + case WasmType::i64: return Literal(uint64_t(i64) >= uint64_t(other.i64)); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::ge(const Literal& other) const { + switch (type) { + case WasmType::f32: return Literal(getf32() >= other.getf32()); + case WasmType::f64: return Literal(getf64() >= other.getf64()); + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::min(const Literal& other) const { + switch (type) { + case WasmType::f32: { + auto l = getf32(), r = other.getf32(); + if (l == r && l == 0) return Literal(std::signbit(l) ? l : r); + auto result = std::min(l, r); + bool lnan = std::isnan(l), rnan = std::isnan(r); + if (!std::isnan(result) && !lnan && !rnan) return Literal(result); + if (!lnan && !rnan) return Literal((int32_t)0x7fc00000).castToF32(); + return Literal(lnan ? l : r).castToI32().or_(Literal(0xc00000)).castToF32(); + } + case WasmType::f64: { + auto l = getf64(), r = other.getf64(); + if (l == r && l == 0) return Literal(std::signbit(l) ? l : r); + auto result = std::min(l, r); + bool lnan = std::isnan(l), rnan = std::isnan(r); + if (!std::isnan(result) && !lnan && !rnan) return Literal(result); + if (!lnan && !rnan) return Literal((int64_t)0x7ff8000000000000LL).castToF64(); + return Literal(lnan ? l : r).castToI64().or_(Literal(int64_t(0x8000000000000LL))).castToF64(); + } + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::max(const Literal& other) const { + switch (type) { + case WasmType::f32: { + auto l = getf32(), r = other.getf32(); + if (l == r && l == 0) return Literal(std::signbit(l) ? r : l); + auto result = std::max(l, r); + bool lnan = std::isnan(l), rnan = std::isnan(r); + if (!std::isnan(result) && !lnan && !rnan) return Literal(result); + if (!lnan && !rnan) return Literal((int32_t)0x7fc00000).castToF32(); + return Literal(lnan ? l : r).castToI32().or_(Literal(0xc00000)).castToF32(); + } + case WasmType::f64: { + auto l = getf64(), r = other.getf64(); + if (l == r && l == 0) return Literal(std::signbit(l) ? r : l); + auto result = std::max(l, r); + bool lnan = std::isnan(l), rnan = std::isnan(r); + if (!std::isnan(result) && !lnan && !rnan) return Literal(result); + if (!lnan && !rnan) return Literal((int64_t)0x7ff8000000000000LL).castToF64(); + return Literal(lnan ? l : r).castToI64().or_(Literal(int64_t(0x8000000000000LL))).castToF64(); + } + default: WASM_UNREACHABLE(); + } +} + +Literal Literal::copysign(const Literal& other) const { + // operate on bits directly, to avoid signalling bit being set on a float + switch (type) { + case WasmType::f32: return Literal((i32 & 0x7fffffff) | (other.i32 & 0x80000000)).castToF32(); break; + case WasmType::f64: return Literal((i64 & 0x7fffffffffffffffUL) | (other.i64 & 0x8000000000000000UL)).castToF64(); break; + default: WASM_UNREACHABLE(); + } +} + +} // namespace wasm diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 095a0942f..4d5d7e176 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -16,6 +16,9 @@ #include "wasm-binary.h" +#include <fstream> +#include "support/bits.h" + namespace wasm { void WasmBinaryWriter::prepare() { @@ -1764,12 +1767,12 @@ void WasmBinaryBuilder::visitGetGlobal(GetGlobal *curr) { if (debug) std::cerr << "zz node: GetGlobal " << pos << std::endl; auto index = getU32LEB(); curr->name = getGlobalName(index); - auto* global = wasm.checkGlobal(curr->name); + auto* global = wasm.getGlobalOrNull(curr->name); if (global) { curr->type = global->type; return; } - auto* import = wasm.checkImport(curr->name); + auto* import = wasm.getImportOrNull(curr->name); if (import && import->kind == ExternalKind::Global) { curr->type = import->globalType; return; diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index cdec89c74..0263b2112 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -338,7 +338,7 @@ void SExpressionWasmBuilder::preParseFunctionType(Element& s) { functionTypes[name] = stringToWasmType(curr[1]->str()); } else if (id == TYPE) { Name typeName = getFunctionTypeName(*curr[1]); - if (!wasm.checkFunctionType(typeName)) throw ParseException("unknown function type", curr.line, curr.col); + if (!wasm.getFunctionTypeOrNull(typeName)) throw ParseException("unknown function type", curr.line, curr.col); type = wasm.getFunctionType(typeName); functionTypes[name] = type->result; } else if (id == PARAM && curr.size() > 1) { @@ -426,7 +426,7 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { ex->name = exportName; ex->value = name; ex->kind = ExternalKind::Function; - if (wasm.checkExport(ex->name)) throw ParseException("duplicate export", s.line, s.col); + if (wasm.getExportOrNull(ex->name)) throw ParseException("duplicate export", s.line, s.col); wasm.addExport(ex.release()); } Expression* body = nullptr; @@ -489,7 +489,7 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { } else if (id == TYPE) { Name name = getFunctionTypeName(*curr[1]); type = name; - if (!wasm.checkFunctionType(name)) throw ParseException("unknown function type"); + if (!wasm.getFunctionTypeOrNull(name)) throw ParseException("unknown function type"); FunctionType* type = wasm.getFunctionType(name); result = type->result; for (size_t j = 0; j < type->params.size(); j++) { @@ -939,12 +939,12 @@ Expression* SExpressionWasmBuilder::makeSetLocal(Element& s) { Expression* SExpressionWasmBuilder::makeGetGlobal(Element& s) { auto ret = allocator.alloc<GetGlobal>(); ret->name = getGlobalName(*s[1]); - auto* global = wasm.checkGlobal(ret->name); + auto* global = wasm.getGlobalOrNull(ret->name); if (global) { ret->type = global->type; return ret; } - auto* import = wasm.checkImport(ret->name); + auto* import = wasm.getImportOrNull(ret->name); if (import && import->kind == ExternalKind::Global) { ret->type = import->globalType; return ret; @@ -955,7 +955,7 @@ Expression* SExpressionWasmBuilder::makeGetGlobal(Element& s) { Expression* SExpressionWasmBuilder::makeSetGlobal(Element& s) { auto ret = allocator.alloc<SetGlobal>(); ret->name = getGlobalName(*s[1]); - if (wasm.checkGlobal(ret->name) && !wasm.checkGlobal(ret->name)->mutable_) throw ParseException("set_global of immutable", s.line, s.col); + if (wasm.getGlobalOrNull(ret->name) && !wasm.getGlobalOrNull(ret->name)->mutable_) throw ParseException("set_global of immutable", s.line, s.col); ret->value = parseExpression(s[2]); return ret; } @@ -1194,7 +1194,7 @@ Expression* SExpressionWasmBuilder::makeLoop(Element& s) { Expression* SExpressionWasmBuilder::makeCall(Element& s) { auto target = getFunctionName(*s[1]); - auto* import = wasm.checkImport(target); + auto* import = wasm.getImportOrNull(target); if (import && import->kind == ExternalKind::Function) { auto ret = allocator.alloc<CallImport>(); ret->target = target; @@ -1223,7 +1223,7 @@ Expression* SExpressionWasmBuilder::makeCallIndirect(Element& s) { if (!wasm.table.exists) throw ParseException("no table"); auto ret = allocator.alloc<CallIndirect>(); IString type = s[1]->str(); - auto* fullType = wasm.checkFunctionType(type); + auto* fullType = wasm.getFunctionTypeOrNull(type); if (!fullType) throw ParseException("invalid call_indirect type", s.line, s.col); ret->fullType = fullType->name; ret->type = fullType->result; @@ -1351,7 +1351,7 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { ex->name = inner[1]->str(); ex->value = wasm.memory.name; ex->kind = ExternalKind::Memory; - if (wasm.checkExport(ex->name)) throw ParseException("duplicate export", s.line, s.col); + if (wasm.getExportOrNull(ex->name)) throw ParseException("duplicate export", s.line, s.col); wasm.addExport(ex.release()); i++; } else if (inner[0]->str() == IMPORT) { @@ -1443,7 +1443,7 @@ void SExpressionWasmBuilder::parseExport(Element& s) { ex->kind = ExternalKind::Table; } else if (inner[0]->str() == GLOBAL) { ex->kind = ExternalKind::Global; - if (wasm.checkGlobal(ex->value) && wasm.getGlobal(ex->value)->mutable_) throw ParseException("cannot export a mutable global", s.line, s.col); + if (wasm.getGlobalOrNull(ex->value) && wasm.getGlobal(ex->value)->mutable_) throw ParseException("cannot export a mutable global", s.line, s.col); } else { WASM_UNREACHABLE(); } @@ -1464,7 +1464,7 @@ void SExpressionWasmBuilder::parseExport(Element& s) { ex->value = s[2]->str(); ex->kind = ExternalKind::Function; } - if (wasm.checkExport(ex->name)) throw ParseException("duplicate export", s.line, s.col); + if (wasm.getExportOrNull(ex->name)) throw ParseException("duplicate export", s.line, s.col); wasm.addExport(ex.release()); } @@ -1545,7 +1545,7 @@ void SExpressionWasmBuilder::parseImport(Element& s) { type->result = stringToWasmType(params[1]->str()); } else if (id == TYPE) { IString name = params[1]->str(); - if (!wasm.checkFunctionType(name)) throw ParseException("bad function type for import"); + if (!wasm.getFunctionTypeOrNull(name)) throw ParseException("bad function type for import"); *type = *wasm.getFunctionType(name); } else { throw ParseException("bad import element"); @@ -1608,7 +1608,7 @@ void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) { ex->name = inner[1]->str(); ex->value = global->name; ex->kind = ExternalKind::Global; - if (wasm.checkExport(ex->name)) throw ParseException("duplicate export", s.line, s.col); + if (wasm.getExportOrNull(ex->name)) throw ParseException("duplicate export", s.line, s.col); wasm.addExport(ex.release()); exported = true; i++; @@ -1672,7 +1672,7 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) { ex->name = inner[1]->str(); ex->value = wasm.table.name; ex->kind = ExternalKind::Table; - if (wasm.checkExport(ex->name)) throw ParseException("duplicate export", s.line, s.col); + if (wasm.getExportOrNull(ex->name)) throw ParseException("duplicate export", s.line, s.col); wasm.addExport(ex.release()); i++; } else if (inner[0]->str() == IMPORT) { diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp new file mode 100644 index 000000000..1e48ce525 --- /dev/null +++ b/src/wasm/wasm-type.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2017 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. + */ + +#include "wasm-type.h" + +#include <cstdlib> +#include "compiler-support.h" + +namespace wasm { + +const char* printWasmType(WasmType type) { + switch (type) { + case WasmType::none: return "none"; + case WasmType::i32: return "i32"; + case WasmType::i64: return "i64"; + case WasmType::f32: return "f32"; + case WasmType::f64: return "f64"; + case WasmType::unreachable: return "unreachable"; + default: WASM_UNREACHABLE(); + } +} + +unsigned getWasmTypeSize(WasmType type) { + switch (type) { + case WasmType::none: abort(); + case WasmType::i32: return 4; + case WasmType::i64: return 8; + case WasmType::f32: return 4; + case WasmType::f64: return 8; + default: WASM_UNREACHABLE(); + } +} + +bool isWasmTypeFloat(WasmType type) { + switch (type) { + case f32: + case f64: return true; + default: return false; + } +} + +WasmType getWasmType(unsigned size, bool float_) { + if (size < 4) return WasmType::i32; + if (size == 4) return float_ ? WasmType::f32 : WasmType::i32; + if (size == 8) return float_ ? WasmType::f64 : WasmType::i64; + abort(); +} + +WasmType getReachableWasmType(WasmType a, WasmType b) { + return a != unreachable ? a : b; +} + +bool isConcreteWasmType(WasmType type) { + return type != none && type != unreachable; +} + +} // namespace wasm diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 412c2b7ac..a11646e65 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -67,6 +67,38 @@ Name GROW_WASM_MEMORY("__growWasmMemory"), PRINT("print"), EXIT("exit"); +// Expressions + +const char* getExpressionName(Expression* curr) { + switch (curr->_id) { + case Expression::Id::InvalidId: abort(); + case Expression::Id::BlockId: return "block"; + case Expression::Id::IfId: return "if"; + case Expression::Id::LoopId: return "loop"; + case Expression::Id::BreakId: return "break"; + case Expression::Id::SwitchId: return "switch"; + case Expression::Id::CallId: return "call"; + case Expression::Id::CallImportId: return "call_import"; + case Expression::Id::CallIndirectId: return "call_indirect"; + case Expression::Id::GetLocalId: return "get_local"; + case Expression::Id::SetLocalId: return "set_local"; + case Expression::Id::GetGlobalId: return "get_global"; + case Expression::Id::SetGlobalId: return "set_global"; + case Expression::Id::LoadId: return "load"; + case Expression::Id::StoreId: return "store"; + case Expression::Id::ConstId: return "const"; + case Expression::Id::UnaryId: return "unary"; + case Expression::Id::BinaryId: return "binary"; + case Expression::Id::SelectId: return "select"; + case Expression::Id::DropId: return "drop"; + case Expression::Id::ReturnId: return "return"; + case Expression::Id::HostId: return "host"; + case Expression::Id::NopId: return "nop"; + case Expression::Id::UnreachableId: return "unreachable"; + default: WASM_UNREACHABLE(); + } +} + // core AST type checking struct TypeSeeker : public PostWalker<TypeSeeker> { @@ -212,4 +244,358 @@ void Loop::finalize() { type = body->type; } +void Break::finalize() { + if (condition) { + if (value) { + type = value->type; + } else { + type = none; + } + } else { + type = unreachable; + } +} + +bool FunctionType::structuralComparison(FunctionType& b) { + if (result != b.result) return false; + if (params.size() != b.params.size()) return false; + for (size_t i = 0; i < params.size(); i++) { + if (params[i] != b.params[i]) return false; + } + return true; +} + +bool FunctionType::operator==(FunctionType& b) { + if (name != b.name) return false; + return structuralComparison(b); +} +bool FunctionType::operator!=(FunctionType& b) { + return !(*this == b); +} + +bool SetLocal::isTee() { + return type != none; +} + +void SetLocal::setTee(bool is) { + if (is) type = value->type; + else type = none; +} + +void Store::finalize() { + assert(valueType != none); // must be set +} + +Const* Const::set(Literal value_) { + value = value_; + type = value.type; + return this; +} + +bool Unary::isRelational() { + return op == EqZInt32 || op == EqZInt64; +} + +void Unary::finalize() { + switch (op) { + case ClzInt32: + case CtzInt32: + case PopcntInt32: + case NegFloat32: + case AbsFloat32: + case CeilFloat32: + case FloorFloat32: + case TruncFloat32: + case NearestFloat32: + case SqrtFloat32: + case ClzInt64: + case CtzInt64: + case PopcntInt64: + case NegFloat64: + case AbsFloat64: + case CeilFloat64: + case FloorFloat64: + case TruncFloat64: + case NearestFloat64: + case SqrtFloat64: type = value->type; break; + case EqZInt32: + case EqZInt64: type = i32; break; + case ExtendSInt32: case ExtendUInt32: type = i64; break; + case WrapInt64: type = i32; break; + case PromoteFloat32: type = f64; break; + case DemoteFloat64: type = f32; break; + case TruncSFloat32ToInt32: + case TruncUFloat32ToInt32: + case TruncSFloat64ToInt32: + case TruncUFloat64ToInt32: + case ReinterpretFloat32: type = i32; break; + case TruncSFloat32ToInt64: + case TruncUFloat32ToInt64: + case TruncSFloat64ToInt64: + case TruncUFloat64ToInt64: + case ReinterpretFloat64: type = i64; break; + case ReinterpretInt32: + case ConvertSInt32ToFloat32: + case ConvertUInt32ToFloat32: + case ConvertSInt64ToFloat32: + case ConvertUInt64ToFloat32: type = f32; break; + case ReinterpretInt64: + case ConvertSInt32ToFloat64: + case ConvertUInt32ToFloat64: + case ConvertSInt64ToFloat64: + case ConvertUInt64ToFloat64: type = f64; break; + default: std::cerr << "waka " << op << '\n'; WASM_UNREACHABLE(); + } +} + +bool Binary::isRelational() { + switch (op) { + case EqFloat64: + case NeFloat64: + case LtFloat64: + case LeFloat64: + case GtFloat64: + case GeFloat64: + case EqInt32: + case NeInt32: + case LtSInt32: + case LtUInt32: + case LeSInt32: + case LeUInt32: + case GtSInt32: + case GtUInt32: + case GeSInt32: + case GeUInt32: + case EqInt64: + case NeInt64: + case LtSInt64: + case LtUInt64: + case LeSInt64: + case LeUInt64: + case GtSInt64: + case GtUInt64: + case GeSInt64: + case GeUInt64: + case EqFloat32: + case NeFloat32: + case LtFloat32: + case LeFloat32: + case GtFloat32: + case GeFloat32: return true; + default: return false; + } +} + +void Binary::finalize() { + assert(left && right); + if (isRelational()) { + type = i32; + } else { + type = getReachableWasmType(left->type, right->type); + } +} + +void Select::finalize() { + assert(ifTrue && ifFalse); + type = getReachableWasmType(ifTrue->type, ifFalse->type); +} + +void Host::finalize() { + switch (op) { + case PageSize: case CurrentMemory: case HasFeature: { + type = i32; + break; + } + case GrowMemory: { + type = i32; + break; + } + default: abort(); + } +} + +size_t Function::getNumParams() { + return params.size(); +} + +size_t Function::getNumVars() { + return vars.size(); +} + +size_t Function::getNumLocals() { + return params.size() + vars.size(); +} + +bool Function::isParam(Index index) { + return index < params.size(); +} + +bool Function::isVar(Index index) { + return index >= params.size(); +} + +bool Function::hasLocalName(Index index) const { + return index < localNames.size() && localNames[index].is(); +} + +Name Function::getLocalName(Index index) { + assert(hasLocalName(index)); + return localNames[index]; +} + +Name Function::getLocalNameOrDefault(Index index) { + if (hasLocalName(index)) { + return localNames[index]; + } + // this is an unnamed local + return Name(); +} + +Index Function::getLocalIndex(Name name) { + assert(localIndices.count(name) > 0); + return localIndices[name]; +} + +Index Function::getVarIndexBase() { + return params.size(); +} + +WasmType Function::getLocalType(Index index) { + if (isParam(index)) { + return params[index]; + } else if (isVar(index)) { + return vars[index - getVarIndexBase()]; + } else { + WASM_UNREACHABLE(); + } +} + +FunctionType* Module::getFunctionType(Name name) { + assert(functionTypesMap.count(name)); + return functionTypesMap[name]; +} + +Import* Module::getImport(Name name) { + assert(importsMap.count(name)); + return importsMap[name]; +} + +Export* Module::getExport(Name name) { + assert(exportsMap.count(name)); + return exportsMap[name]; +} + +Function* Module::getFunction(Name name) { + assert(functionsMap.count(name)); + return functionsMap[name]; +} + +Global* Module::getGlobal(Name name) { + assert(globalsMap.count(name)); + return globalsMap[name]; +} + +FunctionType* Module::getFunctionTypeOrNull(Name name) { + if (!functionTypesMap.count(name)) + return nullptr; + return functionTypesMap[name]; +} + +Import* Module::getImportOrNull(Name name) { + if (!importsMap.count(name)) + return nullptr; + return importsMap[name]; +} + +Export* Module::getExportOrNull(Name name) { + if (!exportsMap.count(name)) + return nullptr; + return exportsMap[name]; +} + +Function* Module::getFunctionOrNull(Name name) { + if (!functionsMap.count(name)) + return nullptr; + return functionsMap[name]; +} + +Global* Module::getGlobalOrNull(Name name) { + if (!globalsMap.count(name)) + return nullptr; + return globalsMap[name]; +} + +void Module::addFunctionType(FunctionType* curr) { + assert(curr->name.is()); + functionTypes.push_back(std::unique_ptr<FunctionType>(curr)); + assert(functionTypesMap.find(curr->name) == functionTypesMap.end()); + functionTypesMap[curr->name] = curr; +} + +void Module::addImport(Import* curr) { + assert(curr->name.is()); + imports.push_back(std::unique_ptr<Import>(curr)); + assert(importsMap.find(curr->name) == importsMap.end()); + importsMap[curr->name] = curr; +} + +void Module::addExport(Export* curr) { + assert(curr->name.is()); + exports.push_back(std::unique_ptr<Export>(curr)); + assert(exportsMap.find(curr->name) == exportsMap.end()); + exportsMap[curr->name] = curr; +} + +void Module::addFunction(Function* curr) { + assert(curr->name.is()); + functions.push_back(std::unique_ptr<Function>(curr)); + assert(functionsMap.find(curr->name) == functionsMap.end()); + functionsMap[curr->name] = curr; +} + +void Module::addGlobal(Global* curr) { + assert(curr->name.is()); + globals.push_back(std::unique_ptr<Global>(curr)); + assert(globalsMap.find(curr->name) == globalsMap.end()); + globalsMap[curr->name] = curr; +} + +void Module::addStart(const Name& s) { + start = s; +} + +void Module::removeImport(Name name) { + for (size_t i = 0; i < imports.size(); i++) { + if (imports[i]->name == name) { + imports.erase(imports.begin() + i); + break; + } + } + importsMap.erase(name); +} + // TODO: remove* for other elements + +void Module::updateMaps() { + functionsMap.clear(); + for (auto& curr : functions) { + functionsMap[curr->name] = curr.get(); + } + functionTypesMap.clear(); + for (auto& curr : functionTypes) { + functionTypesMap[curr->name] = curr.get(); + } + importsMap.clear(); + for (auto& curr : imports) { + importsMap[curr->name] = curr.get(); + } + exportsMap.clear(); + for (auto& curr : exports) { + exportsMap[curr->name] = curr.get(); + } + globalsMap.clear(); + for (auto& curr : globals) { + globalsMap[curr->name] = curr.get(); + } +} + } // namespace wasm |