This patch is to avoid PR120423 and PR116389 on avr. The PRs are about paradoxical subregs, that ICE after old reload as follows:
For rtxes like (subreg:HI (QI) 0), the inner rtx may be reloaded to (reg:QI 31) which is fine, but the paradoxical subreg will be changed to (reg:HI 31) which is invalid since all hard regs that hold more than one byte must start in an even register. Reload calls targetm.hard_regno_mode_ok on the QI reg several times and in several places, but in no case it calls that hook on the HImode register. Moreover, in struct reload there is no information about it's a subreg, so there is no simple fix in reload. The patch passes without new regressions on avr + v15. It is not activated when LRA is on (-mlra). The PR120423 test cases generate the same code with the patch like with -mlra, so the patch doesn not even introduce some performance penalty. The patch is only for v15, though it still works with trunk. I have no plans to apply it on trunk, but when you like to see it on trunk, too, that is no problem. The patch is of course only in lack of a better patch. Ok for v15? Johann -- AVR: Fix PR120423 / PR116389.The problem with PR120423 and PR116389 is that reload might assign an invalid
hard register to a paradoxical subreg. For example with the test case from the PR, it assigns (REG:QI 31) to the inner of (subreg:HI (QI) 0) which is valid, but the subreg will be turned into (REG:HI 31) which is invalid and triggers an ICE in postreload. The problem only occurs with the old reload pass. The patch maps the paradoxical subregs to a zero-extends which will be allocated correctly. For the 120423 testcases, the code is the same like with -mlra (which doesn't implement the fix), so the patch doesn't even introduce a performance penalty. The patch is only needed for v15: v14 is not affected, and in v16 reload will be removed. PR rtl-optimization/120423 PR rtl-optimization/116389 gcc/ * config/avr/avr.md [-mno-lra]: Add pre-reload split to transform (left shift of) a paradoxical subreg to a (left shift of) zero-extend. gcc/testsuite/ * gcc.target/avr/torture/pr120423-1.c: New test. * gcc.target/avr/torture/pr120423-2.c: New test. * gcc.target/avr/torture/pr120423-116389.c: New test.
diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 01b8e4bce4c..f8bbdc76608 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -5273,6 +5273,41 @@ (define_insn_and_split "*rotb<mode>" ;;<< << << << << << << << << << << << << << << << << << << << << << << << << << ;; arithmetic shift left +;; Work around PR120423: Transform left shift of a paradoxical subreg +;; into left shift of the zero-extended entity. +(define_split ; PR120423 + [(set (match_operand:HISI 0 "register_operand") + (ashift:HISI (subreg:HISI (match_operand:QIPSI 1 "nonimmediate_operand") + 0) + (match_operand:QI 2 "const_int_operand")))] + "!reload_completed + && !avropt_lra_p + && <HISI:SIZE> > <QIPSI:SIZE>" + [(set (match_dup 4) + (zero_extend:HISI (match_dup 5))) + (set (match_dup 0) + (ashift:HISI (match_dup 4) + (match_dup 2)))] + { + operands[4] = gen_reg_rtx (<HISI:MODE>mode); + operands[5] = force_reg (<QIPSI:MODE>mode, operands[1]); + }) + +;; Similar happens for PR116389. +(define_split ; PR116389 + [(set (match_operand:HISI 0 "register_operand") + (subreg:HISI (match_operand:QIPSI 1 "nonimmediate_operand") + 0))] + "!reload_completed + && !avropt_lra_p + && <HISI:SIZE> > <QIPSI:SIZE>" + [(set (match_dup 0) + (zero_extend:HISI (match_dup 2)))] + { + operands[2] = force_reg (<QIPSI:MODE>mode, operands[1]); + }) + + ;; "ashlqi3" ;; "ashlqq3" "ashluqq3" (define_expand "ashl<mode>3" diff --git a/gcc/testsuite/gcc.target/avr/torture/pr120423-1.c b/gcc/testsuite/gcc.target/avr/torture/pr120423-1.c new file mode 100644 index 00000000000..91b4bbc812c --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/pr120423-1.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ + +struct data +{ + int a; + int b; + long c; +}; + +unsigned char val; +unsigned val2; + +void func1 (struct data *d) +{ + d->a = 0; + d->b = 0x100 * val - 1; +} + +void func2 (struct data *d) +{ + d->a = 0; + d->c = 0x10000 * val2 - 1; +} + +void func3 (struct data *d) +{ + d->a = 0; + d->c = 0x1000000 * val - 1; +} diff --git a/gcc/testsuite/gcc.target/avr/torture/pr120423-116389.c b/gcc/testsuite/gcc.target/avr/torture/pr120423-116389.c new file mode 100644 index 00000000000..928c1358c3c --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/pr120423-116389.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ + +struct T { int val; }; + +void f_int (int); +char* get_pos (void); +struct T* get_pT (void); + +void func (char i) +{ + struct T t = * get_pT (); + unsigned diff = get_pos () - &i; + + if (diff) + { + long val32 = t.val; + if (get_pos ()) + val32 = diff; + if (get_pos ()) + f_int (2 * val32); + } +} diff --git a/gcc/testsuite/gcc.target/avr/torture/pr120423-2.c b/gcc/testsuite/gcc.target/avr/torture/pr120423-2.c new file mode 100644 index 00000000000..56e61415afb --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/pr120423-2.c @@ -0,0 +1,30 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-ffixed-18 -ffixed-20 -ffixed-22" } */ + +struct data +{ + int a; + int b; + long c; +}; + +unsigned char val; +unsigned val2; + +void func1 (struct data *d) +{ + d->a = 0; + d->b = 0x100 * val - 1; +} + +void func2 (struct data *d) +{ + d->a = 0; + d->c = 0x10000 * val2 - 1; +} + +void func3 (struct data *d) +{ + d->a = 0; + d->c = 0x1000000 * val - 1; +}