summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/I64ToI32Lowering.cpp37
-rw-r--r--src/wasm2asm.h24
-rw-r--r--test/wasm2asm/reinterpret.2asm.js77
-rw-r--r--test/wasm2asm/reinterpret.wast24
4 files changed, 159 insertions, 3 deletions
diff --git a/src/passes/I64ToI32Lowering.cpp b/src/passes/I64ToI32Lowering.cpp
index 759e5e265..657df4896 100644
--- a/src/passes/I64ToI32Lowering.cpp
+++ b/src/passes/I64ToI32Lowering.cpp
@@ -587,6 +587,37 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
replaceCurrent(curr->value);
}
+ void lowerReinterpretFloat64(Unary* curr) {
+ // Assume that the wasm file assumes the address 0 is invalid and roundtrip
+ // our f64 through memory at address 0
+ Expression* zero = builder->makeConst(Literal(int32_t(0)));
+ TempVar highBits = getTemp();
+ Block *result = builder->blockify(
+ builder->makeStore(8, 0, 8, zero, curr->value, f64),
+ builder->makeSetLocal(
+ highBits,
+ builder->makeLoad(4, true, 4, 4, zero, i32)
+ ),
+ builder->makeLoad(4, true, 0, 4, zero, i32)
+ );
+ setOutParam(result, std::move(highBits));
+ replaceCurrent(result);
+ }
+
+ void lowerReinterpretInt64(Unary* curr) {
+ // Assume that the wasm file assumes the address 0 is invalid and roundtrip
+ // our i64 through memory at address 0
+ TempVar highBits = fetchOutParam(curr->value);
+ TempVar lowBits = getTemp();
+ Expression* zero = builder->makeConst(Literal(int32_t(0)));
+ Block *result = builder->blockify(
+ builder->makeStore(4, 0, 4, zero, curr->value, i32),
+ builder->makeStore(4, 4, 4, zero, builder->makeGetLocal(highBits, i32), i32),
+ builder->makeLoad(8, true, 0, 8, zero, f64)
+ );
+ replaceCurrent(result);
+ }
+
void lowerPopcnt64(Unary* curr) {
TempVar highBits = fetchOutParam(curr->value);
TempVar lowBits = getTemp();
@@ -701,7 +732,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
replaceCurrent(curr->value);
return;
}
- assert(hasOutParam(curr->value) || curr->type == i64);
+ assert(hasOutParam(curr->value) || curr->type == i64 || curr->type == f64);
switch (curr->op) {
case ClzInt64:
case CtzInt64: lowerCountZeros(curr); break;
@@ -710,16 +741,16 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
case ExtendSInt32: lowerExtendSInt32(curr); break;
case ExtendUInt32: lowerExtendUInt32(curr); break;
case WrapInt64: lowerWrapInt64(curr); break;
+ case ReinterpretFloat64: lowerReinterpretFloat64(curr); break;
+ case ReinterpretInt64: lowerReinterpretInt64(curr); break;
case TruncSFloat32ToInt64:
case TruncUFloat32ToInt64:
case TruncSFloat64ToInt64:
case TruncUFloat64ToInt64:
- case ReinterpretFloat64:
case ConvertSInt64ToFloat32:
case ConvertSInt64ToFloat64:
case ConvertUInt64ToFloat32:
case ConvertUInt64ToFloat64:
- case ReinterpretInt64:
default:
std::cerr << "Unhandled unary operator: " << curr->op << std::endl;
abort();
diff --git a/src/wasm2asm.h b/src/wasm2asm.h
index dce91dacb..ea8fd8142 100644
--- a/src/wasm2asm.h
+++ b/src/wasm2asm.h
@@ -1376,6 +1376,19 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
makeAsmCoercion(visit(curr->value,
EXPRESSION_RESULT), ASM_INT), EQ,
makeAsmCoercion(ValueBuilder::makeInt(0), ASM_INT));
+ case ReinterpretFloat32: {
+ // Naively assume that the address 0 and the next 4 bytes are
+ // permanently unused by the source program, which is definitely
+ // true for languages like C/C++/Rust
+ Ref zero = ValueBuilder::makeInt(0);
+ Ref ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF32), zero);
+ Ref value = visit(curr->value, EXPRESSION_RESULT);
+ Ref store = ValueBuilder::makeBinary(ret, SET, value);
+ return ValueBuilder::makeSeq(
+ store,
+ ValueBuilder::makeSub(ValueBuilder::makeName(HEAP32), zero)
+ );
+ }
default: {
std::cerr << "Unhandled unary i32 operator: " << curr
<< std::endl;
@@ -1442,6 +1455,17 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
case DemoteFloat64:
return makeAsmCoercion(visit(curr->value, EXPRESSION_RESULT),
ASM_FLOAT);
+ case ReinterpretInt32: {
+ // Like above, assume address 0 is unused.
+ Ref zero = ValueBuilder::makeInt(0);
+ Ref ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP32), zero);
+ Ref value = visit(curr->value, EXPRESSION_RESULT);
+ Ref store = ValueBuilder::makeBinary(ret, SET, value);
+ return ValueBuilder::makeSeq(
+ store,
+ ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF32), zero)
+ );
+ }
// TODO: more complex unary conversions
default:
std::cerr << "Unhandled unary float operator: " << curr
diff --git a/test/wasm2asm/reinterpret.2asm.js b/test/wasm2asm/reinterpret.2asm.js
new file mode 100644
index 000000000..3d770efe1
--- /dev/null
+++ b/test/wasm2asm/reinterpret.2asm.js
@@ -0,0 +1,77 @@
+function asmFunc(global, env, buffer) {
+ "use asm";
+ var HEAP8 = new global.Int8Array(buffer);
+ var HEAP16 = new global.Int16Array(buffer);
+ var HEAP32 = new global.Int32Array(buffer);
+ var HEAPU8 = new global.Uint8Array(buffer);
+ var HEAPU16 = new global.Uint16Array(buffer);
+ var HEAPU32 = new global.Uint32Array(buffer);
+ var HEAPF32 = new global.Float32Array(buffer);
+ var HEAPF64 = new global.Float64Array(buffer);
+ var Math_imul = global.Math.imul;
+ var Math_fround = global.Math.fround;
+ var Math_abs = global.Math.abs;
+ var Math_clz32 = global.Math.clz32;
+ var Math_min = global.Math.min;
+ var Math_max = global.Math.max;
+ var i64toi32_i32$HIGH_BITS = 0;
+ function dummy() {
+
+ }
+
+ function $1($0) {
+ $0 = $0 | 0;
+ return ((HEAPF32[0] = (HEAP32[0] = $0, HEAPF32[0]), HEAP32[0]) | 0) == ($0 | 0) | 0;
+ }
+
+ function $2($0, $0$hi) {
+ $0 = $0 | 0;
+ $0$hi = $0$hi | 0;
+ HEAP32[0 >> 2] = $0;
+ HEAP32[(0 + 4 | 0) >> 2] = $0$hi;
+ HEAPF64[0 >> 3] = +HEAPF64[0 >> 3];
+ return (HEAP32[0 >> 2] | 0 | 0) == ($0 | 0) & (HEAP32[(0 + 4 | 0) >> 2] | 0 | 0) == ($0$hi | 0) | 0 | 0;
+ }
+
+ function __wasm_ctz_i32(x) {
+ x = x | 0;
+ var $1 = 0;
+ if ((x | 0) == (0 | 0)) $1 = 32; else $1 = 31 - Math_clz32(x ^ (x - 1 | 0) | 0) | 0;
+ return $1 | 0;
+ }
+
+ function __wasm_popcnt_i32(x) {
+ x = x | 0;
+ var count = 0, $2 = 0;
+ count = 0;
+ b : {
+ l : do {
+ $2 = count;
+ if ((x | 0) == (0 | 0)) break b;
+ x = x & (x - 1 | 0) | 0;
+ count = count + 1 | 0;
+ continue l;
+ break l;
+ } while (1);
+ };
+ return $2 | 0;
+ }
+
+ function __wasm_rotl_i32(x, k) {
+ x = x | 0;
+ k = k | 0;
+ return ((4294967295 >>> (k & 31 | 0) | 0) & x | 0) << (k & 31 | 0) | 0 | (((4294967295 << (32 - (k & 31 | 0) | 0) | 0) & x | 0) >>> (32 - (k & 31 | 0) | 0) | 0) | 0 | 0;
+ }
+
+ function __wasm_rotr_i32(x, k) {
+ x = x | 0;
+ k = k | 0;
+ return ((4294967295 << (k & 31 | 0) | 0) & x | 0) >>> (k & 31 | 0) | 0 | (((4294967295 >>> (32 - (k & 31 | 0) | 0) | 0) & x | 0) << (32 - (k & 31 | 0) | 0) | 0) | 0 | 0;
+ }
+
+ return {
+ i32_roundtrip: $1,
+ i64_roundtrip: $2
+ };
+}
+
diff --git a/test/wasm2asm/reinterpret.wast b/test/wasm2asm/reinterpret.wast
new file mode 100644
index 000000000..816fb90a3
--- /dev/null
+++ b/test/wasm2asm/reinterpret.wast
@@ -0,0 +1,24 @@
+(module
+ (func $dummy)
+
+ (func (export "i32_roundtrip") (param $0 i32) (result i32)
+ (i32.eq (i32.reinterpret (f32.reinterpret (get_local $0))) (get_local $0)))
+ (func (export "i64_roundtrip") (param $0 i64) (result i32)
+ (i64.eq (i64.reinterpret (f64.reinterpret (get_local $0))) (get_local $0)))
+)
+
+(assert_return (invoke "i32_roundtrip" (i32.const 0))
+ (i32.const 1))
+(assert_return (invoke "i32_roundtrip" (i32.const 1))
+ (i32.const 1))
+(assert_return (invoke "i32_roundtrip" (i32.const 100))
+ (i32.const 1))
+(assert_return (invoke "i32_roundtrip" (i32.const 10000))
+ (i32.const 1))
+
+(assert_return (invoke "i64_roundtrip" (i32.const 0) (i32.const 0))
+ (i32.const 1))
+(assert_return (invoke "i64_roundtrip" (i32.const 1) (i32.const 0))
+ (i32.const 1))
+(assert_return (invoke "i64_roundtrip" (i32.const 0) (i32.const 1))
+ (i32.const 1))