Skip to content

Commit 4deed1e

Browse files
authored
add i64_atomics_* support to asm2wasm (#1262)
* add i64_atomics_* support to asm2wasm * OptimizeInstructions: atomic loads can't be signed
1 parent 82d693b commit 4deed1e

12 files changed

Lines changed: 611 additions & 1 deletion

src/asm2wasm.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ Name I32_CTTZ("i32_cttz"),
119119
ATOMICS_AND("and"),
120120
ATOMICS_OR("or"),
121121
ATOMICS_XOR("xor"),
122+
I64_ATOMICS_LOAD("i64_atomics_load"),
123+
I64_ATOMICS_STORE("i64_atomics_store"),
124+
I64_ATOMICS_AND("i64_atomics_and"),
125+
I64_ATOMICS_OR("i64_atomics_or"),
126+
I64_ATOMICS_XOR("i64_atomics_xor"),
127+
I64_ATOMICS_ADD("i64_atomics_add"),
128+
I64_ATOMICS_SUB("i64_atomics_sub"),
129+
I64_ATOMICS_EXCHANGE("i64_atomics_exchange"),
130+
I64_ATOMICS_COMPAREEXCHANGE("i64_atomics_compareExchange"),
122131
EMSCRIPTEN_DEBUGINFO("emscripten_debuginfo");
123132

124133
// Utilities
@@ -1994,6 +2003,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
19942003
if (name == I64_CTTZ) return builder.makeUnary(UnaryOp::CtzInt64, value);
19952004
if (name == I64_CTLZ) return builder.makeUnary(UnaryOp::ClzInt64, value);
19962005
if (name == I64_CTPOP) return builder.makeUnary(UnaryOp::PopcntInt64, value);
2006+
if (name == I64_ATOMICS_LOAD) return builder.makeAtomicLoad(8, 0, value, i64);
19972007
} else if (num == 2) { // 2 params,binary
19982008
if (name == I64_CONST) return builder.makeConst(getLiteral(ast));
19992009
auto* left = process(ast[2][0]);
@@ -2035,6 +2045,40 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
20352045
if (name == I64_SLT) return builder.makeBinary(BinaryOp::LtSInt64, left, right);
20362046
if (name == I64_UGT) return builder.makeBinary(BinaryOp::GtUInt64, left, right);
20372047
if (name == I64_SGT) return builder.makeBinary(BinaryOp::GtSInt64, left, right);
2048+
// atomics
2049+
if (name == I64_ATOMICS_STORE) {
2050+
wasm.memory.shared = true;
2051+
return builder.makeAtomicStore(8, 0, left, right, i64);
2052+
}
2053+
if (name == I64_ATOMICS_ADD) {
2054+
wasm.memory.shared = true;
2055+
return builder.makeAtomicRMW(AtomicRMWOp::Add, 8, 0, left, right, i64);
2056+
}
2057+
if (name == I64_ATOMICS_SUB) {
2058+
wasm.memory.shared = true;
2059+
return builder.makeAtomicRMW(AtomicRMWOp::Sub, 8, 0, left, right, i64);
2060+
}
2061+
if (name == I64_ATOMICS_AND) {
2062+
wasm.memory.shared = true;
2063+
return builder.makeAtomicRMW(AtomicRMWOp::And, 8, 0, left, right, i64);
2064+
}
2065+
if (name == I64_ATOMICS_OR) {
2066+
wasm.memory.shared = true;
2067+
return builder.makeAtomicRMW(AtomicRMWOp::Or, 8, 0, left, right, i64);
2068+
}
2069+
if (name == I64_ATOMICS_XOR) {
2070+
wasm.memory.shared = true;
2071+
return builder.makeAtomicRMW(AtomicRMWOp::Xor, 8, 0, left, right, i64);
2072+
}
2073+
if (name == I64_ATOMICS_EXCHANGE) {
2074+
wasm.memory.shared = true;
2075+
return builder.makeAtomicRMW(AtomicRMWOp::Xchg, 8, 0, left, right, i64);
2076+
}
2077+
} else if (num == 3) {
2078+
if (name == I64_ATOMICS_COMPAREEXCHANGE) {
2079+
wasm.memory.shared = true;
2080+
return builder.makeAtomicCmpxchg(8, 0, process(ast[2][0]), process(ast[2][1]), process(ast[2][2]), i64);
2081+
}
20382082
}
20392083
break;
20402084
}

