Bug 116780 - [lra][avr] internal compiler error: output_operand: address operand requires constraint for X, Y, or Z register
Summary: [lra][avr] internal compiler error: output_operand: address operand requires ...
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: rtl-optimization (show other bugs)
Version: 15.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: ice-on-invalid-code, ra
Depends on:
Blocks: avr+ra 113934
  Show dependency treegraph
 
Reported: 2024-09-19 11:43 UTC by Georg-Johann Lay
Modified: 2024-10-22 17:45 UTC (History)
2 users (show)

See Also:
Host:
Target: avr
Build:
Known to work:
Known to fail:
Last reconfirmed: 2024-09-19 00:00:00


Attachments
pr64088.c: C test case (169 bytes, text/x-csrc)
2024-09-19 11:43 UTC, Georg-Johann Lay
Details
Simplified testcase (52 bytes, text/plain)
2024-10-19 11:01 UTC, Denis Chertykov
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Georg-Johann Lay 2024-09-19 11:43:42 UTC
Created attachment 59148 [details]
pr64088.c: C test case

$ avr-gcc pr64088.c -S -O1 -mlra -fdump-rtl-final 
during RTL pass: final
dump file: pr64088.c.358r.final
pr64088.c: In function 'setup_tone_curves_center_boost':
pr64088.c:17:1: internal compiler error: output_operand: address operand requires constraint for X, Y, or Z register
   17 | }
      | ^
0x6f39cc output_operand_lossage(char const*, ...)
	../../../source/gcc-master/gcc/final.cc:3190
0xdf09d8 ptrreg_to_str
	../../../source/gcc-master/gcc/config/avr/avr.cc:2416
0xdf0ba2 avr_print_operand_address
	../../../source/gcc-master/gcc/config/avr/avr.cc:2484
0xdf17d6 avr_print_operand
	../../../source/gcc-master/gcc/config/avr/avr.cc:2694
0x6f479f output_operand(rtx_def*, int)
	../../../source/gcc-master/gcc/final.cc:3632
0x6f42c2 output_asm_insn(char const*, rtx_def**)
	../../../source/gcc-master/gcc/final.cc:3525
0xdf0960 avr_asm_len
	../../../source/gcc-master/gcc/config/avr/avr.cc:2392
0xdf7db0 out_movhi_mr_r
	../../../source/gcc-master/gcc/config/avr/avr.cc:5756
0xdf46a0 output_movhi(rtx_insn*, rtx_def**, int*)
	../../../source/gcc-master/gcc/config/avr/avr.cc:3981
0x6f0e7c get_insn_template(int, rtx_insn*)
	../../../source/gcc-master/gcc/final.cc:2027
0x6f2a2f final_scan_insn_1
	../../../source/gcc-master/gcc/final.cc:2773
0x6f2ea7 final_scan_insn(rtx_insn*, _IO_FILE*, int, int, int*)
	../../../source/gcc-master/gcc/final.cc:2886
0x6f0ca3 final_1
	../../../source/gcc-master/gcc/final.cc:1977
0x6f59af rest_of_handle_final
	../../../source/gcc-master/gcc/final.cc:4240
0x6f5d0e execute
	../../../source/gcc-master/gcc/final.cc:4318
0x7f2d3446ed8f __libc_start_call_main
	../sysdeps/nptl/libc_start_call_main.h:58
0x7f2d3446ee3f __libc_start_main_impl
	../csu/libc-start.c:392

The reason why avr.cc::ptrreg_to_str() runs into output_operand_lossage() is that there are insns with invalid mem operands, like seen in the .final dump:

(insn 27 2 28 (parallel [
            (set (mem:HI (plus:HI (reg/f:HI 32 __SP_L__)
                        (const_int 1 [0x1])) [1 b[0]+0 S2 A8])
                (const_int 0 [0]))
            (clobber (reg:CC 36 cc))
        ]) "pr64088.c":14:12 106 {*movhi}
     (expr_list:REG_UNUSED (reg:CC 36 cc)
        (nil)))

The memory location is invalid because the stack pointer (regno __SP_L__) cannot be used to access stack slots.  Indirect addressing and indirect addressing with offset must use one of the X, Y or Z registers (regno HI:26, HI:28, HI:30 respectively).

The code from old reload (-mno-lra) is correct: It loads SP to reg:HI 30 (Z):

(insn 28 2 29 (parallel [
            (set (reg:HI 30 r30)
                (reg/f:HI 32 __SP_L__))
            (clobber (reg:CC 36 cc))
        ]) "pr64088.c":14:12 106 {*movhi}
     (expr_list:REG_UNUSED (reg:CC 36 cc)
        (nil)))
