Even C++ didn’t fully repent from this sin until around C++17. I appreciate the non-begrudging acceptance of this reality in Zig.
Even C++ didn’t fully repent from this sin until around C++17. I appreciate the non-begrudging acceptance of this reality in Zig.
For example, apparently the plan9 OS gets special page_allocator handling: https://ziglang.org/documentation/master/std/#std.heap.page_...
It generates no code, it is a compiler barrier related to constant folding and lifetime analysis that is particularly useful when operating on objects in DMA memory. As far as a compiler is concerned DMA doesn’t exist, it is a Deus Ex Machina. This is an annotation to the compiler that everything it thinks it understands about the contents and lifetime of a bit of memory is now voided and it has to start over. This case is endemic in high-end database engines.
It should be noted that `std::launder` only works for different instances of the same type. If you want to dynamically re-type memory there is a different set of APIs for informing the compiler that DMA dropped a completely different type in the same memory address.
All of this is compiled down to nothing. It annotates for the compiler things it can’t understand just by inspecting the code.
But what's the Zig equivalent?
You might also find some of the builtin functions interesting as well[1], they have a lot of really useful functions that in other languages are only accessible via the blessed stdlib, such as @addrSpaceCast, @atomicLoad, @branchHint, @fieldParentPtr, @frameAddress, @prefetch, @returnAddress(), and more.
[1] https://ziglang.org/documentation/master/#Builtin-Functions
For example, in this test case:
https://gcc.godbolt.org/z/j3Ko7rf7z
GCC generates a store followed by a load from the same location, because of the asm block (compiler barrier) in between. But if you change `if (1)` to `if (0)`, making it use `std::launder` instead of an asm block, GCC doesn't generate a load. GCC still assumes that the value read back from the pointer must be 42, despite the use of `std::launder`.
I think the subtle semantic distinction is that `volatile` is a current property of the type whereas `std::launder` only indicates that it was a former property not visible in the current scope. Within the scope of that trivial function in which the pointer is not volatile, the behavior of `std::launder` is what I'd expect. The practical effect is to limit value propagation of types marked `const` in that memory. Or at least this is my understanding.
DMA memory (and a type residing therein) is often only operationally volatile within narrow, controlled windows of time. The rest of the time you really don't want that volatile qualifier to follow those types around the code.