/src/wasm3/source/m3_atomic.h
Line | Count | Source |
1 | | // |
2 | | // m3_atomic.h |
3 | | // |
4 | | // The primitives under the threads proposal's atomic accesses. Each takes the |
5 | | // address of a naturally aligned cell in linear memory - the interpreter has |
6 | | // checked that already - and works in host byte order; a big-endian host swaps |
7 | | // on the way in and out, as the ordinary loads and stores do. |
8 | | // |
9 | | // The width of the cell is a value, its log2 from 0 to 3, and so is the operation of |
10 | | // a read-modify-write, so that one body serves every instruction of a kind. Values |
11 | | // travel as u64 and are cut to the width on the way into the cell. |
12 | | // |
13 | | // Every read-modify-write returns the value the cell held before. |
14 | | // |
15 | | // With one thread the accesses only have to be correct in sequence, so these are |
16 | | // plain reads and writes. |
17 | | // |
18 | | |
19 | | #ifndef m3_atomic_h |
20 | | #define m3_atomic_h |
21 | | |
22 | | #include "m3_core.h" |
23 | | |
24 | | #if d_m3HasAtomics |
25 | | |
26 | | d_m3BeginExternC |
27 | | |
28 | | enum { |
29 | | c_atomicAdd, |
30 | | c_atomicSub, |
31 | | c_atomicAnd, |
32 | | c_atomicOr, |
33 | | c_atomicXor, |
34 | | c_atomicXchg |
35 | | }; |
36 | | |
37 | | # define d_m3AtomicTyped(T) \ |
38 | | static inline T m3_AtomicLoad_##T (const u8* i_cell) \ |
39 | 956k | { \ |
40 | 956k | T value; \ |
41 | 956k | memcpy (& value, i_cell, sizeof (value)); \ |
42 | 956k | M3_BSWAP_##T (value); \ |
43 | 956k | return value; \ |
44 | 956k | } \ m3_compile.c:m3_AtomicLoad_u32 Line | Count | Source | 39 | 156k | { \ | 40 | 156k | T value; \ | 41 | 156k | memcpy (& value, i_cell, sizeof (value)); \ | 42 | 156k | M3_BSWAP_##T (value); \ | 43 | 156k | return value; \ | 44 | 156k | } \ |
m3_compile.c:m3_AtomicLoad_u64 Line | Count | Source | 39 | 230k | { \ | 40 | 230k | T value; \ | 41 | 230k | memcpy (& value, i_cell, sizeof (value)); \ | 42 | 230k | M3_BSWAP_##T (value); \ | 43 | 230k | return value; \ | 44 | 230k | } \ |
m3_compile.c:m3_AtomicLoad_u8 Line | Count | Source | 39 | 416k | { \ | 40 | 416k | T value; \ | 41 | 416k | memcpy (& value, i_cell, sizeof (value)); \ | 42 | 416k | M3_BSWAP_##T (value); \ | 43 | 416k | return value; \ | 44 | 416k | } \ |
m3_compile.c:m3_AtomicLoad_u16 Line | Count | Source | 39 | 153k | { \ | 40 | 153k | T value; \ | 41 | 153k | memcpy (& value, i_cell, sizeof (value)); \ | 42 | 153k | M3_BSWAP_##T (value); \ | 43 | 153k | return value; \ | 44 | 153k | } \ |
|
45 | | \ |
46 | | static inline void m3_AtomicStore_##T (u8* o_cell, T i_value) \ |
47 | 697k | { \ |
48 | 697k | M3_BSWAP_##T (i_value); \ |
49 | 697k | memcpy (o_cell, & i_value, sizeof (i_value)); \ |
50 | 697k | } m3_compile.c:m3_AtomicStore_u8 Line | Count | Source | 47 | 330k | { \ | 48 | 330k | M3_BSWAP_##T (i_value); \ | 49 | 330k | memcpy (o_cell, & i_value, sizeof (i_value)); \ | 50 | 330k | } |
m3_compile.c:m3_AtomicStore_u16 Line | Count | Source | 47 | 115k | { \ | 48 | 115k | M3_BSWAP_##T (i_value); \ | 49 | 115k | memcpy (o_cell, & i_value, sizeof (i_value)); \ | 50 | 115k | } |
m3_compile.c:m3_AtomicStore_u32 Line | Count | Source | 47 | 117k | { \ | 48 | 117k | M3_BSWAP_##T (i_value); \ | 49 | 117k | memcpy (o_cell, & i_value, sizeof (i_value)); \ | 50 | 117k | } |
m3_compile.c:m3_AtomicStore_u64 Line | Count | Source | 47 | 134k | { \ | 48 | 134k | M3_BSWAP_##T (i_value); \ | 49 | 134k | memcpy (o_cell, & i_value, sizeof (i_value)); \ | 50 | 134k | } |
|
51 | | |
52 | | d_m3AtomicTyped(u8); |
53 | | d_m3AtomicTyped(u16); |
54 | | d_m3AtomicTyped(u32); |
55 | | d_m3AtomicTyped(u64); |
56 | | |
57 | | static inline |
58 | | u64 m3_AtomicLoadW (const u8* i_cell, u32 i_log2Width) |
59 | 954k | { |
60 | 954k | switch (i_log2Width) { |
61 | 416k | case 0: return m3_AtomicLoad_u8(i_cell); |
62 | 153k | case 1: return m3_AtomicLoad_u16(i_cell); |
63 | 156k | case 2: return m3_AtomicLoad_u32(i_cell); |
64 | 228k | default: return m3_AtomicLoad_u64(i_cell); |
65 | 954k | } |
66 | 954k | } |
67 | | |
68 | | static inline |
69 | | void m3_AtomicStoreW (u8* o_cell, u32 i_log2Width, u64 i_value) |
70 | 697k | { |
71 | 697k | switch (i_log2Width) { |
72 | 330k | case 0: m3_AtomicStore_u8(o_cell, (u8)i_value); break; |
73 | 115k | case 1: m3_AtomicStore_u16(o_cell, (u16)i_value); break; |
74 | 117k | case 2: m3_AtomicStore_u32(o_cell, (u32)i_value); break; |
75 | 134k | default: m3_AtomicStore_u64(o_cell, i_value); break; |
76 | 697k | } |
77 | 697k | } |
78 | | |
79 | | static inline |
80 | | u64 m3_AtomicRmwW (u8* io_cell, u32 i_log2Width, u32 i_kind, u64 i_value) |
81 | 394k | { |
82 | 394k | u64 old = m3_AtomicLoadW(io_cell, i_log2Width); |
83 | 394k | u64 updated; |
84 | | |
85 | 394k | switch (i_kind) { |
86 | 125k | case c_atomicAdd: updated = old + i_value; break; |
87 | 83.0k | case c_atomicSub: updated = old - i_value; break; |
88 | 16.5k | case c_atomicAnd: updated = old & i_value; break; |
89 | 72.8k | case c_atomicOr: updated = old | i_value; break; |
90 | 39.3k | case c_atomicXor: updated = old ^ i_value; break; |
91 | 57.5k | default: updated = i_value; break; |
92 | 394k | } |
93 | | |
94 | 394k | m3_AtomicStoreW(io_cell, i_log2Width, updated); |
95 | 394k | return old; |
96 | 394k | } |
97 | | |
98 | | static inline |
99 | | u64 m3_AtomicCmpxchgW (u8* io_cell, u32 i_log2Width, u64 i_expected, u64 i_replacement) |
100 | 297k | { |
101 | 297k | u64 old = m3_AtomicLoadW(io_cell, i_log2Width); |
102 | 297k | u64 mask = (i_log2Width == 3) ? ~(u64)0 : (((u64)1 << (8u << i_log2Width)) - 1); |
103 | | |
104 | 297k | if (old == (i_expected & mask)) { |
105 | 206k | m3_AtomicStoreW(io_cell, i_log2Width, i_replacement); |
106 | 206k | } |
107 | 297k | return old; |
108 | 297k | } |
109 | | |
110 | | d_m3EndExternC |
111 | | |
112 | | #endif // d_m3HasAtomics |
113 | | |
114 | | #endif // m3_atomic_h |