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