[bigendian] Fix crash during PThread initialization - #23700
Conversation
2dae9cd to
13d97b3
Compare
| var h16 = new Int16Array(1); | ||
| var h8 = new Int8Array(h16.buffer); | ||
| h16[0] = 42; | ||
| return h8[0] === 42 && h8[1] === 0; // little endian ordering |
There was a problem hiding this comment.
Surely we must already have helpers that perform this kind of thing in emscripten? Since all the non-atomic read/writes already work?
There was a problem hiding this comment.
Other accesses use the DataView methods.
There was a problem hiding this comment.
Similar test is performed in the runtime_debug.js, but it is only included with ASSERTIONS option.
|
The general mechanism we use for this is emscripten/tools/acorn-optimizer.mjs Lines 1103 to 1105 in bb7fd25 Perhaps the best thing is to update that pass so it handles Atomic operations? |
|
I agree, yes, that is necessary. I am just saying that doing it in the processing pass has advantages. Specifically it will find all atomic operations, guaranteeing that we don't forget any (as you wrote above, "There are other places using Atomic.something that likely have the same issue").
Oh, definitely, yes, we want to use wrapper functions. The processing pass should just add calls to those wrappers. That is what it does today: it replaces e.g.
And
I am suggesting that Atomics be implemented in a similar way. |
76f6d46 to
6d93671
Compare
| HEAPU32.unsigned = (x => x >>> 0); | ||
| #if WASM_BIGINT | ||
| HEAPU64.unsigned = (x => x >= 0 ? x : BigInt(2**64) + x); | ||
| #endif |
There was a problem hiding this comment.
Ideally we could avoid adding this here and keep all the code chagnes to liblittle_endian_heap.js.
Ideally we could also avoid adding extra methods to these heaps, but maybe thats not easy.
There was a problem hiding this comment.
I didn't really find a better way to keep the extra methods out while not introducing per-type functions (and associated transformations).
There was a problem hiding this comment.
Can you just use the uppercase version here? i.e. why do we need the new lower case versions?
There was a problem hiding this comment.
I wonder if wen also move these lines to liblittle_endian_heap.js?
Perhaps they could be part of the __postset too?
There was a problem hiding this comment.
re the new variables - if the uppercased variables are used they get replaced by e.g. the growable heap wrappers.
There was a problem hiding this comment.
One option would be to rename updateMemoryViews to _updateMemoryViews. Assign _updateMemoryViews to updateMemoryViews and use to __postset to override the new updateMemoryViews variable.
If you prefer this option, I can look into that on Monday.
There was a problem hiding this comment.
I tried this approach and it does not look any cleaner. The maybeExportHeap needs to be accessed via globalThis and there is still no good way to access to the HEAPxx variables without the acorn optimizer interfering with the code.
Additionally, the effective updateMemoryViews changes during initialization.
There was a problem hiding this comment.
Regarding the HEAPXX variable substitution, we do have a list of function for which this is not performed. I don't know if that will be useful in this case but the list is here:
emscripten/tools/acorn-optimizer.mjs
Lines 1484 to 1491 in cb8d502
There was a problem hiding this comment.
I moved the heap update functions, I am unable to move the part that depends on maybeExportHeap.
I cannot handle the whole situation in the postset, as the updateMemoryViews is called before postset code runs.
| '$LE_ATOMICS_SUB', | ||
| '$LE_ATOMICS_WAIT', | ||
| '$LE_ATOMICS_WAITASYNC', | ||
| '$LE_ATOMICS_XOR', |
There was a problem hiding this comment.
While I agree it would be good to add all of these, I also thing we might want to only add the ones that we actually use (and also test).
There was a problem hiding this comment.
I thought that the whole point of this approach was to actually implement comprehensive support like was done to the non-atomic memory accesses done from JS code.
There was a problem hiding this comment.
Yes, I suppose you right, seems reasonable to leave them all in then.
But lets skip 64-bit atomics and just fail in that case, to keep things simple. I don't think anyone should be doing that.
There was a problem hiding this comment.
I've tested it on the following program:
#include <atomic>
#include <print>
#include <emscripten.h>
int main(){
std::atomic<unsigned long long> x = 0x0123456789ABCDEF;
std::println("x: {:X}", x.load());
EM_ASM({ Atomics.compareExchange(HEAP64, ($0)/8, BigInt("0x0123456789ABCDEF"), BigInt("0xFEDCBA9876543210")); }, &x);
std::println("x: {:X}", x.load());
}built with
em++ -o main.js -s USE_PTHREADS=1 -s PROXY_TO_PTHREAD=1 -s PTHREAD_POOL_SIZE=8 -s SUPPORT_BIG_ENDIAN=1 -pthread -s EXIT_RUNTIME=1 -s ALLOW_MEMORY_GROWTH=1 -std=c++23 main.cpp
And I do get the expected result on both LE and BE:
x: 123456789ABCDEF
x: FEDCBA9876543210
| else | ||
| return res - BigInt(2**64); | ||
| } | ||
| ), |
There was a problem hiding this comment.
Do we need to support more than 4 byte access? Maybe lets just skip then completely? If anyone tries to do an atomic 64-bit access they would see a crash accessing nativeByteOrder OOB.
There was a problem hiding this comment.
See the post above... I think that the 8B atomics will be needed eventually for the MEMORY64.
There was a problem hiding this comment.
I dont think we want to be adding complexity to support APIs that we don't use in emscripten.
There was a problem hiding this comment.
That was just an example, perhaps more realistic would be just accessing 8B atomic or perhaps 2x4B pointer+counter pair value.
There was a problem hiding this comment.
Sure, but until we get a request that somebody wants to to actually do that from JS then I don't think we should support it. Better to simply error out for now maybe?
There was a problem hiding this comment.
Perhaps I can explain my motivation a little better: Emscripten has a lot of different settings and configurations in which it can be run. One of our biggest issues is controlling the combinatorial complexity of all these settings. We are always looks for way to avoid new options, or limit their scope. I am also always looking for settings can can be removed completely or downscoped.
This often means I will, by default, argue against new settings of the expansion of existing ones. Of course I often agree that certain settings are useful and will agree, especially if we can understand or limit their scope.
The BIG_ENDIAN support we have in emscripten has, IIUC, very few users, and almost zero testing. If posssible, I think it would be great to delete it one day, but I'm also fine with fixing bugs in it, like this one, as along is it continues to exist.
Ideally we could add some real testing on a big endian machine in order to prevent/detect regression, but that doesn't exist today.
Going back to this PR in particular, I think its great to get this bug fixed. However, I would like to limit to scope of the fix to features that emscripten actually uses. Because we don't have any usage of 64-bit atomics in JS I think we it would be reasonable to simple crash/assert if 64-bit JS atomics are used. Otherwise we are shipping code that we neither use or test.
There was a problem hiding this comment.
I removed the 8B support, even though I think it is a mistake.
There was a problem hiding this comment.
As for testing in CI, there is an option on e.g. Ubuntu:
apt-get install -y qemu-user libc6-s390x-cross libstdc++6-s390x-cross
wget https://nodejs.org/dist/v22.14.0/node-v22.14.0-linux-s390x.tar.xz
tar xJf node-v22.14.0-linux-s390x.tar.xz
qemu-s390x -L /usr/s390x-linux-gnu/ node-v22.14.0-linux-s390x/bin/node program.js
There was a problem hiding this comment.
Sorry I just saw this comment. This looks like a good idea. Should we do this as part of this PR, or open a bug to do this as a followup?
There was a problem hiding this comment.
I suggest a follow up. It is not strictly connected to this PR.
3a3138f to
319b9e5
Compare
319b9e5 to
331431c
Compare
sbc100
left a comment
There was a problem hiding this comment.
Nice, this looks much cleaner now.
Do you think we can find some way to test this in CI? Or do we just have to rely on folks like you to manually test it once in while? Can we perhaps run a few tests in quemu or some such?
|
It could be tested in CI via QEMU |
|
About testing, it does seem like QEMU or such is the only option. I would definitely not block on that, but it would be nice to have eventually, though only if it isn't very slow on CI. |
True but we should just run a handful of tests in this most (maybe just one even). |
Notes:
nativeByteOrder32function in thelibpthread.jsand use the dependency mechanism, but it was always optimized intox => x.Fixes: #23689