Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Behavior of t.atomic.rmw.cmpxchg instructions #195

Open
q82419 opened this issue Nov 17, 2022 · 7 comments
Open

Behavior of t.atomic.rmw.cmpxchg instructions #195

q82419 opened this issue Nov 17, 2022 · 7 comments

Comments

@q82419
Copy link

q82419 commented Nov 17, 2022

Hi,

As the 17. and 18. steps in the spec:

17. If c1 equals c2, then:
    Let c be c2.
18. Else:
    Let c be c3.

And the 21. step will write the c back to the memory.
Where c1 is the origin value on the memory, c2 is the expected value, c3 is the value to replace.

Maybe I can realize this as following simply:

if (memory[addr] != expected)
    memory[addr] = replacement;

But the closest function in C++ std::atomic<T>::compare_exchange_strong has the different behavior.

if (memory[addr] == expected)
   memory[addr] = replacement;

The atomic_rmw instructions are similar to the member functions of std::atomic<T> in C++ STL, such as fetch_add(), fetch_xor, etc.

Is it necessary to have the same behavior between cmpxchg instructions and std::atomic<T>::compare_exchange_strong in C++?

Thanks.

@conrad-watt
Copy link
Collaborator

conrad-watt commented Nov 17, 2022

Ah, there are unfortunate mistakes in the draft spec text here. The overview correctly describes the intended behaviour, which is the same as the C++ compare_exchange_strong.

@q82419
Copy link
Author

q82419 commented Nov 17, 2022

Ah, there are unfortunate mistakes in the draft spec text here. The overview correctly describes the intended behaviour, which is the same as the C++ compare_exchange_strong.

Hi @conrad-watt ,
Thanks for the information!

Another question is that according to the overview:

The atomic compare exchange operators take three operands: an address, an expected value, and a replacement value. If the loaded value is equal to the expected value, the replacement value is stored to the same memory address. If the values are not equal, no value is stored. In either case, the loaded value is returned.

But take the test suite for example:

(invoke "init" (i64.const 0x1111111111111111))
(assert_return (invoke "i32.atomic.rmw8.cmpxchg_u" (i32.const 0) (i32.const 0x11111111) (i32.const 0xcdcdcdcd)) (i32.const 0x11))
(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111))

When invoking the i32.atomic.rmw8.cmpxchg_u in line 326, the address is 0, the expected value is 0x11, and the replacement value is 0xcd. Therefore the loaded value is 0x11.
The loaded value 0x11 is equal to the expected value 0x11, so the replacement value 0xcd is stored to the same memory address.
Then when invoking the i64.atomic.load in line 327, the returned value shoud be i64.const 0x11111111111111cd, not 0x1111111111111111?
The same questions are at line 334, 342, 350, and 358.

Thanks.

@conrad-watt
Copy link
Collaborator

conrad-watt commented Nov 17, 2022

Oh dear. You've identified an interesting discrepancy, and it looks like there is real implementation divergence here.

The culprit is the line in the overview which governs how value comparison works. It states that for i32.atomic.rmw8.cmpxchg_u, the "expected" value is wrapped to 8 bits before it is compared. However the reference interpreter and test suite are acting as though the value is not wrapped before comparison (i.e. compare line 325 to line 371).

Running the following test, it appears that Chrome and Firefox implement the overview semantics, while Safari implements the test suite semantics.

(module
  (memory 1 1)
  (func (export "testWrapping") (result i32)
    (i32.store (i32.const 0) (i32.const 0x11))
    (i32.atomic.rmw8.cmpxchg_u (i32.const 0) (i32.const 0x1111) (i32.const 0xcd))
    (drop)
    (i32.load (i32.const 0))))
    
;; chrome returns 205, firefox returns 205, safari returns 17

@conrad-watt
Copy link
Collaborator

conrad-watt commented Nov 17, 2022

Well... now we have to work out which is the preferable semantics. Thanks for discovering this @q82419 :)

@dtig who would be the right person to ask regarding V8's code generation? Is the current V8 semantics implemented by wrapping the expected value to the packed width as a separate platform asm instruction, or is there an asm instruction that directly implements sub-32-bit cmpxchg with the wrapping semantics for the expected value?

@conrad-watt
Copy link
Collaborator

conrad-watt commented Nov 17, 2022

@Constellation it looks like WebKit's behaviour is the odd one out here, although the fault lies with the reference interpreter for implementing this behaviour in the first place (as seen in the tests added here, which pass in the reference interpreter but should fail according to the overview). In principle, would it be possible to change the behaviour here to align with V8 and SpiderMonkey? (we would also change the reference interpreter and relevant tests).

@q82419
Copy link
Author

q82419 commented Nov 17, 2022

@conrad-watt
Thanks for your verification very much!

q82419 added a commit to second-state/WasmEdge-unittest that referenced this issue Nov 18, 2022
Please refer to this issue:
WebAssembly/threads#195

Signed-off-by: YiYing He <yiying@secondstate.io>
q82419 added a commit to second-state/WasmEdge-unittest that referenced this issue Dec 22, 2022
Please refer to this issue:
WebAssembly/threads#195

Signed-off-by: YiYing He <yiying@secondstate.io>
q82419 added a commit to second-state/WasmEdge-unittest that referenced this issue May 26, 2023
Please refer to this issue:
WebAssembly/threads#195

Signed-off-by: YiYing He <yiying@secondstate.io>
q82419 added a commit to second-state/WasmEdge-unittest that referenced this issue Jun 20, 2023
Please refer to this issue:
WebAssembly/threads#195

Signed-off-by: YiYing He <yiying@secondstate.io>
@conrad-watt
Copy link
Collaborator

