struct rtattr { unsigned short rta_len; unsigned short rta_type; }; __attribute__ ((noinline)) int inet_check_attr (void *r, struct rtattr **rta) { int i;
for (i = 1; i <= 14; i++) { struct rtattr *attr = rta[i - 1]; if (attr) { if (attr->rta_len - sizeof (struct rtattr) < 4) return -22; if (i != 9 && i != 8) rta[i - 1] = attr + 1; } } return 0; } extern void abort (void); int main (void) { struct rtattr rt[2]; struct rtattr *rta[14]; int i; rt[0].rta_len = sizeof (struct rtattr) + 8; rt[0].rta_type = 0; rt[1] = rt[0]; for (i = 0; i < 14; i++) rta[i] = &rt[0]; if (inet_check_attr (0, rta) != 0) abort (); for (i = 0; i < 14; i++) if (rta[i] != &rt[i != 7 && i != 8]) abort (); for (i = 0; i < 14; i++) rta[i] = &rt[0]; rta[1] = 0; rt[1].rta_len -= 8; rta[5] = &rt[1]; if (inet_check_attr (0, rta) != -22) abort (); for (i = 0; i < 14; i++) if (i == 1 && rta[i] != 0) abort (); else if (i != 1 && i <= 5 && rta[i] != &rt[1]) abort (); else if (i > 5 && rta[i] != &rt[0]) abort (); return 0; } is miscompiled on i386-linux at -Os. The bug seems to be in strength-reduction. The loop starts with unsigned int ivtmp.14 = -7U; and keeps cycling until ivtmp.14 >= 7U, ivtmp.14 is incremented by 1 in each iteration and the if (i != 9 && i != 8) test has been transformed into if (ivtmp.14 <= 1U) test. But, strength reduction transforms: (insn 42 40 43 4 (set (reg:CC 17 flags) (compare:CC (reg:SI 61 [ ivtmp.14 ]) (const_int 1 [0x1]))) 5 {*cmpsi_1_insn} (nil) (nil)) (jump_insn 43 42 45 4 (set (pc) (if_then_else (leu (reg:CC 17 flags) (const_int 0 [0x0])) (label_ref 49) (pc))) 489 {*jcc_1} (nil) (expr_list:REG_BR_PROB (const_int 2100 [0x834]) (nil))) into: (insn 42 40 43 4 (set (reg:CC 17 flags) (compare:CC (reg/f:SI 75) (reg/f:SI 77))) -1 (nil) (nil)) (jump_insn 43 42 45 4 (set (pc) (if_then_else (leu (reg:CC 17 flags) (const_int 0 [0x0])) (label_ref 49) (pc))) -1 (nil) (expr_list:REG_BR_PROB (const_int 2100 [0x834]) (nil))) where pseudo 77 is constant, initialized to: (insn 85 84 86 0 (parallel [ (set (reg/f:SI 77) (plus:SI (reg/v/f:SI 66 [ rta ]) (const_int 4 [0x4]))) (clobber (reg:CC 17 flags)) ]) -1 (nil) (nil)) and pseudo 75 is initialized to: (insn 82 14 84 0 (parallel [ (set (reg/f:SI 75) (plus:SI (reg/v/f:SI 66 [ rta ]) (const_int -28 [0xffffffffffffffe4]))) (clobber (reg:CC 17 flags)) ]) -1 (nil) (nil)) and increment by 4 at the end of each iteration. This transformation should work fine for EQ/NE comparisons, but really can't work for other unsigned comparisons (and not really sure about GT/LT/GE/LE). The testcase is distilled from Linux kernel, so this is quite important problem. -- Summary: [4.0 Regression] Strength-reduction breaking unsigned COMPARE Product: gcc Version: 4.0.2 Status: UNCONFIRMED Severity: critical Priority: P2 Component: rtl-optimization AssignedTo: unassigned at gcc dot gnu dot org ReportedBy: jakub at gcc dot gnu dot org CC: gcc-bugs at gcc dot gnu dot org GCC target triplet: i386-linux http://gcc.gnu.org/bugzilla/show_bug.cgi?id=23560