Coverage Report

Created: 2026-11-12 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm3/source/m3_exec_atomic.h
Line
Count
Source
1
//
2
//  m3_exec_atomic.h
3
//
4
//  The atomic instructions of the threads proposal, included from m3_exec.h.
5
//
6
//  Operands follow Compile_Atomic: the topmost is in _r0, and the rest are read
7
//  from slots in the order the stack gives them, topmost first, with the address
8
//  always the last. The access itself is an immediate after them. A load has only
9
//  the address, so it is in _r0.
10
//
11
//  The trap order is the spec's: an address that is not aligned traps before one
12
//  that is out of bounds, and both before anything is read or written.
13
//
14
15
#if d_m3HasAtomics
16
17
// WIDTH is the size of the cell in bytes, and EA a u64 already holding the effective
18
// address. An access leaves the bounds to m3MemCheck, which a guarded memory answers
19
// with the fault its reservation raises; notify and wait may never touch the cell, so
20
// they always check.
21
#  define d_m3AtomicCheckWith(IN_BOUNDS, WIDTH, EA)                         \
22
1.53M
      if (M3_UNLIKELY((EA) & ((WIDTH) - 1))) {                              \
23
22
          newTrap(m3Err_trapUnalignedAtomic);                               \
24
22
      }                                                                     \
25
1.53M
      if (not IN_BOUNDS((EA) + (WIDTH) <= _mem->length)) {                  \
26
15
          d_outOfBoundsMemOp((size_t)(EA), (u32)(WIDTH));                   \
27
15
      }
28
29
1.53M
#  define d_m3AtomicCheck(WIDTH, EA)        d_m3AtomicCheckWith(m3MemCheck, WIDTH, EA)
30
4.88k
#  define d_m3AtomicCheckAlways(WIDTH, EA)  d_m3AtomicCheckWith(M3_LIKELY, WIDTH, EA)
31
32
109k
#  define d_m3AtomicCell(EA)                (m3MemData(_mem) + (EA))
33
34
35
// One op per kind of access. The last immediate describes the instruction:
36
//
37
//   bits 0-1  log2 of the width of the cell
38
//   bit  2    the value type is i64 rather than i32
39
//   bits 3-5  for a read-modify-write, which one (c_atomicAdd and the rest)
40
1.53M
#  define d_m3AtomicLog2Width(DESC)  ((DESC) & 3u)
41
1.79M
#  define d_m3AtomicIs64(DESC)       (((DESC) >> 2) & 1u)
42
#  define d_m3AtomicKind(DESC)       (((DESC) >> 3) & 7u)
43
44
// an i32 result is kept sign-extended in the register, as an i32 load leaves it
45
1.42M
#  define d_m3AtomicResult(DESC, V)  (d_m3AtomicIs64(DESC) ? (m3reg_t)(V) : (m3reg_t)(i32)(V))
46
47
d_m3Op(AtomicLoad)
48
268k
{
49
268k
    u64 ea = (u64)(u32)_r0;
50
268k
    ea += immediate(u32);
51
268k
    u32 desc = immediate(u32);
52
268k
    u32 lw = d_m3AtomicLog2Width(desc);
53
268k
    d_m3AtomicCheck((u64)1 << lw, ea);
54
55
268k
    _r0 = d_m3AtomicResult(desc, m3_AtomicLoadW(d_m3AtomicCell(ea), lw));
56
268k
    nextOp();
57
268k
}
58
59
d_m3Op(AtomicStore)
60
106k
{
61
106k
    u64 ea = slot(u32);
62
106k
    ea += immediate(u32);
63
106k
    u32 desc = immediate(u32);
64
106k
    u32 lw = d_m3AtomicLog2Width(desc);
65
106k
    d_m3AtomicCheck((u64)1 << lw, ea);
66
67
106k
    m3_AtomicStoreW(d_m3AtomicCell(ea), lw, (u64)_r0);
68
106k
    nextOp();
69
106k
}
70
71
d_m3Op(AtomicRmw)
72
784k
{
73
784k
    u64 ea = slot(u32);
74
784k
    ea += immediate(u32);
75
784k
    u32 desc = immediate(u32);
76
784k
    u32 lw = d_m3AtomicLog2Width(desc);
77
784k
    d_m3AtomicCheck((u64)1 << lw, ea);
78
79
784k
    _r0 = d_m3AtomicResult(desc, m3_AtomicRmwW(d_m3AtomicCell(ea), lw, d_m3AtomicKind(desc), (u64)_r0));
80
784k
    nextOp();
81
784k
}
82
83
// the value compared against is cut to the width of the cell. Its slot comes before
84
// the description that says how wide it is, so it is taken by number first.
85
d_m3Op(AtomicCmpxchg)
86
370k
{
87
370k
    i32 expectedSlot = immediate(i32);
88
370k
    u64 ea = slot(u32);
89
370k
    ea += immediate(u32);
90
370k
    u32 desc = immediate(u32);
91
370k
    u32 lw = d_m3AtomicLog2Width(desc);
92
370k
    d_m3AtomicCheck((u64)1 << lw, ea);
93
94
370k
    u64 expected = d_m3AtomicIs64(desc) ? *(u64*)(_sp + expectedSlot) : (u64) * (u32*)(_sp + expectedSlot);
95
96
370k
    _r0 = d_m3AtomicResult(desc, m3_AtomicCmpxchgW(d_m3AtomicCell(ea), lw, expected, (u64)_r0));
97
370k
    nextOp();
98
370k
}
99
100
101
// memory.atomic.notify: nothing waits on a memory only one thread can reach
102
d_m3Op(AtomicNotify)
103
2.03k
{
104
2.03k
    u64 ea = slot(u32);
105
2.03k
    ea += immediate(u32);
106
2.03k
    d_m3AtomicCheckAlways(sizeof(u32), ea);
107
108
2.02k
    _r0 = 0;
109
2.02k
    nextOp();
110
2.03k
}
111
112
113
// The wait itself, for a cell of type T. The result is 1 when the cell did not hold
114
// the expected value, and 2 when the timeout ran out. A negative timeout is
115
// infinite, which with one thread is a wait that nothing can end.
116
#  define d_m3AtomicWaitOp(NAME, T)                                          \
117
      d_m3Op(NAME)                                                           \
