summaryrefslogtreecommitdiff
path: root/src/wasm.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm.h')
-rw-r--r--src/wasm.h1040
1 files changed, 53 insertions, 987 deletions
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