https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80491
--- Comment #5 from Jakub Jelinek <jakub at gcc dot gnu.org> --- Created attachment 41256 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=41256&action=edit gcc8-pr80491.patch The conditional jump is part of the overflow checking optabs, the expectation was that we have ifcvt.c and combine.c that should be able to transform it back into add with carry etc. if beneficial. Otherwise we'd need to add optabs next to the overflow checking with jump that would perform effectively setcc (1 for overflow, 0 otherwise into a pseudo) and during expansion check the uses of the IMAGPART_EXPR to see which kind of expansion is more beneficial if target provides both. Anyway, there are two reasons why ifcvt.c/combine.c don't DTRT here. One is that ifcvt.c for some strange reason when it sees no else_bb only looks at a single insn before the condition, while there might be other insns in between x's setter and the cond_earliest insn. This is hopefully fixed by the attached patch. And the second issue is that in the combiner is called on: (insn 43 10 18 2 (set (reg:DI 97 [ _15+8 ]) (ltu:DI (reg:CCC 17 flags) (const_int 0 [0]))) 610 {*setcc_di_1} (expr_list:REG_DEAD (reg:CCC 17 flags) (nil))) ... (insn 20 19 21 2 (parallel [ (set (reg:DI 106) (plus:DI (mem:DI (plus:DI (reg/v/f:DI 101 [ a ]) (const_int 8 [0x8])) [2 a_11(D)->hi+0 S8 A64]) (reg:DI 108 [ b_12(D)->hi ]))) (clobber (reg:CC 17 flags)) ]) "pr80491.c":8 218 {*adddi_1} (expr_list:REG_DEAD (reg/v/f:DI 101 [ a ]) (expr_list:REG_UNUSED (reg:CC 17 flags) (expr_list:REG_DEAD (reg:DI 108 [ b_12(D)->hi ]) (nil))))) ... (note 21 20 22 2 NOTE_INSN_DELETED) (note 22 21 23 2 NOTE_INSN_DELETED) (note 23 22 24 2 NOTE_INSN_DELETED) (insn 24 23 39 2 (parallel [ (set (reg:DI 95 [ _9 ]) (plus:DI (reg:DI 106) (reg:DI 97 [ _15+8 ]))) (clobber (reg:CC 17 flags)) ]) "pr80491.c":8 218 {*adddi_1} (expr_list:REG_DEAD (reg:DI 97 [ _15+8 ]) (expr_list:REG_UNUSED (reg:CC 17 flags) (expr_list:REG_DEAD (reg:DI 106) (nil))))) where i3 is 24, i2 is 43 and i1 is 20 (and not i2 20 and i1 43), and we bail out on that early because the flags hard register is clobbered in insn 20, I think it is: && use_crosses_set_p (src, DF_INSN_LUID (insn))) in can_combine_p that fails for the flags register. If I slightly modify the testcase, like: struct S { unsigned long long low, hi; }; struct S add (struct S *a, struct S *b) { struct S s; s.hi = a->hi + b->hi; s.low = a->low + b->low; s.hi += (s.low < a->low); return s; } then unpatched gcc at -O2 on x86_64-linux still creates: movq 8(%rsi), %rdx movq 8(%rdi), %rcx movq (%rsi), %rax addq %rdx, %rcx xorl%edx, %edx addq (%rdi), %rax jc .L5 .L2: addq %rcx, %rdx ret .L5: movl $1, %edx jmp .L2 but patched gcc creates: movq (%rsi), %rax addq (%rdi), %rax movq 8(%rsi), %rdx adcq 8(%rdi), %rdx This is because then the high addition except for the carry comes before the overflow checking addition and so combine.c sees the two insns in a different order.