13 ms·
Maybe I'm missing something, but I don't see what's special about Carbon here. In C++ the compiler can also optimize pass-by-const-reference to pass-by-value, a
by Denvercoder9 4y ago
Maybe I'm missing something, but I don't see what's special about Carbon here. In C++ the compiler can also optimize pass-by-const-reference to pass-by-value, and they do. It just can't do it across an ABI boundary, but that should only be an issue with dynamic libraries, and Carbon has to follow the standard ABI there as well. Just make sure the compiler knows it doesn't have to follow the standard ABI for every symbol in your program, e.g. by enabling LTO and setting -fvisibility=hidden. See what happens `add` is made static: https://godbolt.org/z/KsqxP5Pxh https://godbolt.org/z/KsqxP5Pxh (I also had to change foo() to avoid the compiler hardcoding the result of the single call in `add`).
- moralestapia 4y ago>Maybe I'm missing something, but I don't see what's special about Carbon here. It's nothing really out of the ordinary ... also that particular optimization is meager in practice (on the order of 1%?). I know everything improvement adds up, but the author talks about it as if it was a game changer. I like the enthusiasm, though :D.
- kllrnohj 4y agoThe "ABI now or never" paper suggests somewhere on the order of 5-10%, and that's without considering the escape pessimization. Especially with the rapid expansion of smart pointer usage (and for good reason!). Given the major selling point of C/C++ is performance, that's not an insignificant amount.
- olliej 4y agoI think the alias analysis “pro” is overblown, it’s UB to take the address of a parameter via any mechanism other than explicitly taking the address - which a compiler obviously sees, and because it’s UB the compiler optimizes is free to assume no one is taking the address. Then for any parameters that are passed by reference the compiler has to assume there are other references so there’s no gain. Honestly the only thing that really stood out as nice is the default pass by word-sized value, in the context of templates - it’s a thing that is achievable in C++, but requires a bunch of obnoxious additional templates that aren’t even part of the standard library so everyone ends up reimplementing the same cruft. Happily I believe there’s a proposal to add this exact functionality. I also loathe their desire to listen to the BNF maximalists insistence on not having any “ambiguity” from <>s. I’m sorry it’s clearly parseable, and <>s are the standard token for decades. Switching to []s doesn’t make it less conceptually ambiguous, if anything it makes it more ambiguous to a human reader. The only people who don’t want <>s are PLT academics obsessed with forcing their dragon book idea of what a grammar should be. You can’t argue you’re doing it because the ambiguity in a grammar or lexer is bad, because then you would also drop infix operators. Then in carbon the more reasonable adoption of pascal’s : notation for typing a variable or parameter removes the most common case of the supposedly terrible ambiguity anyway.
- XiKLeR 4y agoWhy not just use parentheses, because, you know, templates are just AOT-functions behind the scenes! Try to come up with an example where this syntax is worse than (angle or square) brackets...
- temac 4y ago> it’s UB to take the address of a parameter via any mechanism other than explicitly taking the address - which a compiler obviously sees, and because it’s UB the compiler optimizes is free to assume no one is taking the address. I don't get that: can you express in C++ a code that "take the address of a parameter via any mechanism other than explicitly taking the address"?
- josephcsible 4y agoConsider this C code (also "works" if compiled as C++): int main(void) { int x = 0; int arr[1]; int *p = arr + 1; *p = 42; return x; } On a lot of systems (e.g., https://godbolt.org/z/jYqM8TT3Y https://godbolt.org/z/jYqM8TT3Y), it just so happens that `x` is right above `arr` on the stack, so that code will return 42. But that code is absolutely UB. The more general name for this concept is "pointer provenance". Basically, you can't pull pointer values out of thin air; you have to derive them from operations rooted at taking the address of something within the same allocation.
- hedora 4y agoThat's a buffer overflow. The optimizer doesn't need to reason about changing the behavior of such things.
- josephcsible 4y agoThe point is that on systems where that code returns 42, `p` has the exact same value it would if I did `int *p = &x;` instead, but not the same provenance.
- comex 4y agoThat optimization is quite fragile. For example, try putting `puts("hello");` at the beginning of `add`. Now neither GCC nor Clang performs the optimization. Why? Because `puts` could theoretically modify the value behind the reference, so the value loaded is not necessarily the same as the value at the beginning of the function, which makes things more complicated, so both compilers give up. As another example, GCC and Clang both perform the optimization within a translation unit, but if the function definition and the call are in different translation units, GCC doesn't perform the optimization even with LTO, and Clang doesn't perform it with ThinLTO (but does perform it with full LTO). Meanwhile, many projects don't compile with any form of LTO, which is a reasonable decision to improve compilation speed and predictability. Neither compiler is smart enough to perform the optimization for virtual calls in almost any situation.
- ad8e 4y agoCarbon doesn't do the next logical optimization, and only converts a const& parameter to a value. If a function has parameter "const struct_with_two_floats&", and reads it, calls puts(), then reads it again, Carbon will not be willing to optimize out the "redundant" read. It seems odd to break with C++ semantics for small parameters only; I'd expect it to either break for all parameters or no parameters. EDIT: looks like Carbon is not doing this optimization for small values either. Its docs on parameters say, "This binding will be implemented using a pointer, unless it is legal to copy and copying is cheaper". And the OP link says, "the compiler is allowed to convert that to a T [copy] under the as-if rule." That seems like no extra optimization is enabled other than changing the function calling convention. And the "as-if" rule would require looking at the function body to check if the value could be changed (by things like puts()). I hope my understanding is wrong because this does not make sense. The word "legal" is ambiguous, so hopefully they mean something else!
- phire 4y agoI have been burned many times in the past by "the compiler is allowed to optimise something away". You write your code assuming such an optimisation will happen, and for some reason, the compiler decides not to apply the optimisation. Perhaps the wind was blowing in the wrong direction, or it was in a bad mood, or you forgot to specify -fvisiblitly=hidden. The exciting part here is that it happens by default, and the compiler is required to do it. I don't have to think about it, and the ABI automatically does the thing best for performance.
- jcelerier 4y agoI don't think that fvisibility=hidden on its own is sufficient, it does not allow the compiler to break the call abi as the function could still be called from another .o (which will only know the mangled name of the original function). You need fvisibility=internal (or maybe fno-semantic-interposition but I'm not sure if it's enough).
- variadix 4y agoI think you just need LTO if you want full optimization without regards for the ABI constraints