src/ir/load-utils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ inline bool isSignRelevant(Load* load) {
3232
return !isWasmTypeFloat(type) && load->bytes < getWasmTypeSize(type);
3333
}
3434

35+
// check if a load can be signed (which some opts want to do)
36+
inline bool canBeSigned(Load* load) {
37+
return !load->isAtomic;
38+
}
39+
3540
} // namespace LoadUtils
3641

3742
} // namespace wasm

src/passes/OptimizeInstructions.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,8 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
422422
if (extraShifts == 0) {
423423
if (auto* load = getFallthrough(ext)->dynCast<Load>()) {
424424
// pattern match a load of 8 bits and a sign extend using a shl of 24 then shr_s of 24 as well, etc.
425-
if ((load->bytes == 1 && bits == 8) || (load->bytes == 2 && bits == 16)) {
425+
if (LoadUtils::canBeSigned(load) &&
426+
((load->bytes == 1 && bits == 8) || (load->bytes == 2 && bits == 16))) {
426427
// if the value falls through, we can't alter the load, as it might be captured in a tee
427428
if (load->signed_ == true || load == ext) {
428429
load->signed_ = true;

test/passes/optimize-instructions.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,3 +2199,20 @@
21992199
)
22002200
)
22012201
)
2202+
(module
2203+
(type $0 (func))
2204+
(import "env" "memory" (memory $0 (shared 256 256)))
2205+
(func $x (; 0 ;) (type $0)
2206+
(drop
2207+
(i32.shr_s
2208+
(i32.shl
2209+
(i32.atomic.load8_u
2210+
(i32.const 100)
2211+
)
2212+
(i32.const 24)
2213+
)
2214+
(i32.const 24)
2215+
)
2216+
)
2217+
)
2218+
)

test/passes/optimize-instructions.wast

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,3 +2641,19 @@
26412641
)
26422642
)
26432643
)
2644+
(module
2645+
(import "env" "memory" (memory $0 (shared 256 256)))
2646+
(func $x
2647+
(drop
2648+
(i32.shr_s
2649+
(i32.shl
2650+
(i32.atomic.load8_u ;; can't be signed
2651+
(i32.const 100)
2652+
)
2653+
(i32.const 24)
2654+
)
2655+
(i32.const 24)
2656+
)
2657+
)
2658+
)
2659+
)

test/threads.wasm-only.asm.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// Test wasm-only builds. In this case, fastcomp emits code that is
3+
// not asm.js, it will only ever run as wasm, and contains special intrinsics for
4+
// asm2wasm that map LLVM IR into i64s.
5+
//
6+
7+
function asm(global, env, buffer) {
8+
"use asm";
9+
10+
var HEAP8 = new global.Int8Array(buffer);
11+
var HEAP16 = new global.Int16Array(buffer);
12+
var HEAP32 = new global.Int32Array(buffer);
13+
var HEAPU8 = new global.Uint8Array(buffer);
14+
var HEAPU16 = new global.Uint16Array(buffer);
15+
var HEAPU32 = new global.Uint32Array(buffer);
16+
var HEAPF32 = new global.Float32Array(buffer);
17+
var HEAPF64 = new global.Float64Array(buffer);
18+
19+
var STACKTOP = env.STACKTOP | 0;
20+
21+
var fround = global.Math.fround;
22+
var Math_imul = global.Math.imul;
23+
24+
var illegalImport = env.illegalImport;
25+
var illegalImportResult = env.illegalImportResult;
26+
27+
var _fabsf = env._fabsf;
28+
var do_i64 = env.do_i64;
29+
var abort = env.abort;
30+
31+
function test64() {
32+
var x = i64(), y = i64(), z = 0; // define i64 variables using special intrinsic
33+
var int32 = 0, float32 = fround(0), float64 = +0;
34+
i64_atomics_store(4656, i64_const(92, 0))|0;
35+
x = i64_atomics_load(4656);
36+
y = i64_atomics_add(int32, i64_const(26, 0))|0;
37+
x = i64_atomics_sub(1024, y)|0;
38+
y = i64_atomics_and(1024, x)|0;
39+
x = i64_atomics_or(1024, y)|0;
40+
y = i64_atomics_xor(1024, x)|0;
41+
x = i64_atomics_exchange(1024, y)|0;
42+
y = i64_atomics_compareExchange(1024, x, y)|0;
43+
return x;
44+
}
45+
46+
return { test64: test64 };
47+
}
48+

