Add multi-threaded tests for acquire/release atomics - #8999
Add multi-threaded tests for acquire/release atomics#8999stevenfontanella wants to merge 4 commits into
Conversation
b28920c to
b9ded3f
Compare
| ;; 1, 3 is only possible with acqrel, while others are also possible with | ||
| ;; seqcst. | ||
| (assert_return (invoke "check") | ||
| (either (i32.const 1) (i32.const 4)) |
There was a problem hiding this comment.
Is there a way to express "either (1, 2) or (3, 4) or (2, 4)" with the either construct? Or would you have to write test code to explicitly check that kind of condition?
There was a problem hiding this comment.
Right, I was having trouble understanding how to read this because I would have expected either to be more powerful. It's fine for this case, but it would be useful to have more expressive power for the seqcst case.
There was a problem hiding this comment.
I'll leave it for now, and if we have a seqcst test in the future it might make more sense to make both into a check function.
| ;; Store observed flag at address 8, observed payload at address 12 | ||
| (i32.store (i32.const 8) (i32.atomic.load acqrel (i32.const 0))) |
There was a problem hiding this comment.
But the flag was written at address 0, right?
It might be nice to give the addresses "names" by having immutable globals named $x, $flag, etc. that are initialized with the same values in every module.
It would also be nice to have pseudocode like x =un 1; flag =rel 1 in the comments.
There was a problem hiding this comment.
Yes, I think this code is correct, can you rephrase if I missed something? We're reading the flag here (address 0), and writing the observation to address 8. Added comments on each of the loads and stores.
(For posterity, we discussed in person that adding globals to name the addresses would be more noisy since they can't be shared across threads and we'd have lots of extra global.gets)
There was a problem hiding this comment.
I was confused and the new names help, thanks! This the other case below LGTM.
| ;; Observed flag - address 8 | ||
| (i32.store (i32.const 8) (i32.atomic.load acqrel (i32.const 0))) |
There was a problem hiding this comment.
Looks like we have the same address mismatch here.
There was a problem hiding this comment.
Ditto here, it still looks correct to me after reading it again. Let me know if I missed something.
| (func $lock | ||
| (loop $spin | ||
| (if (i32.eqz (i32.atomic.rmw.cmpxchg acqrel (i32.const 0) (i32.const 0) (i32.const 1))) | ||
| (then (return)) | ||
| ) | ||
| (pause) | ||
| (br $spin) | ||
| ) | ||
| ) |
There was a problem hiding this comment.
It's awfully inconvenient to have to repeat so much of the module contents on each thread! It might be worth going back and adding some richer .wast infrastructure either in the original threads repo or as part of the acquire-release proposal.
There was a problem hiding this comment.
I looked into this and one potential solution is the (module definition ...) and (module instance ...) syntax from the upstream spec interpreter (example). I think we would have one module that exports the shared memory, then a module definition that imports the shared memory and exports functions for the spinlock, then each threads instantiates the second module separately.
But I think we should wait on this solution, since the threads repo (and thus the acquire-release-atomics repo) is behind the upstream spec and doesn't support this syntax, so we wouldn't be able to run this on the spec interpreter. I'd suggest that we keep it this way and simplify the test once the threads + acquire-release-atomics interpreter have support for this syntax.
| ;; a =rel 1 | ||
| ;; b =rel 2 |
There was a problem hiding this comment.
The convention in the memory model papers is to use x,y,z as memory locations and a,b,c or r1,r2,r3 as local variables / registers. So to match those conventions, these should be x =rel 1, y =rel 2.
| (module | ||
| (memory (import "mem" "shared") 1 1 shared) | ||
| (func (export "run") | ||
| ;; payload =un 42 |
There was a problem hiding this comment.
But I also like using descriptive names as payload and flag. Let's not change those.
| (func $lock | ||
| (loop $spin | ||
| (if (i32.eqz (i32.atomic.rmw.cmpxchg acqrel (i32.const 0) (i32.const 0) (i32.const 1))) | ||
| (then (return)) | ||
| ) | ||
| (pause) | ||
| (br $spin) | ||
| ) | ||
| ) |

seqcst. It's impossible to write a test that would fail on an engine that implementsacqrelusingseqcst, since strengthening an atomic operation is always sound.(atomic.fence acqrel)currently can't be exercised in a way that's correct without being completely redundant alongside another acqrel load/store. With a relaxed memory ordering the test would make more sense.basic.wasttest which was previously unexercised(thread ...)and(wait ...)expressions are captured in the split output (the latter is currently a no-op anyway but best to include it for readability and correctness).(thread)blocks always run sequentially in a blocking manner.