4 ms·
I figured any half decent compiler already do plenty of flow and liveness analysis on everything for register allocation, dead code elimination and what not. M
by sprocketz 1mo ago
I figured any half decent compiler already do plenty of flow and liveness analysis on everything for register allocation, dead code elimination and what not.
Maybe it's the guaranteed elision that makes it a problem, like you can't fail the analysis, but then maybe you go the rust route - fail to compile and urge the programmer to rewrite their code so it accepts it.
Make it opt in with [[must_elide]] so old code still works I guess.
- jcranmer 1mo agoDespite its name, NRVO isn't an optimization performed by the optimizer, it's something done by the frontend of the compiler before it generates the code for the optimizer to run on. The frontend is extremely reluctant to do anything like flow analysis, in large part because the frontend doesn't even really have any code to do the analysis on, just the AST. More people (including far too many on the committee itself) need to understand the separation between the different parts of the compiler, and what each part can and cannot do effectively.
- whizzter 1mo agoThey probably _were_, since lower level code representations often has little notion of complex types and their semantics they could be kept clean and focused on machine code, however type inference in a language like C++ complicates such matters immensly since an assignment can be both a register move and a function call. Template resolution solved that in the past, but C++ today allows auto in so many places that I'm uncertain that it can be done without some flow based support (if constexpr comes to mind). I mentioned ClangIR(MLIR) in the sibling comment here, feels like it was built for stuff like this.
- jcranmer 1mo ago> Template resolution solved that in the past, but C++ today allows auto in so many places that I'm uncertain that it can be done without some flow based support (if constexpr comes to mind). C++ requires determining the type of every expression immediately. Even auto doesn't change that: it determines the type of the variable based on the initializer, literally following the same rules as template argument deduction (there's a little bit of patching to tweak the exact expression being used for deduction, but https://eel.is/c++draft/dcl.type.auto.deduct#3 https://eel.is/c++draft/dcl.type.auto.deduct#3 is the core rule here). ClangIR doesn't necessarily help here, because while it does give a more abstract C abstract machine IR semantics, it's still downstream of things like NRVO decision points--it's still fundamentally past the codegen-the-AST barrier.
- locknitpicker 29d ago> Template resolution solved that in the past, but C++ today allows auto in so many places that I'm uncertain that it can be done without some flow based support (if constexpr comes to mind). This concern is unfounded. The auto keyword in C++ acts as mere syntactic sugar. It works only when the compiler is able to tell exactly what's the type by evaluating the expression. The auto keyword is also considered a code smell for the same reason: just because the compiler can tell exactly what the type is expected to be, that does not mean the developer can. Therefore it makes the code harder to reason about.
- account42 28d ago> The auto keyword is also considered a code smell for the same reason: just because the compiler can tell exactly what the type is expected to be, that does not mean the developer can. Therefore it makes the code harder to reason about. This really depends though. In many cases even the programmer can tell the type of auto because its on the very same line and not using auto would mean needlessly repeating it. In other cases (e.g. iterators) the programmer also doesn't need to care about the concrete type.
- ahartmetz 28d agoGP was possibly arguing against Herb Sutter's "almost always auto". C++ wasn't designed for auto and it shows. You can't rely on it always doing the right thing or at least a safe thing in C++, unlike in Rust - I've seen bugs due to auto. It is also often helpful to spell out the concrete type in important places such as (most/many) variable definitions. I'm also fine with auto if it just repeats information, especially so if the concrete type takes half of your line length budget (yes it's an iterator over that container containing...).
- bluGill 28d agoYeah, when auto first came out a few people started abusing it everywhere making for unreadable code. Most developers settled quickly on much better rules for using auto that do not destroy readability, and never abused it in the first place. Auto is intended for and works very well to avoid typing very long types where you often never care. Iterators is the common example: the type name is always long, and nearly always used in a context where it is obvious. Even if you do care about the type, looking up the iterator is wrong - you always first look up the base type (ie std:vector) first to understand the iterator, only rarely do you need to dig into the iterator once you understand the base type. Generic code (template and non-template) are the other - they type could be anything, so trying to specify more detail isn't going to gain you anything.
- whizzter 1mo agoRegister allocation is usually on a far "lower" codegen level as is often DCE, they should be possible to compute/run on a SSA node level or similar long after destruction sequences are applied. Now, there is far more "language level" flow analysis today apart from this as required by allowing auto type inference in more places (and things relaxed in relation to that). Reading up it seems to be suitably done in Clang on the ClangIR(MLIR extension) level, something that sits between AST and the LLVM IR. Regardless of how it's implemented, I'm pretty sure that NRVO carried a fair bit more complexity requirements compared to RVO depending on how prepared the corebases for different compilers were to handle it.