diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/wasm-interpreter.h | 323 | ||||
-rw-r--r-- | src/wasm.h | 411 |
2 files changed, 507 insertions, 227 deletions
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 88b87d88f..59e42783d 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -378,71 +378,61 @@ private: Literal value = flow.value; NOTE_EVAL1(value); if (value.type == i32) { - int32_t v = value.geti32(); switch (curr->op) { - case Clz: return Literal((int32_t)CountLeadingZeroes(v)); - case Ctz: return Literal((int32_t)CountTrailingZeroes(v)); - case Popcnt: return Literal((int32_t)PopCount(v)); + case Clz: return value.countLeadingZeroes(); + case Ctz: return value.countTrailingZeroes(); + case Popcnt: return value.popCount(); case ReinterpretInt: return value.castToF32(); - case ExtendSInt32: return Literal(int64_t(value.geti32())); - case ExtendUInt32: return Literal(uint64_t((uint32_t)value.geti32())); - case ConvertUInt32: return curr->type == f32 ? Literal(float(uint32_t(value.geti32()))) : Literal(double(uint32_t(value.geti32()))); - case ConvertSInt32: return curr->type == f32 ? Literal(float(int32_t(value.geti32()))) : Literal(double(int32_t(value.geti32()))); + case ExtendSInt32: return value.extendToSI64(); + case ExtendUInt32: return value.extendToUI64(); + case ConvertUInt32: return curr->type == f32 ? value.convertUToF32() : value.convertUToF64(); + case ConvertSInt32: return curr->type == f32 ? value.convertSToF32() : value.convertSToF64(); default: abort(); } } if (value.type == i64) { - int64_t v = value.geti64(); switch (curr->op) { - case Clz: return Literal((int64_t)CountLeadingZeroes(v)); - case Ctz: return Literal((int64_t)CountTrailingZeroes(v)); - case Popcnt: return Literal((int64_t)PopCount(v)); - case WrapInt64: return Literal(int32_t(value.geti64())); + case Clz: return value.countLeadingZeroes(); + case Ctz: return value.countTrailingZeroes(); + case Popcnt: return value.popCount(); + case WrapInt64: return value.truncateToI32(); case ReinterpretInt: return value.castToF64(); - case ConvertUInt64: return curr->type == f32 ? Literal(float((uint64_t)value.geti64())) : Literal(double((uint64_t)value.geti64())); - case ConvertSInt64: return curr->type == f32 ? Literal(float(value.geti64())) : Literal(double(value.geti64())); + case ConvertUInt64: return curr->type == f32 ? value.convertUToF32() : value.convertUToF64(); + case ConvertSInt64: return curr->type == f32 ? value.convertSToF32() : value.convertSToF64(); default: abort(); } } if (value.type == f32) { - float v = value.getf32(); - float ret; switch (curr->op) { - // operate on bits directly, to avoid signalling bit being set on a float - case Neg: return Literal(value.reinterpreti32() ^ 0x80000000).castToF32(); break; - case Abs: return Literal(value.reinterpreti32() & 0x7fffffff).castToF32(); break; - case Ceil: ret = std::ceil(v); break; - case Floor: ret = std::floor(v); break; - case Trunc: ret = std::trunc(v); break; - case Nearest: ret = std::nearbyint(v); break; - case Sqrt: ret = std::sqrt(v); break; + case Neg: return value.neg(); + case Abs: return value.abs(); + case Ceil: return value.ceil(); + case Floor: return value.floor(); + case Trunc: return value.trunc(); + case Nearest: return value.nearbyint(); + case Sqrt: return value.sqrt(); case TruncSFloat32: return truncSFloat(curr, value); case TruncUFloat32: return truncUFloat(curr, value); - case ReinterpretFloat: return Literal(value.reinterpreti32()); - case PromoteFloat32: return Literal(double(value.getf32())); + case ReinterpretFloat: return value.castToI32(); + case PromoteFloat32: return value.extendToF64(); default: abort(); } - return Literal(fixNaN(v, ret)); } if (value.type == f64) { - double v = value.getf64(); - double ret; switch (curr->op) { - // operate on bits directly, to avoid signalling bit being set on a float - case Neg: return Literal(uint64_t((value.reinterpreti64() ^ 0x8000000000000000ULL))).castToF64(); break; - case Abs: return Literal(uint64_t(value.reinterpreti64() & 0x7fffffffffffffffULL)).castToF64(); break; - case Ceil: ret = std::ceil(v); break; - case Floor: ret = std::floor(v); break; - case Trunc: ret = std::trunc(v); break; - case Nearest: ret = std::nearbyint(v); break; - case Sqrt: ret = std::sqrt(v); break; + case Neg: return value.neg(); + case Abs: return value.abs(); + case Ceil: return value.ceil(); + case Floor: return value.floor(); + case Trunc: return value.trunc(); + case Nearest: return value.nearbyint(); + case Sqrt: return value.sqrt(); case TruncSFloat64: return truncSFloat(curr, value); case TruncUFloat64: return truncUFloat(curr, value); - case ReinterpretFloat: return Literal(value.reinterpreti64()); - case DemoteFloat64: return Literal(float(value.getf64())); + case ReinterpretFloat: return value.castToI64(); + case DemoteFloat64: return value.truncateToF32(); default: abort(); } - return Literal(fixNaN(v, ret)); } abort(); } @@ -458,167 +448,104 @@ private: assert(left.type == curr->left->type); assert(right.type == curr->right->type); if (left.type == i32) { - uint32_t l = left.geti32(), r = right.geti32(); - int32_t l_signed = l, r_signed = r; switch (curr->op) { - case Add: return Literal(l + r); - case Sub: return Literal(l - r); - case Mul: return Literal(l * r); + case Add: return left.add(right); + case Sub: return left.sub(right); + case Mul: return left.mul(right); case DivS: { - if (r_signed == 0) trap("i32.div_s by 0"); - if (l_signed == INT32_MIN && r_signed == -1) trap("i32.div_s overflow"); // signed division overflow - return Literal(l_signed / r_signed); + if (right.getInteger() == 0) trap("i32.div_s by 0"); + if (left.getInteger() == std::numeric_limits<int32_t>::min() && right.getInteger() == -1) trap("i32.div_s overflow"); // signed division overflow + return left.divS(right); } case DivU: { - if (r == 0) trap("i32.div_u by 0"); - return Literal(l / r); + if (right.getInteger() == 0) trap("i32.div_u by 0"); + return left.divU(right); } case RemS: { - if (r_signed == 0) trap("i32.rem_s by 0"); - if (l_signed == INT32_MIN && r_signed == -1) return Literal(int32_t(0)); - return Literal(l_signed % r_signed); + if (right.getInteger() == 0) trap("i32.rem_s by 0"); + if (left.getInteger() == std::numeric_limits<int32_t>::min() && right.getInteger() == -1) return Literal(int32_t(0)); + return left.remS(right); } case RemU: { - if (r == 0) trap("i32.rem_u by 0"); - return Literal(l % r); + if (right.getInteger() == 0) trap("i32.rem_u by 0"); + return left.remU(right); } - case And: return Literal(l & r); - case Or: return Literal(l | r); - case Xor: return Literal(l ^ r); - case Shl: { - r = r & 31; - return Literal(l << r); - } - case ShrU: { - r = r & 31; - return Literal(l >> r); - } - case ShrS: { - r_signed = r_signed & 31; - return Literal(l_signed >> r_signed); - } - case Eq: return Literal(l == r); - case Ne: return Literal(l != r); - case LtS: return Literal(l_signed < r_signed); - case LtU: return Literal(l < r); - case LeS: return Literal(l_signed <= r_signed); - case LeU: return Literal(l <= r); - case GtS: return Literal(l_signed > r_signed); - case GtU: return Literal(l > r); - case GeS: return Literal(l_signed >= r_signed); - case GeU: return Literal(l >= r); + case And: return left.and_(right); + case Or: return left.or_(right); + case Xor: return left.xor_(right); + case Shl: return left.shl(right.and_(Literal(int32_t(31)))); + case ShrU: return left.shrU(right.and_(Literal(int32_t(31)))); + case ShrS: return left.shrS(right.and_(Literal(int32_t(31)))); + case Eq: return left.eq(right); + case Ne: return left.ne(right); + case LtS: return left.ltS(right); + case LtU: return left.ltU(right); + case LeS: return left.leS(right); + case LeU: return left.leU(right); + case GtS: return left.gtS(right); + case GtU: return left.gtU(right); + case GeS: return left.geS(right); + case GeU: return left.geU(right); default: abort(); } } else if (left.type == i64) { - uint64_t l = left.geti64(), r = right.geti64(); - int64_t l_signed = l, r_signed = r; switch (curr->op) { - case Add: return Literal(l + r); - case Sub: return Literal(l - r); - case Mul: return Literal(l * r); + case Add: return left.add(right); + case Sub: return left.sub(right); + case Mul: return left.mul(right); case DivS: { - if (r_signed == 0) trap("i64.div_s by 0"); - if (l_signed == LLONG_MIN && r_signed == -1LL) trap("i64.div_s overflow"); // signed division overflow - return Literal(l_signed / r_signed); + if (right.getInteger() == 0) trap("i64.div_s by 0"); + if (left.getInteger() == LLONG_MIN && right.getInteger() == -1LL) trap("i64.div_s overflow"); // signed division overflow + return left.divS(right); } case DivU: { - if (r == 0) trap("i64.div_u by 0"); - return Literal(l / r); + if (right.getInteger() == 0) trap("i64.div_u by 0"); + return left.divU(right); } case RemS: { - if (r_signed == 0) trap("i64.rem_s by 0"); - if (l_signed == LLONG_MIN && r_signed == -1LL) return Literal(int64_t(0)); - return Literal(l_signed % r_signed); + if (right.getInteger() == 0) trap("i64.rem_s by 0"); + if (left.getInteger() == LLONG_MIN && right.getInteger() == -1LL) return Literal(int64_t(0)); + return left.remS(right); } case RemU: { - if (r == 0) trap("i64.rem_u by 0"); - return Literal(l % r); - } - case And: return Literal(l & r); - case Or: return Literal(l | r); - case Xor: return Literal(l ^ r); - case Shl: { - r = r & 63; - return Literal(l << r); + if (right.getInteger() == 0) trap("i64.rem_u by 0"); + return left.remU(right); } - case ShrU: { - r = r & 63; - return Literal(l >> r); - } - case ShrS: { - r_signed = r_signed & 63; - return Literal(l_signed >> r_signed); - } - case Eq: return Literal(l == r); - case Ne: return Literal(l != r); - case LtS: return Literal(l_signed < r_signed); - case LtU: return Literal(l < r); - case LeS: return Literal(l_signed <= r_signed); - case LeU: return Literal(l <= r); - case GtS: return Literal(l_signed > r_signed); - case GtU: return Literal(l > r); - case GeS: return Literal(l_signed >= r_signed); - case GeU: return Literal(l >= r); + case And: return left.and_(right); + case Or: return left.or_(right); + case Xor: return left.xor_(right); + case Shl: return left.shl(right.and_(Literal(int64_t(63)))); + case ShrU: return left.shrU(right.and_(Literal(int64_t(63)))); + case ShrS: return left.shrS(right.and_(Literal(int64_t(63)))); + case Eq: return left.eq(right); + case Ne: return left.ne(right); + case LtS: return left.ltS(right); + case LtU: return left.ltU(right); + case LeS: return left.leS(right); + case LeU: return left.leU(right); + case GtS: return left.gtS(right); + case GtU: return left.gtU(right); + case GeS: return left.geS(right); + case GeU: return left.geU(right); default: abort(); } - } else if (left.type == f32) { - float l = left.getf32(), r = right.getf32(); - float ret; + } else if (left.type == f32 || left.type == f64) { switch (curr->op) { - case Add: ret = l + r; break; - case Sub: ret = l - r; break; - case Mul: ret = l * r; break; - case Div: ret = l / r; break; - // operate on bits directly, to avoid signalling bit being set on a float - case CopySign: return Literal((left.reinterpreti32() & 0x7fffffff) | (right.reinterpreti32() & 0x80000000)).castToF32(); break; - case Min: { - if (l == r && l == 0) ret = 1/l < 0 ? l : r; - else ret = std::min(l, r); - break; - } - case Max: { - if (l == r && l == 0) ret = 1/l < 0 ? r : l; - else ret = std::max(l, r); - break; - } - case Eq: return Literal(l == r); - case Ne: return Literal(l != r); - case Lt: return Literal(l < r); - case Le: return Literal(l <= r); - case Gt: return Literal(l > r); - case Ge: return Literal(l >= r); - default: abort(); - } - return Literal(fixNaN(l, r, ret)); - } else if (left.type == f64) { - double l = left.getf64(), r = right.getf64(); - double ret; - switch (curr->op) { - case Add: ret = l + r; break; - case Sub: ret = l - r; break; - case Mul: ret = l * r; break; - case Div: ret = l / r; break; - // operate on bits directly, to avoid signalling bit being set on a float - case CopySign: return Literal((left.reinterpreti64() & 0x7fffffffffffffffUL) | (right.reinterpreti64() & 0x8000000000000000UL)).castToF64(); break; - case Min: { - if (l == r && l == 0) ret = 1/l < 0 ? l : r; - else ret = std::min(l, r); - break; - } - case Max: { - if (l == r && l == 0) ret = 1/l < 0 ? r : l; - else ret = std::max(l, r); - break; - } - case Eq: return Literal(l == r); - case Ne: return Literal(l != r); - case Lt: return Literal(l < r); - case Le: return Literal(l <= r); - case Gt: return Literal(l > r); - case Ge: return Literal(l >= r); + case Add: return left.add(right); + case Sub: return left.sub(right); + case Mul: return left.mul(right); + case Div: return left.div(right); + case CopySign: return left.copysign(right); + case Min: return left.min(right); + case Max: return left.max(right); + case Eq: return left.eq(right); + case Ne: return left.ne(right); + case Lt: return left.lt(right); + case Le: return left.le(right); + case Gt: return left.gt(right); + case Ge: return left.ge(right); default: abort(); } - return Literal(fixNaN(l, r, ret)); } abort(); } @@ -680,47 +607,11 @@ private: return Flow(); } - float fixNaN(float u, float result) { - if (!isnan(result)) return result; - bool unan = isnan(u); - if (!unan) { - return Literal((int32_t)0x7fc00000).reinterpretf32(); - } - return result; - } - - double fixNaN(double u, double result) { - if (!isnan(result)) return result; - bool unan = isnan(u); - if (!unan) { - return Literal((int64_t)0x7ff8000000000000LL).reinterpretf64(); - } - return result; - } - - float fixNaN(float l, float r, float result) { - bool lnan = isnan(l), rnan = isnan(r); - if (!isnan(result) && !lnan && !rnan) return result; - if (!lnan && !rnan) { - return Literal((int32_t)0x7fc00000).reinterpretf32(); - } - return Literal(Literal(lnan ? l : r).reinterpreti32() | 0xc00000).reinterpretf32(); - } - - double fixNaN(double l, double r, double result) { - bool lnan = isnan(l), rnan = isnan(r); - if (!isnan(result) && !lnan && !rnan) return result; - if (!lnan && !rnan) { - return Literal((int64_t)0x7ff8000000000000LL).reinterpretf64(); - } - return Literal(int64_t(Literal(lnan ? l : r).reinterpreti64() | 0x8000000000000LL)).reinterpretf64(); - } - Literal truncSFloat(Unary* curr, Literal value) { - double val = curr->op == TruncSFloat32 ? value.getf32() : value.getf64(); + double val = value.getFloat(); if (isnan(val)) trap("truncSFloat of nan"); if (curr->type == i32) { - if (val > (double)INT_MAX || val < (double)INT_MIN) trap("i32.truncSFloat overflow"); + if (val > (double)std::numeric_limits<int32_t>::max() || val < (double)std::numeric_limits<int32_t>::min()) trap("i32.truncSFloat overflow"); return Literal(int32_t(val)); } else { int64_t converted = val; @@ -730,10 +621,10 @@ private: } Literal truncUFloat(Unary* curr, Literal value) { - double val = curr->op == TruncUFloat32 ? value.getf32() : value.getf64(); + double val = value.getFloat(); if (isnan(val)) trap("truncUFloat of nan"); if (curr->type == i32) { - if (val > (double)UINT_MAX || val <= (double)-1) trap("i64.truncUFloat overflow"); + if (val > (double)std::numeric_limits<uint32_t>::max() || val <= (double)-1) trap("i64.truncUFloat overflow"); return Literal(uint32_t(val)); } else { uint64_t converted = val; 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 |