118
2.85k
      {                                                                      \
119
2.85k
          i64 timeout = (i64)_r0;                                            \
120
2.85k
          T expected = (T)slot(T);                                           \
121
2.85k
          u64 ea = slot(u32);                                                \
122
2.85k
          ea += immediate(u32);                                              \
123
2.85k
          d_m3AtomicCheckAlways(sizeof(T), ea);                              \
124
2.84k
                                                                             \
125
2.84k
          if (M3_UNLIKELY(not m3MemInfo(_mem)->isShared)) {                  \
126
2
              newTrap(m3Err_trapExpectedSharedMemory);                       \
127
2
          }                                                                  \
128
2.84k
                                                                             \
129
2.84k
          if (m3_AtomicLoad_##T(d_m3AtomicCell(ea)) != expected) {           \
130
2.08k
              _r0 = 1;                                                       \
131
2.08k
              nextOp();                                                      \
132
2.08k
          }                                                                  \
133
2.84k
                                                                             \
134
2.84k
          if (timeout < 0) {                                                 \
135
16
              newTrap(m3Err_trapWaitForever);                                \
136
16
          }                                                                  \
137
756
                                                                             \
138
756
          m3_HostSleepNs((u64)timeout);                                      \
139
740
          _r0 = 2;                                                           \
140
740
          nextOp();                                                          \
141
756
      }
m3_compile.c:op_AtomicWait32
Line
Count
Source
118
4
      {                                                                      \
119
4
          i64 timeout = (i64)_r0;                                            \
120
4
          T expected = (T)slot(T);                                           \
121
4
          u64 ea = slot(u32);                                                \
122
4
          ea += immediate(u32);                                              \
123
4
          d_m3AtomicCheckAlways(sizeof(T), ea);                              \
124
1
                                                                             \
125
1
          if (M3_UNLIKELY(not m3MemInfo(_mem)->isShared)) {                  \
126
1
              newTrap(m3Err_trapExpectedSharedMemory);                       \
127
1
          }                                                                  \
128
1
                                                                             \
129
1
          if (m3_AtomicLoad_##T(d_m3AtomicCell(ea)) != expected) {           \
130
0
              _r0 = 1;                                                       \
131
0
              nextOp();                                                      \
132
0
          }                                                                  \
133
0
                                                                             \
134
0
          if (timeout < 0) {                                                 \
135
0
              newTrap(m3Err_trapWaitForever);                                \
136
0
          }                                                                  \
137
0
                                                                             \
138
0
          m3_HostSleepNs((u64)timeout);                                      \
139
0
          _r0 = 2;                                                           \
140
0
          nextOp();                                                          \
141
0
      }
m3_compile.c:op_AtomicWait64
Line
Count
Source
118
2.84k
      {                                                                      \
119
2.84k
          i64 timeout = (i64)_r0;                                            \
120
2.84k
          T expected = (T)slot(T);                                           \
121
2.84k
          u64 ea = slot(u32);                                                \
122
2.84k
          ea += immediate(u32);                                              \
123
2.84k
          d_m3AtomicCheckAlways(sizeof(T), ea);                              \
124
2.84k
                                                                             \
125
2.84k
          if (M3_UNLIKELY(not m3MemInfo(_mem)->isShared)) {                  \
126
1
              newTrap(m3Err_trapExpectedSharedMemory);                       \
127
1
          }                                                                  \
128
2.84k
                                                                             \
129
2.84k
          if (m3_AtomicLoad_##T(d_m3AtomicCell(ea)) != expected) {           \
130
2.08k
              _r0 = 1;                                                       \
131
2.08k
              nextOp();                                                      \
132
2.08k
          }                                                                  \
133
2.84k
                                                                             \
134
2.84k
          if (timeout < 0) {                                                 \
135
16
              newTrap(m3Err_trapWaitForever);                                \
136
16
          }                                                                  \
137
756
                                                                             \
138
756
          m3_HostSleepNs((u64)timeout);                                      \
139
740
          _r0 = 2;                                                           \
140
740
          nextOp();                                                          \
141
756
      }
142
143
d_m3AtomicWaitOp(AtomicWait32, u32);
144
d_m3AtomicWaitOp(AtomicWait64, u64);
145
146
#endif // d_m3HasAtomics