(insn 29 28 30 (parallel [
            (set (mem:HI (plus:HI (reg:HI 30 r30)
                        (const_int 1 [0x1])) [1 b[0]+0 S2 A8])
                (const_int 0 [0]))
            (clobber (reg:CC 36 cc))
        ]) "pr64088.c":14:12 106 {*movhi}
     (expr_list:REG_UNUSED (reg:CC 36 cc)
        (nil)))

Target: avr
Configured with: ../../source/gcc-master/configure --target=avr --disable-nls --with-dwarf2 --with-gnu-as --with-gnu-ld --disable-shared --enable-languages=c,c++
Thread model: single
Supported LTO compression algorithms: zlib
gcc version 15.0.0 20240918 (experimental) (GCC)
Comment 1 Denis Chertykov 2024-10-19 11:01:17 UTC
Created attachment 59393 [details]
Simplified testcase

Very simple testcase
Comment 2 Denis Chertykov 2024-10-19 11:02:27 UTC
Comment on attachment 59393 [details]
Simplified testcase

void
f ()
{
  volatile char c[0];
  c[0] = 0;
}
Comment 3 Georg-Johann Lay 2024-10-19 12:13:34 UTC
Maybe this one is related to the fact that LRA doesn't set strict when it is in strict-RTL mode?  For example, with your latest test case:

$avr-gcc foo.c -S -mlra -O1 -mmcu=atmega128 -fdump-rtl-final -mlog=legitimate_address_p

...
avr_legitimate_address_p[f:reload(322)]: ret=1, mode=QI strict=0 reload_completed=0 ra_in_progress=1 (reg_renumber):(r28 ---> r28)
(plus:HI (reg/f:HI 28 r28)
    (const_int 1 [0x1]))

avr_legitimate_address_p[f:reload(322)]: ret=1, mode=QI strict=0 reload_completed=0 ra_in_progress=1 (reg_renumber):(r28 ---> r28)
(plus:HI (reg/f:HI 28 r28)
    (const_int 1 [0x1]))

avr_legitimate_address_p[f:reload(322)]: ret=1, mode=QI strict=0 reload_completed=1 ra_in_progress=0 (reg_renumber):(r32 ---> r32)
(plus:HI (reg/f:HI 32 __SP_L__)
    (const_int 1 [0x1]))

avr_legitimate_address_p[f:postreload(323)]: ret=1, mode=QI strict=0 reload_completed=1 ra_in_progress=0 (reg_renumber):(r32 ---> r32)
(plus:HI (reg/f:HI 32 __SP_L__)
    (const_int 1 [0x1]))

avr_legitimate_address_p[f:split2(327)]: ret=1, mode=QI strict=0 reload_completed=1 ra_in_progress=0 (reg_renumber):(r32 ---> r32)
(plus:HI (reg/f:HI 32 __SP_L__)
    (const_int 1 [0x1]))

...

