https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99221
Bug ID: 99221 Summary: copyprop_hardreg_forward_1 deletes insn by mistake Product: gcc Version: 11.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: rtl-optimization Assignee: unassigned at gcc dot gnu.org Reporter: stefansf at linux dot ibm.com Target Milestone: --- Created attachment 50242 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=50242&action=edit a-foo.i.301r.jump2 Consider the following reduced example: int g = 9, h, i, b; char j, k; long m; char *a = &j; char n() { return 0; } void o(short o) {} short p(short o, short q) { return q; } long r(long s) { return s; } long t(long s) { return 0; } long u(long s) { return 0; } short v() { return 0; } void x(int **s, unsigned q) { short c = 0, d = 0; int e = 4; if (g) { int f = 4; q = 1; for (; q <= 4; q++) { m = q; d = i = u(d); if (0 == r(1)) { if (u(0)) { h = t(q) != **s; for (; e; e = f &= 0 <= 0) ; } } else { b = c; c = v(); o(q); k = n(); j = p(k || q, q); } } } } int main() { x(0, 0); printf("%d\n", j); return 0; } Command line: gcc -march=arch11 -w -Og foo.i && ./a.out Expected output: 4 Actual output: 0 On IBM Z we have prior pass cprop_hardreg the following insns of interest: (insn 24 23 25 4 (set (reg:DI 24 %f8 [orig:61 _2 ] [61]) (reg/v:DI 12 %r12 [orig:85 q+-4 ] [85])) "./foo.i":19:10 1462 {*movdi_64} (nil)) ... (insn 80 79 81 10 (set (reg:HI 24 %f8 [orig:74 _15 ] [74]) (reg:HI 12 %r12 [orig:85 q+2 ] [85])) "./foo.i":30:10 1474 {*movhi} (nil)) ... (insn 155 96 97 15 (set (reg:HI 1 %r1 [orig:74 _15 ] [74]) (reg:HI 24 %f8 [orig:74 _15 ] [74])) "./foo.i":32:14 1474 {*movhi} (nil)) Register f8 is set to a 64-bit value in insn 24 and to a 16-bit value in insn 80, respectively, while using the same source register r12. During copyprop_hardreg_forward_1 it is then wrongly detected that insn 80 is a noop set and is subsequently removed. Due to different alignments of different modes in FPRs we have that in insn 155 the wrong part of register f8 is then accessed which results in constant value zero. In order to decide whether an insn is a noop set or not I gave it a try by additionally testing whether the prior set and the current are done in compatible modes by asking the backend: diff --git a/gcc/regcprop.c b/gcc/regcprop.c index e1342f56bd1..02753a12510 100644 --- a/gcc/regcprop.c +++ b/gcc/regcprop.c @@ -474,7 +474,8 @@ find_oldest_value_reg (enum reg_class cl, rtx reg, struct value_data *vd) (set (...) (reg:DI r9)) Replacing r9 with r11 is invalid. */ if (mode != vd->e[regno].mode - && REG_NREGS (reg) > hard_regno_nregs (regno, vd->e[regno].mode)) + && (REG_NREGS (reg) > hard_regno_nregs (regno, vd->e[regno].mode) + || !REG_CAN_CHANGE_MODE_P (regno, mode, vd->e[regno].mode))) return NULL_RTX; for (i = vd->e[regno].oldest_regno; i != regno; i = vd->e[i].next_regno) Any thoughts about this fix?