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