i.e. strict=0 in all calls, and hence the backend returns ret=1 for SP+const addresses.
Comment 4 Denis Chertykov 2024-10-19 13:33:28 UTC
After IRA we have:
---------------------------------------
;; bb 2 artificial_defs: { }
;; bb 2 artificial_uses: { u-1(28){ }u-1(32){ }u-1(34){ }}
;; lr  in  28 [r28] 29 [r29] 32 [__SP_L__] 34 [argL]
;; lr  use 28 [r28] 29 [r29] 32 [__SP_L__] 34 [argL]
;; lr  def
(note 3 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
(note 2 3 5 2 NOTE_INSN_FUNCTION_BEG)
(insn 5 2 0 2 (set (mem/v:QI (plus:HI (reg/f:HI 28 r28)
                (const_int 1 [0x1])) [0 c[0]+0 S1 A8])
        (const_int 0 [0])) "a.c":5:8 86 {movqi_insn_split}
     (expr_list:REG_DEAD (reg:QI 29 r29)
        (nil)))
;;  succ:       EXIT [always]  count:1073741824 (estimated locally, freq 1.0000) (FALLTHRU) a.c:6:1
;; lr  out 28 [r28] 32 [__SP_L__] 34 [argL]
---------------------------------------

We have only one executable insn in test function `a' after IRA:
(insn 5 2 0 2 (set (mem/v:QI (plus:HI (reg/f:HI 28 r28)
                (const_int 1 [0x1])) [0 c[0]+0 S1 A8])
        (const_int 0 [0])) "a.c":5:8 86 {movqi_insn_split}
     (expr_list:REG_DEAD (reg:QI 29 r29)
        (nil)))

(reg:HI 28) is a frame pointer register Y or R28,r29 
LRA tries to eliminate frame pointer to stack pointer and uses TARGET_FRAME_POINTER_REQUIRED hook.

The hook:
static bool
avr_frame_pointer_required_p (void) 
{
  return (cfun->calls_alloca
  || cfun->calls_setjmp
  || cfun->has_nonlocal_label
  || crtl->args.info.has_stack_args
  || get_frame_size () > 0);
}

The hook return `true' because frame size is 0.

After LRA we have:
---------------------------------------
;; bb 2 artificial_defs: { }
;; bb 2 artificial_uses: { u-1(32){ }}
;; lr  in  32 [__SP_L__] 33 [__SP_H__]
;; lr  use 32 [__SP_L__] 33 [__SP_H__]
;; lr  def
(note 3 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
(note 2 3 5 2 NOTE_INSN_FUNCTION_BEG)
(insn 5 2 8 2 (set (mem/v:QI (plus:HI (reg/f:HI 32 __SP_L__)
                (const_int 1 [0x1])) [0 c[0]+0 S1 A8])
        (const_int 0 [0])) "a.c":5:8 86 {movqi_insn_split}
     (nil))
;;  succ:       EXIT [always]  count:1073741824 (estimated locally, freq 1.0000) (FALLTHRU) a.c:6:1
;; lr  out 32 [__SP_L__]
---------------------------------------

insn 5 has a wrong address. AVR can't use (reg/f:HI 32 __SP_L__) as an address register.

Probably we have two bugs:
1. AVR port assumes that LRA (or reload) can eliminate frame pointer to stack pointer (if no args and frame size = 0). It's wrong for this testcase;
2. LRA made a wrong elimination (may be because of AVR port have a wrong insn definition or something else)
Comment 5 Denis Chertykov 2024-10-19 13:39:24 UTC
(In reply to Georg-Johann Lay from comment #3)
> Maybe this one is related to the fact that LRA doesn't set strict when it is
> in strict-RTL mode?  For example, with your latest test case:
> 
> $avr-gcc foo.c -S -mlra -O1 -mmcu=atmega128 -fdump-rtl-final
> -mlog=legitimate_address_p
> 
> ...
> avr_legitimate_address_p[f:reload(322)]: ret=1, mode=QI strict=0
> reload_completed=0 ra_in_progress=1 (reg_renumber):(r28 ---> r28)
> (plus:HI (reg/f:HI 28 r28)
>     (const_int 1 [0x1]))
> 
> avr_legitimate_address_p[f:reload(322)]: ret=1, mode=QI strict=0
> reload_completed=0 ra_in_progress=1 (reg_renumber):(r28 ---> r28)
> (plus:HI (reg/f:HI 28 r28)
>     (const_int 1 [0x1]))
> 
> avr_legitimate_address_p[f:reload(322)]: ret=1, mode=QI strict=0
> reload_completed=1 ra_in_progress=0 (reg_renumber):(r32 ---> r32)
> (plus:HI (reg/f:HI 32 __SP_L__)
>     (const_int 1 [0x1]))
> 
> avr_legitimate_address_p[f:postreload(323)]: ret=1, mode=QI strict=0
> reload_completed=1 ra_in_progress=0 (reg_renumber):(r32 ---> r32)
> (plus:HI (reg/f:HI 32 __SP_L__)
>     (const_int 1 [0x1]))
> 
> avr_legitimate_address_p[f:split2(327)]: ret=1, mode=QI strict=0
> reload_completed=1 ra_in_progress=0 (reg_renumber):(r32 ---> r32)
> (plus:HI (reg/f:HI 32 __SP_L__)
>     (const_int 1 [0x1]))
> 
> ...
> 
> i.e. strict=0 in all calls, and hence the backend returns ret=1 for SP+const
> addresses.

Our messages crossed.
I have to analyze your information.
Comment 6 Denis Chertykov 2024-10-19 13:43:10 UTC
At least, our main problem is that we have a frame pointer usage without frame size.
Comment 7 Denis Chertykov 2024-10-19 13:50:58 UTC
Clarification for simplified test case:
1. frame size is 0 (because a declaration of a local array has a zero size);
2. we have a local variable which can be addressable (althought it have a zero size)
Comment 8 Segher Boessenkool 2024-10-20 00:22:58 UTC
(In reply to denisc from comment #2)
> Comment on attachment 59393 [details]
> Simplified testcase
> 
> void
> f ()
> {
>   volatile char c[0];
>   c[0] = 0;
> }

That is invalid C code, of course (an out of bounds access).
Comment 9 Georg-Johann Lay 2024-10-20 07:18:26 UTC
(In reply to Segher Boessenkool from comment #8)
> That is invalid C code, of course (an out of bounds access).
What about the other test case https://gcc.gnu.org/bugzilla/attachment.cgi?id=59148 ?

That's from an already existing test case for PR64088, so why has an ICE been fixed for that PR instead of closing it as invalid? -- Or ar least rejecting that test case?
Comment 10 Georg-Johann Lay 2024-10-20 07:29:09 UTC
...hmmm https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html reads: <quote>

6.18 Arrays of Length Zero

Declaring zero-length arrays is allowed in GNU C as an extension. A zero-length array can be useful as the last element of a structure that is really a header for a variable-length object:

[...]

Declaring zero-length arrays in other contexts, including [...] as non-member objects, is discouraged. Accessing elements of zero-length arrays declared in such contexts is undefined and may be diagnosed. 

</quote>

So the test case for PR64088 is already invalid?
Comment 11 Denis Chertykov 2024-10-20 11:17:05 UTC
(In reply to Segher Boessenkool from comment #8)
> (In reply to denisc from comment #2)
> > Comment on attachment 59393 [details]
> > Simplified testcase
> > 
> > void
> > f ()
> > {
> >   volatile char c[0];
> >   c[0] = 0;
> > }
> 
> That is invalid C code, of course (an out of bounds access).

I don't think that it's invalid.(maybe I'm wrong)

Segher, can you suggest how to obtain information (from internal GCC structures) about such cases ?
ie rewrite TARGET_FRAME_POINTER_REQUIRED hook so that it takes into account that we have an access to frame but frame-size is zero ?

The current hook:
static bool
avr_frame_pointer_required_p (void) 
{
  return (cfun->calls_alloca
  || cfun->calls_setjmp
  || cfun->has_nonlocal_label
  || crtl->args.info.has_stack_args
  || get_frame_size () > 0);
}
Comment 12 Segher Boessenkool 2024-10-20 20:14:21 UTC
(In reply to denisc from comment #11)
> (In reply to Segher Boessenkool from comment #8)
> > (In reply to denisc from comment #2)
> > > Comment on attachment 59393 [details]
> > > Simplified testcase
> > > 
> > > void
> > > f ()
> > > {
> > >   volatile char c[0];
> > >   c[0] = 0;
> > > }
> > 
> > That is invalid C code, of course (an out of bounds access).
> 
> I don't think that it's invalid.(maybe I'm wrong)

You access elt 0 of the array, but the array has *no* elements.
Comment 13 Segher Boessenkool 2024-10-20 20:24:35 UTC
(In reply to Georg-Johann Lay from comment #9)
> (In reply to Segher Boessenkool from comment #8)
> > That is invalid C code, of course (an out of bounds access).
> What about the other test case
> https://gcc.gnu.org/bugzilla/attachment.cgi?id=59148 ?

Same problem there.  It is not valid C.

> That's from an already existing test case for PR64088, so why has an ICE
> been fixed for that PR instead of closing it as invalid? -- Or ar least
> rejecting that test case?

Many such problems cannot ever be diagnosed by the compiler.  Simple cases like
here could be of course, but in general it is the halting problem.

It is fine to have testcases that are invalid C, if there is no reasonable other
way to get at the same problems otherwise, but it of course would be a lot nicer
to have a testcase that is valid C :-)

> Segher, can you suggest how to obtain information (from internal GCC structures) > about such cases ?

I would do something scan-assembler.  I have no idea what instructions accessing
the frame look like on AVR, but I imagine it won't be too hard to see this?

But I know nothing that can be (or *could* be, even) done that is not
target-specific.  But this testcase is in gcc.target/ anyway, right?
Comment 14 Georg-Johann Lay 2024-10-21 15:30:16 UTC
(In reply to Segher Boessenkool from comment #13)
> But this testcase is in gcc.target/ anyway, right?
That's just a copy of  gcc.dg/torture/pr64088.c :

https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/testsuite/gcc.dg/torture/pr64088.c;h=0ea5fabcc2fc30833d9ab80ce30090f23e46594b;hb=HEAD

This test case works with avr+reload but it fails with avr+lra, so it appeared to be lra related.

As gcc.dg/torture/pr64088.c is not target-specific, it's supposed to make sense on all targets? And with lra too, of course.  No?
Comment 15 Segher Boessenkool 2024-10-21 20:53:10 UTC
(In reply to Georg-Johann Lay from comment #14)
> (In reply to Segher Boessenkool from comment #13)
> > But this testcase is in gcc.target/ anyway, right?
> That's just a copy of  gcc.dg/torture/pr64088.c :
> 
> https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/testsuite/gcc.dg/torture/
> pr64088.c;h=0ea5fabcc2fc30833d9ab80ce30090f23e46594b;hb=HEAD

Yeah, that is clearly not valid C at all already.

> This test case works with avr+reload but it fails with avr+lra, so it
> appeared to be lra related.
> 
> As gcc.dg/torture/pr64088.c is not target-specific, it's supposed to make
> sense on all targets? And with lra too, of course.  No?

It makes sense never, not on any target, not with LRA nor without.  It is
incorrect according to 6.7.6.2/1 already ("If the expression is a constant
expression, it shall have a value greater than zero."), but dereferencing a
non-existing object isn't correct either, obviously.
Comment 16 Georg-Johann Lay 2024-10-22 08:36:47 UTC
(In reply to Segher Boessenkool from comment #15)
> It makes sense never, not on any target, not with LRA nor without.
Though there are test cases that are UB and valid as I just
learned some weeks ago.  When I came across gcc.dg/signbit-6.c

https://gcc.gnu.org/pipermail/gcc/2024-October/244903.html

Quote:

> > So as far as I can see, that test has at least 3 bugs?
> > 
> > 1) Missing -fwrapv so that -INT_MIN is no more UB, hence the
> > test would assert that a[0] == b[0].
> 
> That's not a bug.

This is the test case:

https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/testsuite/gcc.dg/signbit-6.c;h=3a522893222d067eb68e242e48789b2f919fbebb;hb=HEAD#l40

Line 40 ff. lead to -INT_MIN  *without*  -fwrapv or similar.

> 41 /* This will invoke UB due to -INT32_MIN.  The test is supposed to pass
> 42    because GCC is supposed to handle this UB case in a predictable way.  */
> 43 a[0] = INT32_MIN;
> 44 b[0] = INT32_MIN;
 
That comment was added by myself.  According to Tamar, this UB test case was explicitly requestet during the review for some new feature / new optimization.

> The INT_MIN case was added because it was a concern during review.

https://gcc.gnu.org/pipermail/gcc/2024-October/244905.html

To make a long story short, there are cases where GCC is handling UB in a predictable / testable way.  Similar should be achievable with the test case in the current PR:

--------------------------------------------------------

The frame size is known at compile time, and the compiler / LRA is accessing a location outside that area with an addressing mode (SP+const or FP+const) that could easily be checked and diagnosed.

Now as a user, I would prefer current LRA behaviour (ice-on-invalid-code) over Reload's (everything seems to be fine).  But on the other hand, ICEs don't look very professional...

> It makes sense never, not on any target, not with LRA nor without.

So ice-on-invalid-code then.

> It is incorrect according to 6.7.6.2/1 already ("If the expression is a constant
> expression, it shall have a value greater than zero.")

Arrays of size zero are GNU-C addition.  But as far as I understand, when a zero-sized array doesn't occur in a structure, or when it is the only element in a struct, then accesses are invalid.

https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
Comment 17 Segher Boessenkool 2024-10-22 17:38:13 UTC
(In reply to Georg-Johann Lay from comment #16)
> (In reply to Segher Boessenkool from comment #15)
> > It makes sense never, not on any target, not with LRA nor without.
> Though there are test cases that are UB and valid as I just
> learned some weeks ago.

But I am talking about *this* case, not about UB in general.

> To make a long story short, there are cases where GCC is handling UB in a
> predictable / testable way.  Similar should be achievable with the test case
> in the current PR:

Hopefully.  But the testcase as written is simply non-sensical.  A better
testcase would be nicer.

> But on the other hand, ICEs don't look very professional...

Oh, I think most ICEs look very serious!  :-)

> > It makes sense never, not on any target, not with LRA nor without.
> 
> So ice-on-invalid-code then.

Yup.

> > It is incorrect according to 6.7.6.2/1 already ("If the expression is a constant
> > expression, it shall have a value greater than zero.")
> 
> Arrays of size zero are GNU-C addition.

Most testcases are also compiled without GNU extensions.

> But as far as I understand, when a
> zero-sized array doesn't occur in a structure, or when it is the only
> element in a struct, then accesses are invalid.
> 
> https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html

You can have zero-length arrays, but you cannot have any accesses to
non-existent objects (like any member of a length zero static array).