https://gcc.gnu.org/g:61789b5abec3079d02ee9eaa7468015ab1f6f701
commit r15-9837-g61789b5abec3079d02ee9eaa7468015ab1f6f701 Author: Georg-Johann Lay <a...@gjlay.de> Date: Sat Jun 14 19:57:18 2025 +0200 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: --- gcc/config/avr/avr.md | 35 ++++++++++++++++++++++ gcc/testsuite/gcc.target/avr/torture/pr120423-1.c | 29 ++++++++++++++++++ .../gcc.target/avr/torture/pr120423-116389.c | 22 ++++++++++++++ gcc/testsuite/gcc.target/avr/torture/pr120423-2.c | 30 +++++++++++++++++++ 4 files changed, 116 insertions(+) diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 01b8e4bce4cf..f8bbdc766085 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -5273,6 +5273,41 @@ ;;<< << << << << << << << << << << << << << << << << << << << << << << << << << ;; 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 000000000000..91b4bbc812c8 --- /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 000000000000..928c1358c3c3 --- /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 000000000000..56e61415afb9 --- /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; +}