Makes it a linker error if the "atomics" feature is used but the user
does not opt in to shared memory or if "atomics" is disallowed but the
user does opt in to shared memory. Also check that an appropriate max
memory size is supplied if shared memory is used.
Details
Diff Detail
- Repository
- rL LLVM
Event Timeline
In the future, atomics will likely be enabled by default, however it will continue to be desirable to be able to select non-shared memory, since non-shared memory doesn't require a maximum size, and since it can make interoperating with JS and other external code simpler. Is it possible to structure this patch in a way that supports this?
When building for non-shared memory, shouldn't input code check for "ifdef _REENTRANT" and not generate any atomic instructions?
The wasm spec says that atomics are simply not allowed when non-shared memory is used. Do we need to hide this fact from the user code?
That, or they could use -mthread-model single and lower atomics into non-atomics.
It sounds like the answer is, users that want non-shared memory should use -mno-atomics. I guess that works, it's just surprising if we think of -matomics as a pure target feature flag.
Yes, that is a little surprising but it also makes the flags simpler. If -matomics was purely for the target and we also used the thread model flags, -matomics would become entirely redundant; there would be no reason to use -matomics without -mthread-model posix and vice versa. So as a simplification, we just have -matomics carry both meanings. The corollary of that is that we will never include -matomics by default in any CPU configuration. Thankfully, this is all entirely transparent to the user, who still uses -pthread if they want threads and don't use -pthread if they don't want threads and everything just works.
Here's a deeper explanation of why treating -matomics this way is fine as far as link-time feature validation is concerned. Consider object files in the following situations for a target that supports atomics:
A) application is single-threaded and atomic operations are lowered to non-atomic operations.
B) application is single-threaded but atomic operations are preserved
C) application is multithreaded so atomic operations are preserved
Here are our goals:
- A should link with B. This is safe because the applications is single-threaded and the target supports atomics.
- A should not link with C. It is not safe to link a module with atomics stripped into a multithreaded application.
- B and C are the same object. Otherwise users would have to build their libraries twice when the only difference is the threadedness of the final application.
These goals form a contradiction, so we can only choose two of them. 2 enforces an important safety property and dropping 3 would be a larger ergonomic loss than dropping 1, so we drop goal 1. The three possible situations above are then represented by two types of object files:
- An object where atomic operations are stripped (A)
- An object where atomic operations are preserved (B and C)
These two types of objects cannot in general be safely linked together, so the first is no different from an object that does not support atomics at all. QED.
What that argument does not capture is the case where atomic operations are neither stripped or preserved because there were none in the source to begin with. In this case it would be ideal to produce an object that can be linked into both mutithreaded and single-threaded applications. This is totally possible as far as the backend and linker are concerned*, but it is an open UI problem. The only way I see to achieve this is to detect the use of atomic operations in the backend and magically change the atomics feature linkage policy depending on their presence. And that seems perhaps too magical. WDYT?
(*) This would be achieved by making objects containing atomic ops use "+atomics" in their target feature section, making objects with any stripped atomics use "-atomics", and making objects with no atomics to begin with simply not mention "atomics".
D) Application uses (non-existent in WebAssembly) signal handling support and atomics just enforce program order.
E) Application is single-threaded but does cooperative interleaving of functions (but doesn't call it threads).
Thanks, JF! I definitely want to consider all cases. The cases you mention use atomics independently from threads, so I think they are further evidence that when the user uses -matomics we should never be stripping away their atomic operations and that it is dangerous to allow linking together objects with atomics both stripped and not stripped.
lld/wasm/Writer.cpp | ||
---|---|---|
915 ↗ | (On Diff #190366) | I think we decided against this part right? i.e. atomics should require but not imply shared memory? So this should probably be an error instead. |
lld/wasm/Writer.cpp | ||
---|---|---|
915 ↗ | (On Diff #190366) | Yes, sorry. I updated the name and description but I haven't updated the code yet. This CL is going to be blocked on an upcoming CL that detects whether any atomics were stripped. Landing that first will avoid unnecessary turn in some tests. |
- Make using --shared-memory when atomics are disallowed or omitting it when atomics are used an error.