diff options
-rw-r--r-- | src/passes/I64ToI32Lowering.cpp | 37 | ||||
-rw-r--r-- | src/wasm2asm.h | 24 | ||||
-rw-r--r-- | test/wasm2asm/reinterpret.2asm.js | 77 | ||||
-rw-r--r-- | test/wasm2asm/reinterpret.wast | 24 |
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)) |