On 6/3/23 12:38, Georg-Johann Lay wrote:
}
note_stores (insn, move2add_note_store, insn);
The point is that in the continue block, the effect of the insn is
recorded even if !success, it's just the computed effect of the code.
Moreover, "next" is REG = REG + CONST_INT, so there are no REG_INC
notes, no?
Also I don't have any testcases that break other than the one
that has a clobber of a GPR along with the pointer addition.
I tried some "smart" solutions before, but all failed for some
reason, so I resorted to something that fixes the bug, and
*only* fixes the bug, and which has clearly no other side
effects than fixing the bug (I have to do all remote on compile
farm). If a more elaborate fix is needed that also catches other
PRs, then I would hand this over to a postreload maintainer please.
Of particular importance for your case would be the note_stores call.
But I could well see other targets needing the search for REG_INC
notes as well as stack pushes.
If I'm right, then wouldn't it be better to factor that blob of code
above into its own function, then use it before the "continue" rather
than implementing a custom can for CLOBBERS?
I cannot answer that. Maybe the authors of the code have some ideas.
Here's what I was thinking. I don't have the bits here to build newlib
or a simulator, so if you could give it a test it'd be appreciated.
I suspect the note_stores call is the important one in here since as you
note we're dealing with simple arithmetic and I wouldn't expect to have
REG_INC notes or SP autoincs in that scenario.
Jeff
diff --git a/gcc/postreload.cc b/gcc/postreload.cc
index 20e138b4fa8..856b7d6e22c 100644
--- a/gcc/postreload.cc
+++ b/gcc/postreload.cc
@@ -1899,6 +1899,79 @@ move2add_use_add3_insn (scalar_int_mode mode, rtx reg, rtx sym, rtx off,
return changed;
}
+/* Perform any invalidations necessary for INSN. */
+
+void
+reload_cse_move2add_invalidate (rtx_insn *insn)
+{
+ for (rtx note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ {
+ if (REG_NOTE_KIND (note) == REG_INC
+ && REG_P (XEXP (note, 0)))
+ {
+ /* Reset the information about this register. */
+ int regno = REGNO (XEXP (note, 0));
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ move2add_record_mode (XEXP (note, 0));
+ reg_mode[regno] = VOIDmode;
+ }
+ }
+ }
+
+ /* There are no REG_INC notes for SP autoinc. */
+ subrtx_var_iterator::array_type array;
+ FOR_EACH_SUBRTX_VAR (iter, array, PATTERN (insn), NONCONST)
+ {
+ rtx mem = *iter;
+ if (mem
+ && MEM_P (mem)
+ && GET_RTX_CLASS (GET_CODE (XEXP (mem, 0))) == RTX_AUTOINC)
+ {
+ if (XEXP (XEXP (mem, 0), 0) == stack_pointer_rtx)
+ reg_mode[STACK_POINTER_REGNUM] = VOIDmode;
+ }
+ }
+
+ note_stores (insn, move2add_note_store, insn);
+
+ /* If INSN is a conditional branch, we try to extract an
+ implicit set out of it. */
+ if (any_condjump_p (insn))
+ {
+ rtx cnd = fis_get_condition (insn);
+
+ if (cnd != NULL_RTX
+ && GET_CODE (cnd) == NE
+ && REG_P (XEXP (cnd, 0))
+ && !reg_set_p (XEXP (cnd, 0), insn)
+ /* The following two checks, which are also in
+ move2add_note_store, are intended to reduce the
+ number of calls to gen_rtx_SET to avoid memory
+ allocation if possible. */
+ && SCALAR_INT_MODE_P (GET_MODE (XEXP (cnd, 0)))
+ && REG_NREGS (XEXP (cnd, 0)) == 1
+ && CONST_INT_P (XEXP (cnd, 1)))
+ {
+ rtx implicit_set = gen_rtx_SET (XEXP (cnd, 0), XEXP (cnd, 1));
+ move2add_note_store (SET_DEST (implicit_set), implicit_set, insn);
+ }
+ }
+
+ /* If this is a CALL_INSN, all call used registers are stored with
+ unknown values. */
+ if (CALL_P (insn))
+ {
+ function_abi callee_abi = insn_callee_abi (insn);
+ for (int i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+ if (reg_mode[i] != VOIDmode
+ && reg_mode[i] != BLKmode
+ && callee_abi.clobbers_reg_p (reg_mode[i], i))
+ /* Reset the information about this register. */
+ reg_mode[i] = VOIDmode;
+ }
+}
+
/* Convert move insns with constant inputs to additions if they are cheaper.
Return true if any changes were made. */
static bool
@@ -1921,7 +1994,7 @@ reload_cse_move2add (rtx_insn *first)
move2add_luid = 2;
for (insn = first; insn; insn = NEXT_INSN (insn), move2add_luid++)
{
- rtx set, note;
+ rtx set;
if (LABEL_P (insn))
{
@@ -2041,6 +2114,12 @@ reload_cse_move2add (rtx_insn *first)
delete_insn (insn);
changed |= success;
insn = next;
+ /* Make sure to perform any invalidations related to
+ NEXT/INSN since we're going to bypass the normal
+ flow with the continue below.
+
+ Do this before recording the new mode/offset. */
+ reload_cse_move2add_invalidate (insn);
move2add_record_mode (reg);
reg_offset[regno]
= trunc_int_for_mode (added_offset + base_offset,
@@ -2094,74 +2173,7 @@ reload_cse_move2add (rtx_insn *first)
continue;
}
}
-
- for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
- {
- if (REG_NOTE_KIND (note) == REG_INC
- && REG_P (XEXP (note, 0)))
- {
- /* Reset the information about this register. */
- int regno = REGNO (XEXP (note, 0));
- if (regno < FIRST_PSEUDO_REGISTER)
- {
- move2add_record_mode (XEXP (note, 0));
- reg_mode[regno] = VOIDmode;
- }
- }
- }
-
- /* There are no REG_INC notes for SP autoinc. */
- subrtx_var_iterator::array_type array;
- FOR_EACH_SUBRTX_VAR (iter, array, PATTERN (insn), NONCONST)
- {
- rtx mem = *iter;
- if (mem
- && MEM_P (mem)
- && GET_RTX_CLASS (GET_CODE (XEXP (mem, 0))) == RTX_AUTOINC)
- {
- if (XEXP (XEXP (mem, 0), 0) == stack_pointer_rtx)
- reg_mode[STACK_POINTER_REGNUM] = VOIDmode;
- }
- }
-
- note_stores (insn, move2add_note_store, insn);
-
- /* If INSN is a conditional branch, we try to extract an
- implicit set out of it. */
- if (any_condjump_p (insn))
- {
- rtx cnd = fis_get_condition (insn);
-
- if (cnd != NULL_RTX
- && GET_CODE (cnd) == NE
- && REG_P (XEXP (cnd, 0))
- && !reg_set_p (XEXP (cnd, 0), insn)
- /* The following two checks, which are also in
- move2add_note_store, are intended to reduce the
- number of calls to gen_rtx_SET to avoid memory
- allocation if possible. */
- && SCALAR_INT_MODE_P (GET_MODE (XEXP (cnd, 0)))
- && REG_NREGS (XEXP (cnd, 0)) == 1
- && CONST_INT_P (XEXP (cnd, 1)))
- {
- rtx implicit_set =
- gen_rtx_SET (XEXP (cnd, 0), XEXP (cnd, 1));
- move2add_note_store (SET_DEST (implicit_set), implicit_set, insn);
- }
- }
-
- /* If this is a CALL_INSN, all call used registers are stored with
- unknown values. */
- if (CALL_P (insn))
- {
- function_abi callee_abi = insn_callee_abi (insn);
- for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
- if (reg_mode[i] != VOIDmode
- && reg_mode[i] != BLKmode
- && callee_abi.clobbers_reg_p (reg_mode[i], i))
- /* Reset the information about this register. */
- reg_mode[i] = VOIDmode;
- }
+ reload_cse_move2add_invalidate (insn);
}
return changed;
}
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr101188.c b/gcc/testsuite/gcc.c-torture/execute/pr101188.c
new file mode 100644
index 00000000000..460149ada49
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr101188.c
@@ -0,0 +1,60 @@
+typedef __UINT8_TYPE__ uint8_t;
+typedef __UINT16_TYPE__ uint16_t;
+
+typedef uint8_t (*fn1)(void *a);
+typedef void (*fn2)(void *a, int *arg);
+
+struct S
+{
+ uint8_t buffer[64];
+ uint16_t n;
+ fn2 f2;
+ void *a;
+ fn1 f1;
+};
+
+volatile uint16_t x;
+
+void __attribute__((__noinline__,__noclone__))
+foo (uint16_t n)
+{
+ x = n;
+}
+
+void __attribute__((__noinline__,__noclone__))
+testfn (struct S *self)
+{
+ int arg;
+
+ foo (self->n);
+ self->n++;
+ self->f2 (self->a, &arg);
+ self->buffer[0] = self->f1 (self->a);
+}
+
+static unsigned char myfn2_called = 0;
+
+static void
+myfn2 (void *a, int *arg)
+{
+ myfn2_called = 1;
+}
+
+static uint8_t
+myfn1 (void *a)
+{
+ return 0;
+}
+
+int main (void)
+{
+ struct S s;
+ s.n = 0;
+ s.f2 = myfn2;
+ s.f1 = myfn1;
+ testfn (&s);
+ if (myfn2_called != 1)
+ __builtin_abort();
+ return 0;
+}
+