summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjgravelle-google <jgravelle@google.com>2017-03-10 13:10:17 -0800
committerGitHub <noreply@github.com>2017-03-10 13:10:17 -0800
commit62e9f5d881e2d7e7f9f5da845ed2dbc176bc0bc5 (patch)
tree9ed29d721cafc8c3c70046ea4e072c7483cd66bd /src
parentd54c03e99f9a43bde1b6cec94f05b0af412d0e4f (diff)
downloadbinaryen-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.h10
-rw-r--r--src/asmjs/asm_v_wasm.cpp2
-rw-r--r--src/literal.h151
-rw-r--r--src/parsing.h2
-rw-r--r--src/passes/LegalizeJSInterface.cpp6
-rw-r--r--src/passes/Metrics.cpp1
-rw-r--r--src/passes/NameManager.cpp2
-rw-r--r--src/passes/Print.cpp3
-rw-r--r--src/passes/RemoveUnusedModuleElements.cpp4
-rw-r--r--src/s2wasm.h2
-rw-r--r--src/shell-interface.h2
-rw-r--r--src/tools/wasm-shell.cpp2
-rw-r--r--src/wasm-binary.h1
-rw-r--r--src/wasm-interpreter.h6
-rw-r--r--src/wasm-js.cpp4
-rw-r--r--src/wasm-linker.cpp8
-rw-r--r--src/wasm-linker.h4
-rw-r--r--src/wasm-type.h42
-rw-r--r--src/wasm-validator.h11
-rw-r--r--src/wasm.h1040
-rw-r--r--src/wasm/CMakeLists.txt2
-rw-r--r--src/wasm/literal.cpp649
-rw-r--r--src/wasm/wasm-binary.cpp7
-rw-r--r--src/wasm/wasm-s-parser.cpp28
-rw-r--r--src/wasm/wasm-type.cpp70
-rw-r--r--src/wasm/wasm.cpp386
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