The unaligned load and store expanders build the byte address of the
access as a raw (plus ADDR CONST) and drop it straight into a SET,
bypassing insn recognition.  ADDR is frequently virtual-stack-vars, so
the constant is only resolved to the real frame offset in the vregs
pass, and add_operand accepts a CONST_INT only when it satisfies K (a
16-bit signed value, lda) or L (a multiple of 65536, ldah).  A stack
slot more than 64K into a large frame therefore yields an insn no
adddi3 pattern matches, and the first extract_insn on it dies:

  internal compiler error: in extract_insn, at recog.cc:2894

adddi3 is a bodyless expander with add_operand on operand 2, so there
is no fallback that would split an arbitrary constant.  Use
copy_addr_to_reg instead, which routes the address through
force_operand and materialises whatever sequence the constant needs;
alpha_expand_unaligned_store already does this.

This is not specific to the stack protector, but the canary reliably
pushes an otherwise in-range array past the boundary, so enabling
-fstack-protector makes it easy to hit.

gcc/ChangeLog:

        * config/alpha/alpha.cc (alpha_expand_unaligned_load): Use
        copy_addr_to_reg to compute the byte address.
        (alpha_expand_unaligned_store_safe_partial): Likewise.

gcc/testsuite/ChangeLog:

        * gcc.target/alpha/unaligned-big-frame.c: New test.
---
 gcc/config/alpha/alpha.cc                     | 13 ++---
 .../gcc.target/alpha/unaligned-big-frame.c    | 48 +++++++++++++++++++
 2 files changed, 53 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/alpha/unaligned-big-frame.c

diff --git ./gcc/config/alpha/alpha.cc ./gcc/config/alpha/alpha.cc
index 43443f82bac..be5e1776150 100644
--- ./gcc/config/alpha/alpha.cc
+++ ./gcc/config/alpha/alpha.cc
@@ -3509,7 +3509,6 @@ alpha_expand_unaligned_load (rtx tgt, rtx mem, 
HOST_WIDE_INT size,
 
   meml = gen_reg_rtx (DImode);
   memh = gen_reg_rtx (DImode);
-  addr = gen_reg_rtx (DImode);
   extl = gen_reg_rtx (DImode);
   exth = gen_reg_rtx (DImode);
 
@@ -3538,7 +3537,7 @@ alpha_expand_unaligned_load (rtx tgt, rtx mem, 
HOST_WIDE_INT size,
 
   if (sign && size == 2)
     {
-      emit_move_insn (addr, plus_constant (Pmode, mema, ofs+2));
+      addr = copy_addr_to_reg (plus_constant (Pmode, mema, ofs + 2));
 
       emit_insn (gen_extql (extl, meml, addr));
       emit_insn (gen_extqh (exth, memh, addr));
@@ -3552,7 +3551,7 @@ alpha_expand_unaligned_load (rtx tgt, rtx mem, 
HOST_WIDE_INT size,
     }
   else
     {
-      emit_move_insn (addr, plus_constant (Pmode, mema, ofs));
+      addr = copy_addr_to_reg (plus_constant (Pmode, mema, ofs));
       emit_insn (gen_extxl (extl, meml, GEN_INT (size*8), addr));
       switch ((int) size)
        {
@@ -3771,10 +3770,9 @@ alpha_expand_unaligned_store_safe_partial (rtx dst, rtx 
src,
   /* Must handle high before low for degenerate case of aligned.  */
   if (size != 1)
     {
-      rtx addrh = gen_reg_rtx (DImode);
+      rtx addrh = copy_addr_to_reg (plus_constant (Pmode, dsta,
+                                                  ofs + size - 1));
       rtx aligned_addrh = gen_reg_rtx (DImode);
-      emit_insn (gen_rtx_SET (addrh,
-                             plus_constant (DImode, dsta, ofs + size - 1)));
       emit_insn (gen_rtx_SET (aligned_addrh,
                              gen_rtx_AND (DImode, addrh, GEN_INT (-8))));
 
@@ -3882,9 +3880,8 @@ alpha_expand_unaligned_store_safe_partial (rtx dst, rtx 
src,
     }
 
   /* Now handle low.  */
-  rtx addrl = gen_reg_rtx (DImode);
+  rtx addrl = copy_addr_to_reg (plus_constant (Pmode, dsta, ofs));
   rtx aligned_addrl = gen_reg_rtx (DImode);
-  emit_insn (gen_rtx_SET (addrl, plus_constant (DImode, dsta, ofs)));
   emit_insn (gen_rtx_SET (aligned_addrl,
                          gen_rtx_AND (DImode, addrl, GEN_INT (-8))));
 
diff --git ./gcc/testsuite/gcc.target/alpha/unaligned-big-frame.c 
./gcc/testsuite/gcc.target/alpha/unaligned-big-frame.c
new file mode 100644
index 00000000000..757aefe239a
--- /dev/null
+++ ./gcc/testsuite/gcc.target/alpha/unaligned-big-frame.c
@@ -0,0 +1,48 @@
+/* Unaligned accesses to a stack object whose byte address does not fit
+   add_operand used to produce an unrecognizable insn.  A stack protector
+   canary pushes the offsets of a 65529-byte array past the -65536 boundary
+   that constraint L still accepts.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-protector-strong -msafe-partial" } */
+/* { dg-require-effective-target fstack_protector } */
+
+struct __attribute__((packed)) S2 { short x; };
+struct __attribute__((packed)) U2 { unsigned short x; };
+struct __attribute__((packed)) U8 { unsigned long x; };
+
+void g (long);
+void sink (char *);
+
+/* alpha_expand_unaligned_load, sign && size == 2.  */
+void
+load_signed_hi (void)
+{
+  char a[65529];
+  g (((struct S2 *) a)->x);
+}
+
+/* alpha_expand_unaligned_load, general case.  */
+void
+load_unsigned_hi (void)
+{
+  char a[65529];
+  g (((struct U2 *) a)->x);
+}
+
+void
+load_di (void)
+{
+  char a[65529];
+  g (((struct U8 *) a)->x);
+}
+
+/* alpha_expand_unaligned_store_safe_partial, both the high and the low
+   address.  */
+void
+store_di (unsigned long v)
+{
+  char a[65529];
+  ((struct U8 *) a)->x = v;
+  sink (a);
+}
-- 
2.54.0

Reply via email to