4 ms·
I don't think "dead-code elimination removes dead code" adds much to the discussion. If you change the code so that the value of `a` is used, then the output i
by jmillikin 2y ago
I don't think "dead-code elimination removes dead code" adds much to the discussion.
If you change the code so that the value of `a` is used, then the output is as expected: https://godbolt.org/z/78eYx37WG https://godbolt.org/z/78eYx37WG
- xmcqdpt2 2y agoThe parent example can be made clearer like this: https://godbolt.org/z/MKWbz9W16 https://godbolt.org/z/MKWbz9W16 Dead code elimination only works here because integer overflow is UB.
- jmillikin 2y agoTake a closer look at 'eru's example and my follow-up. He wrote an example where the result of `a+1` isn't necessary, so the compiler doesn't emit an ADDI even though the literal text of the C source contains the substring "a += 1". Your version has the same issue: unsigned int square2(unsigned int num) { unsigned int a = num; a += 1; if (num < a) return num * num; return num; } The return value doesn't depend on `a+1`, so the compiler can optimize it to just a comparison. If you change it to this: unsigned int square2(unsigned int num) { unsigned int a = num; a += 1; if (num < a) return num * a; return num; } then the result of `a+1` is required to compute the result in the first branch, and therefore the ADDI instruction is emitted. The (implied) disagreement is whether a language can be considered to be "portable assembly" if its compiler elides unnecessary operations from the output. I think that sort of optimization is allowed, but 'eru (presumably) thinks that it's diverging too far from the C source code.
- gpderetta 2y agoIn what world the return value doesn't depends on 'a' in this code? if (num < a) return num * num; /*else*/ return num; A control dependency is still a dependency
- jmillikin 2y ago`a = num; a += 1; if (num < a)` is the same as `if (num < (num + 1))`, which for unsigned integer addition can be rewritten as `if (num != UINT_MAX)`. So there's no need to actually compute `a+1`, the comparison is against a constant. If the code returns `num * a` then the value of `a` is now necessary, and must be computed before the function returns. For signed integer addition the compiler is allowed to assume that `(num < (num + 1))` is true, so the comparison can be removed entirely.
- eru 2y ago> For signed integer addition the compiler is allowed to assume that `(num < (num + 1))` is true, so the comparison can be removed entirely. That's not directly what the compiler assumes. The direct problem is in 'a + 1' having undefined behaviour, and that transitively allows the assumption on the comparison that you mentioned. This was an example where 'a + 1' doesn't compile to an add instruction.
- xmcqdpt2 2y agoC compilers are just too smart IMO to make C portable assembly. Your example doesn’t always ADDI either, for example if it is inlined https://godbolt.org/z/vv9rvKsxn https://godbolt.org/z/vv9rvKsxn This isn’t qualitatively different from what the JVM JIT would do, but Java isn’t considered portable assembly. I guess if you compile with optimizations completely off, you get something that is assembly-like, but I’ve never seen that in prod code.
- eru 2y ago> He wrote an example where the result of `a+1` isn't necessary, so the compiler doesn't emit an ADDI even though the literal text of the C source contains the substring "a += 1". No, the result of the 'a+1' is necessary in my version. And if you change the type from 'int' to 'unsigned' you will see that the compiler no longer just omits the addition.