test/threads.wasm-only.fromasm

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
(module
2+
(import "env" "memory" (memory $0 (shared 256 256)))
3+
(import "env" "table" (table 0 0 anyfunc))
4+
(import "env" "memoryBase" (global $memoryBase i32))
5+
(import "env" "tableBase" (global $tableBase i32))
6+
(global $tempRet0 (mut i32) (i32.const 0))
7+
(data (get_global $memoryBase) "threads.wasm-only.asm.js")
8+
(export "test64" (func $legalstub$test64))
9+
(func $test64 (; 0 ;) (result i64)
10+
(local $0 i64)
11+
(local $1 i64)
12+
(local $2 i32)
13+
(i64.atomic.store
14+
(i32.const 4656)
15+
(i64.const 92)
16+
)
17+
(drop
18+
(i64.atomic.load
19+
(i32.const 4656)
20+
)
21+
)
22+
(drop
23+
(i64.atomic.rmw.cmpxchg
24+
(i32.const 1024)
25+
(tee_local $1
26+
(i64.atomic.rmw.xchg
27+
(i32.const 1024)
28+
(tee_local $0
29+
(i64.atomic.rmw.xor
30+
(i32.const 1024)
31+
(i64.atomic.rmw.or
32+
(i32.const 1024)
33+
(i64.atomic.rmw.and
34+
(i32.const 1024)
35+
(i64.atomic.rmw.sub
36+
(i32.const 1024)
37+
(i64.atomic.rmw.add
38+
(get_local $2)
39+
(i64.const 26)
40+
)
41+
)
42+
)
43+
)
44+
)
45+
)
46+
)
47+
)
48+
(get_local $0)
49+
)
50+
)
51+
(get_local $1)
52+
)
53+
(func $legalstub$test64 (; 1 ;) (result i32)
54+
(local $0 i64)
55+
(set_local $0
56+
(call $test64)
57+
)
58+
(set_global $tempRet0
59+
(i32.wrap/i64
60+
(i64.shr_u
61+
(get_local $0)
62+
(i64.const 32)
63+
)
64+
)
65+
)
66+
(i32.wrap/i64
67+
(get_local $0)
68+
)
69+
)
70+
)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
(module
2+
(import "env" "memory" (memory $0 (shared 256 256)))
3+
(import "env" "table" (table 0 0 anyfunc))
4+
(import "env" "memoryBase" (global $memoryBase i32))
5+
(import "env" "tableBase" (global $tableBase i32))
6+
(global $tempRet0 (mut i32) (i32.const 0))
7+
(data (get_global $memoryBase) "threads.wasm-only.asm.js")
8+
(export "test64" (func $legalstub$test64))
9+
(func $test64 (; 0 ;) (result i64)
10+
(local $0 i64)
11+
(local $1 i64)
12+
(local $2 i32)
13+
(i64.atomic.store
14+
(i32.const 4656)
15+
(i64.const 92)
16+
)
17+
(drop
18+
(i64.atomic.load
19+
(i32.const 4656)
20+
)
21+
)
22+
(drop
23+
(i64.atomic.rmw.cmpxchg
24+
(i32.const 1024)
25+
(tee_local $1
26+
(i64.atomic.rmw.xchg
27+
(i32.const 1024)
28+
(tee_local $0
29+
(i64.atomic.rmw.xor
30+
(i32.const 1024)
31+
(i64.atomic.rmw.or
32+
(i32.const 1024)
33+
(i64.atomic.rmw.and
34+
(i32.const 1024)
35+
(i64.atomic.rmw.sub
36+
(i32.const 1024)
37+
(i64.atomic.rmw.add
38+
(get_local $2)
39+
(i64.const 26)
40+
)
41+
)
42+
)
43+
)
44+
)
45+
)
46+
)
47+
)
48+
(get_local $0)
49+
)
50+
)
51+
(get_local $1)
52+
)
53+
(func $legalstub$test64 (; 1 ;) (result i32)
54+
(local $0 i64)
55+
(set_local $0
56+
(call $test64)
57+
)
58+
(set_global $tempRet0
59+
(i32.wrap/i64
60+
(i64.shr_u
61+
(get_local $0)
62+
(i64.const 32)
63+
)
64+
)
65+
)
66+
(i32.wrap/i64
67+
(get_local $0)
68+
)
69+
)
70+
)

0 commit comments

Comments
 (0)