https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81275
Jakub Jelinek <jakub at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |mpolacek at gcc dot gnu.org --- Comment #4 from Jakub Jelinek <jakub at gcc dot gnu.org> --- Marek, could we reuse the fallthrough warning infrastructure for this to determine whether there is a possible fallthrough or not? Though, trying: int foo (int a, int b, int c) { switch (c) { case 5: switch (a) { case 0: switch (b) { default: return 0; } break; #ifdef FT case 7: break; #endif default: return 0; } case 6: return 7; } return 8; } we don't warn about the fallthrough from case 5 to case 6 no matter if FT is defined (in that case there is obvious fallthrough, say for foo (7, 0, 5), or when FT is not defined (then there isn't, but gimple_seq_may_fallthru doesn't know that). Whether a label is emitted after a switch (for the break; label) is determined already in the FEs based on whether any break; is present, no matter if reachable or not. So indeed, dead code such as the break; in your #c0 testcase, or whatever other code followed by a break, is able to confuse the gimplifier and eh lowering into assuming there might be fallthrough. While the -Wreturn-type warning is emitted after cfg is built, the problem is if we decide to emit the try/finally with a finally_tmp variable to avoid duplicating the cleanup actions, then while after building cfg we optimize away obviously unreachable code, we have no SSA form yet and thus determining if the finally_tmp.N variable is always the same in all paths might be expensive. At -O0 that actually isn't optimized by anything until assembly is emited, so we still see in the assembly: testl %eax, %eax jne .L9 movl $0, %ebx movl $0, %r12d jmp .L4 .L9: movl $0, %ebx movl $0, %r12d .L4: // Here both %ebx and %r12d are 0 (set the same on both paths reaching this // label. leaq -17(%rbp), %rax movq %rax, %rdi // Here we run ~C() call _ZN1CD1Ev // Now we test whether it is 1, which is never, in that case we fall through // into returning without a value. cmpl $1, %r12d jne .L10 nop jmp .L1 .L10: movl %ebx, %eax .L1: addq $32, %rsp popq %rbx popq %r12 popq %rbp ret So, if we can't do anyuthing for this at the gimplifier level, we'd need to add an optimization in the cfg pass that would look for cases like: ;; basic block 4, loop depth 0 ;; pred: 3 <L0> [0.00%] [count: INV]: D.2315 = 0; finally_tmp.0 = 0; goto <bb 6>; [INV] [count: INV] ;; succ: 6 ;; basic block 5, loop depth 0 ;; pred: 3 <L2> [0.00%] [count: INV]: D.2315 = 0; finally_tmp.0 = 0; ;; succ: 6 ;; basic block 6, loop depth 0 ;; pred: 4 ;; 5 C::~C (&c); switch (finally_tmp.0) <default: <L12> [INV] [count: INV], case 1: <L5> [INV] [count: INV]> and optimize that switch into goto <L12>; after checking that finally_tmp.0 var is not addressable, not modified in the bb with the switch and set to the same value at the end of all predecessor bbs. But that would be an optimization, not sure if we want to do that generally for -O0, or limit it to somehow specially marked finally_tmp.NN variables.