diff options
Diffstat (limited to 'src/wasm.h')
-rw-r--r-- | src/wasm.h | 411 |
1 files changed, 400 insertions, 11 deletions
diff --git a/src/wasm.h b/src/wasm.h index 8701bd2f4..34b16c4ea 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -58,6 +58,7 @@ #include "emscripten-optimizer/simple_ast.h" #include "mixed_arena.h" #include "pretty_printing.h" +#include "support/bits.h" #include "support/utilities.h" namespace wasm { @@ -169,27 +170,39 @@ public: Literal castToF32() { assert(type == WasmType::i32); Literal ret(i32); - ret.type = f32; + ret.type = WasmType::f32; return ret; } Literal castToF64() { assert(type == WasmType::i64); Literal ret(i64); - ret.type = f64; + 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() { assert(type == WasmType::i32); return i32; } - int64_t geti64() { assert(type == WasmType::i64); return i64; } - float getf32() { assert(type == WasmType::f32); return bit_cast<float>(i32); } - double getf64() { assert(type == WasmType::f64); return bit_cast<double>(i64); } + 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() { assert(type == WasmType::f32); return i32; } - int64_t reinterpreti64() { assert(type == WasmType::f64); return i64; } - float reinterpretf32() { assert(type == WasmType::i32); return bit_cast<float>(i32); } - double reinterpretf64() { assert(type == WasmType::i64); return bit_cast<double>(i64); } + 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) { @@ -207,7 +220,7 @@ public: } } - bool operator==(Literal& other) { + bool operator==(const Literal& other) const { if (type != other.type) return false; switch (type) { case WasmType::none: return true; @@ -273,6 +286,382 @@ public: 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(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 sub(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 mul(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 div(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 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(i32 << other.i32); + case WasmType::i64: return Literal(i64 << other.i64); + default: WASM_UNREACHABLE(); + } + } + Literal shrS(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 shrU(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 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 = isnan(l), rnan = isnan(r); + if (!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 = isnan(l), rnan = isnan(r); + if (!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 = isnan(l), rnan = isnan(r); + if (!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 = isnan(l), rnan = isnan(r); + if (!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 |