7 ms·
> that prevents the optimizer from performing constant propagation I understand how it works, and why you would even maybe want this, but this just straight-up
by dvt 3y ago
> that prevents the optimizer from performing constant propagation
I understand how it works, and why you would even maybe want this, but this just straight-up seems like a mistake. It breaks const-invariance, which I guess is no longer invariant. Is just any proposal making into C++ these days?
> He tells us that this function might be handy when dealing with containers of const-qualified elements.
Then just.. don't.. const-qualify your elements? What a neat footgun, not even consts are safe in C++ now.
- amluto 3y agoC++’s const was never especially safe — contemplate what mutable does. And that one can cast pointers-to-nonconst to pointer-to-const.
- kimixa 3y agoIt's always been a "programmer aid annotation" rather than anything that really affects code generation. The only possible difference (from the resulting binary POV) I can think of is "static const" variables might be allocated in a read-only executable section?
- throwway120385 3y agoProbably not, given the possibility that they can be const_casted back into mutable values. Locating static const values in read-only pages would result in crashes from valid statements in the language. In other words if you put a static const in a read only page, it would probably be considered a bug in your linker.
- actionfromafar 3y agoThat doesn't rhyme with my understanding of C++ (which is limited, of course). This sounds like undefined.
- secondcoming 3y agoAren't they put into the .rodata section of the binary? Crashing is exactly what happens, thankfully https://godbolt.org/z/vKj9dcPMc https://godbolt.org/z/vKj9dcPMc
- gary_0 3y agoGenerally yes, if you have a bunch of const data in your C++ code, it will go in .rodata. Depending on the compiler/flags/optimizations, of course. https://stackoverflow.com/a/44938843 https://stackoverflow.com/a/44938843
- PhilipRoman 3y agoIt doesn't even have to be static. Const is powerful when used on non-pointer types. You can write something like this and the compiler will (on appropriate optimization levels) hard-code return value to 7777 regardless of what "mutate" does: extern void mutate(int *p); int func() { const int foo = 7777; mutate(&foo); return foo; }
- gary_0 3y agoconst_cast is only for removing const from references or pointers that actually refer to a non-const object. You can't declare a const variable and then modify it with const_cast. See "Notes" at: https://en.cppreference.com/w/cpp/language/const_cast https://en.cppreference.com/w/cpp/language/const_cast The compiler can indeed put const objects in the executable's .rodata section, which means trying to modify it at runtime will probably cause a segfault (but it's all Undefined Behavior so who knows).
- bingo3131 3y agoIt is undefined behaviour to modify objects declared as const (except if those objects have mutable members, in which case the mutable members can be modified). const int a = 1; If you try to const_cast away the const of x to change its value, you have undefined behaviour. As for pointers, if you make a const pointer to a non-const object then all it means is that you cannot modify the object via that pointer, not that the object will never be modified. int a = 1; int b = &a; const int c = &a; a = 2; Both b and c are now 2 and this is absolutely fine, and the compiler won't try to optimise out any reads of *c and assume its value is still 1 as const/non-const pointer aliasing is allowed (C has the restrict keyword, it's not in C++).
- sqeaky 3y agoThey never were safe in C++. And no serious developer was expecting them to be perfectly safe, they are a tool for normal well-formed software to be a little less errer-prone. Everybody taking a class in C++ learns about const_cast and anyone clever eventually figures out how to use reinterpreter_cast for the similar things. Launder is a replacement for calling compiler intrinsics during certain special operations, not everyday use. For one example: If I wrote a simple benchmarking library and shipped its source code then an optimizer could easily mingle my benchmarking library into another developer's benchmarked code. Launder provides at least some rudimentary options for minimizing this so artifacts of the implementation of my benchmark impact the code being measured less and in more predictable ways. Other places this might be useful that I can think of off the top of my head might include: code that needs predictable binaries to avoid spectre and similar issues, working with precompiled binaries with headers but without source, code that is trying to leverage specific features of the underlying hardware possibly for maximum performance. I am sure there are more but disabling optimizations is super useful sometimes.
- secondcoming 3y agoI think you're thinking of barriers?
- kazinator 3y agoI don't understand why plain old volatile wouldn't be enough to prevent constant propagation.
- cryptonector 3y agoI would expect volatile and const to be mutually exclusive.
- Someone 3y agoFor C++, you would be wrong. You can use it for variables that your code won’t change (hence const), but that can change due to outside events and hence can’t be cached in a register (hence volatile) See https://www.embedded.com/combining-cs-volatile-and-const-keywords/ https://www.embedded.com/combining-cs-volatile-and-const-key... for examples, https://en.cppreference.com/w/cpp/language/cv https://en.cppreference.com/w/cpp/language/cv for a more formal description.
- cryptonector 3y agoYes, I'm aware, and thus I said "would". But I wouldn't make use of this without first reading the spec on that very carefully. Thankfully I've not needed this.
- Rucadi 3y agoIn a Memory Mapped device, a read can trigger side effects (in fact, I worked a lot with that), so is not only is about memory.
- jcranmer 3y agoC++ is a weird language that essentially combines both a high-level type system whose details of representations and whatnot can be freely modified by a sufficiently smart compiler with a low-level bytemucking type system, and the interface between the two systems is gnarly and confusing and std::launder is lying in a pit on this border. The point of std::launder is to let you use an object that would otherwise be undefined behavior to use. The original motivating example for std::launder essentially boils down to "you can't properly make std::vector<T> if T has a const member variable without std::launder," although C++ later did change the object model (after this feature had been accepted!) so that std::vector works without std::launder. The residual use of std::launder has to do with vtables (which are important properties of an object yet not properly part of the object model because it's a high-level thing, not a low-level thing) after placement new, which is an extremely niche thing that truly almost nobody needs to care about and probably doesn't deserve a place in the standard library. libc++ overloads it to use it as a way to indicate intentional strict aliasing violations, but this is still UB per the standard.
- amluto 3y agoI'm wondering if any uses at all would be left if programs were using strict provenance, a la: https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-... If I have a pointer to an object that no longer exists because I've futzed with a union or placement new or otherwise put a different object at that address, but I know that my pointer has the correct bit pattern (i.e. address on most systems) for the new object, then a strict provenance model says that I have no business dereferencing the pointer. But an API like with_addr() gives me a very explicit way to tell the compiler (and the CPU or runtime if provenance is being enforced!) what I'm doing, and the cases that could plausibly generate valid code will be well defined and work correctly.
- jcranmer 3y agoThe issue that's trying to be solved with std::launder is essentially around lifetime issues, which strict provenance doesn't really solve. Strict pointer provenance is about fixing the semantics of int-to-pointer, which requires tweaks to pointer-to-int. std::launder, by contrast, is trying to fix cases where memory is being validly reused after the language thinks the object is dead--it's the kind of thing you might need to use to implement an allocator, for example, although you already have screwy effective type rules breaking you that std::launder doesn't fix.