To bump this: the spec, interpreter, and tests in upstream-rebuild, and the spec render at https://webassembly.github.io/threads/ (see #198), should now reflect the following:

  • cmpxchg performs the replacement if the expected value matches the read value (see here)
  • packed cmpxchg wraps before comparing, enshrining the overview/Chrome/Firefox semantics (see here)

Please let me know if any remaining artefacts fail to reflect the above. I'll flag these issues up again at the in-person meeting, since there really was an unfortunate divergence here.

justinmichaud pushed a commit to justinmichaud/WebKit that referenced this issue Oct 18, 2023
https://bugs.webkit.org/show_bug.cgi?id=263292
rdar://117102231

Reviewed by NOBODY (OOPS!).

In WebAssembly/threads#195, it was documented
that JSC implements cmpxchg_u without wrapping the expected value.

Previously, the spec tests and reference interpreter followed this interpretation,
but the spec text, SpiderMonkey and v8 did the wrapping.

The spec tests were updated, and this new behaviour is simpler anyway,
so let's just do it.

* JSTests/wasm/spec-tests/atomic.wast.js: Added.
* JSTests/wasm/spec-tests/binary-leb128.wast.js:
* JSTests/wasm/spec-tests/binary.wast.js:
* JSTests/wasm/spec-tests/br_table.wast.js:
* JSTests/wasm/spec-tests/elem.wast.js:
* JSTests/wasm/spec-tests/float_literals.wast.js:
* JSTests/wasm/threads-spec-tests/atomic-signed.wast.js:
* Source/JavaScriptCore/llint/WebAssembly64.asm:
* Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::emitAtomicCompareExchange):
* Source/JavaScriptCore/wasm/WasmBBQJIT.cpp:
(JSC::Wasm::BBQJIT::emitAtomicCompareExchange):
eugeneia pushed a commit to eugeneia/WebKit that referenced this issue Nov 2, 2023
https://bugs.webkit.org/show_bug.cgi?id=263292
rdar://117102231

Reviewed by NOBODY (OOPS!).

In WebAssembly/threads#195, it was documented
that JSC implements cmpxchg_u without wrapping the expected value.

Previously, the spec tests and reference interpreter followed this interpretation,
but the spec text, SpiderMonkey and v8 did the wrapping.

The spec tests were updated, and this new behaviour is simpler anyway,
so let's just do it.

* JSTests/wasm/spec-tests/atomic.wast.js: Added.
* JSTests/wasm/spec-tests/binary-leb128.wast.js:
* JSTests/wasm/spec-tests/binary.wast.js:
* JSTests/wasm/spec-tests/br_table.wast.js:
* JSTests/wasm/spec-tests/elem.wast.js:
* JSTests/wasm/spec-tests/float_literals.wast.js:
* JSTests/wasm/threads-spec-tests/atomic-signed.wast.js:
* Source/JavaScriptCore/llint/WebAssembly32_64.asm:
* Source/JavaScriptCore/llint/WebAssembly64.asm:
* Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::emitAtomicCompareExchange):
* Source/JavaScriptCore/wasm/WasmBBQJIT.cpp:
(JSC::Wasm::BBQJIT::emitAtomicCompareExchange):
webkit-commit-queue pushed a commit to eugeneia/WebKit that referenced this issue Nov 16, 2023
https://bugs.webkit.org/show_bug.cgi?id=263292
rdar://117102231

Reviewed by Justin Michaud and Yusuke Suzuki.

In WebAssembly/threads#195, it was documented
that JSC implements cmpxchg_u without wrapping the expected value.

Previously, the spec tests and reference interpreter followed this interpretation,
but the spec text, SpiderMonkey and v8 did the wrapping.

The spec tests were updated, and this new behaviour is simpler anyway,
so let's just do it.

* JSTests/wasm/spec-tests/atomic.wast.js: Added.
* JSTests/wasm/spec-tests/binary-leb128.wast.js:
* JSTests/wasm/spec-tests/binary.wast.js:
* JSTests/wasm/spec-tests/br_table.wast.js:
* JSTests/wasm/spec-tests/elem.wast.js:
* JSTests/wasm/spec-tests/float_literals.wast.js:
* JSTests/wasm/threads-spec-tests/atomic-signed.wast.js:
* Source/JavaScriptCore/llint/WebAssembly32_64.asm:
* Source/JavaScriptCore/llint/WebAssembly64.asm:
* Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::emitAtomicCompareExchange):
* Source/JavaScriptCore/wasm/WasmBBQJIT.cpp:
(JSC::Wasm::BBQJIT::emitAtomicCompareExchange):

Canonical link: https://commits.webkit.org/270828@main
q82419 added a commit to second-state/WasmEdge-unittest that referenced this issue Dec 5, 2023
Please refer to this issue:
WebAssembly/threads#195

Signed-off-by: YiYing He <yiying@secondstate.io>
q82419 added a commit to second-state/WasmEdge-unittest that referenced this issue Dec 5, 2023
Please refer to this issue:
WebAssembly/threads#195

Signed-off-by: YiYing He <yiying@secondstate.io>
q82419 added a commit to second-state/WasmEdge-unittest that referenced this issue Dec 5, 2023
Please refer to this issue:
WebAssembly/threads#195

Signed-off-by: YiYing He <yiying@secondstate.io>
q82419 added a commit to second-state/WasmEdge-unittest that referenced this issue Jan 4, 2024
Please refer to this issue:
WebAssembly/threads#195

Signed-off-by: YiYing He <yiying@secondstate.io>
q82419 added a commit to second-state/WasmEdge-unittest that referenced this issue Mar 25, 2024
Please refer to this issue:
WebAssembly/threads#195

Signed-off-by: YiYing He <yiying@secondstate.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants