9 ms·
GCC's inefficient code generation for C++ initializers
All examples are compiled with -O3 for x86-64.
https://godbolt.org/z/PYxz55
(GCC 9, 10, 11)
xorl %eax, %eax
movl $0, -24(%rsp)
movw %ax, -20(%rsp)
movabsq $72057594037927935, %rax
movb $0, -18(%rsp)
andq -24(%rsp), %rax
salq $8, %rax
movb %dil, %al
ret
https://godbolt.org/z/8njqn1 (GCC 9, 10)
xorl %edx, %edx
movl $1, %eax
movb $2, %dl
movq %rax, -24(%rsp)
xorl %eax, %eax
movq %rdx, -16(%rsp)
movdqa -24(%rsp), %xmm0
pinsrd $3, %eax, %xmm0
movaps %xmm0, -24(%rsp)
movq -24(%rsp), %rax
movq -16(%rsp), %rdx
ret
https://godbolt.org/z/sronaG (GCC 7, 8, 9, 10, 11)
movq %rdi, -24(%rsp)
movq -24(%rsp), %rax
movq %rsi, -16(%rsp)
movq -16(%rsp), %rdx
For each example, two movs and a ret (and no stack access) would suffice.
- fartcannon 6y agoOk, so submit the patch for review? That's the whole idea.
- luc_ 6y agoThe fart cannon hath spoken.
- Areading314 6y agoThis appears to be fixed if you `return {c, 0}` to fully initialize the struct.
- mcolavita 6y agoThe first example is, but the second and third are not.
- xyproto 6y agoHaving to keep tricks and workarounds in mind at all times is a big part of why C++ is disliked by many programmers.
- gumby 6y agoThis is not an issue with the C++ language. It is clearly a compiler bug (well, misoptimization). There are plenty of real reasons for liking or disliking C++ but your response reads like a knee-jerk response.
- xyproto 6y agoVtables, are they always predictably optimized? Whole program linking? Consts and constexpr? Yes, it's possible to know exactly how everything is compiled down to assembly with enough experience, but all abstractions carry a requirements of knowledge about how well they are optimized.
- gumby 6y agoThose are not language specification issues, apart from const and constexpr. If you are doing systems programming you may quite reasonably care about fine details and at least C++ is one of the languages that gives you the tools to do so if it matters to your use case. But the high (abstraction) level of C++ development, in particular modern C++ which is largely pointer-free, make is pretty easy to rapidly write code with a minimum of foot gunning. I'm more concerned with the unpredictable performance of java or python. Others write excellent code in those languages without needing to worry about that, which is also great.
- kazinator 6y agoThe bug is alarming. The code is not even peephole optimized up to 1985-ish standards Regarding { c, 0 } versus { c }, I don't expect in a mature C or C++ compiler for such high level differences in an expression to affect instruction selection. Both { c } and { c, 0 } say exactly the same thing: one member is initialized and the rest are zero. I would expect that there is no difference in intermediate representations at all, long before we get to a code generating back end. The compiler should convert the initializer to a canonical representation early on and not deal with different cases of it thereafter. This is definitely a WTH?
- 6y ago
- kazinator 6y agoreturn { c } fully initializes the struct according to the language semantics, and the inefficient code does that. What you've written is just a different way of spelling the full initialization, which should not have a radical effect on the generated code.
- Areading314 6y agoAgreed, just thought it was noteworthy
- kazinator 6y agoIt's alarming that such a high level difference has that much influence over code generation, in such a mature compiler.
- chrisseaton 6y agoHave you submitted this to the GCC issue tracker? If not why have you escalated it to here? What are you trying to achieve?
- mcolavita 6y agoAwareness about seemingly innocuous code that compiles down inefficiently, and to see if there are known issues with GCC assembling aggregates on the stack. It's submitted to the tracker here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98335 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98335
- deleted 6y ago[deleted]
- kazinator 6y agomovq %rdi, -24(%rsp) movq -24(%rsp), %rax It's amazing that this pattern isn't caught by peephole optimization. Assuming we want %rdi to end up in both destinations, memory and %rax, that wants to be: movq %rdi, -24(%rsp) movq %rdi, %rax Is it true that we do not want it in both destinations? It's as if the code is trying to be compatible with two conventions for returning small structs, which could be a legitimate requirement. A library function catering to two conventions at the same time will be compatible with client code compiled with either one without itself having to be recompiled.
- chubot 6y agoHm I tried "fixing" it it with return value optimization, like: Data val(uint8_t c) { Data tmp = { c }; return tmp; } but it made it worse?
- Koffiepoeder 6y agoInterestingly the first example fails on clang, because of the implicit conversion from `uint8_t` to `int8_t` in the struct.
- ncmncm 6y agoI recently filed a bug against a regression in Gcc 9 and 10 switch statements that changed a "bt" instruction to a branch through a 168-byte table. It was fixed for 11, but left in 9 and 10 because Gcc "does not backport optimization regression fixes" apparently because they risk introducing new bugs. ?! The author of the regression wanted to leave it in place, in 11.