summaryrefslogtreecommitdiff
path: root/src/wasm.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm.h')
-rw-r--r--src/wasm.h411
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