]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
PR middle-end/124637: Fix passing padded constant structs in registers on big-endian...
authorRoger Sayle <roger@nextmovesoftware.com>
Fri, 8 May 2026 20:06:54 +0000 (21:06 +0100)
committerRoger Sayle <roger@nextmovesoftware.com>
Fri, 8 May 2026 20:09:22 +0000 (21:09 +0100)
This patch resolves PR middle-end/124637, a wrong code regression when
passing a struct as a register on big-endian targets.  On big-endian
targets, store_constructor fills fields from the most significant bits,
so for structs narrower than word size, any padding is incorrectly
placed in the least significant bytes.  This issue is fixed (on
affected targets) by using a (unsigned) right shift on the value
determined by store_constructor to correctly align the structure in
the least significant bytes, and place the padding in the high bits.

Many thanks to Manjunath Matti for testing this patch on real hardware,
and Drea Pinski for reviewing/approving it.  The new test case may be
a little fragile, but currently "works for me".  Please feel free to
tweak it for powerpc variants/environments I've not consider/encountered.

2026-05-08  Roger Sayle  <roger@nextmovesoftware.com>

gcc/ChangeLog
PR middle-end/124637
* calls.cc (load_register_parameters): If using store_constructor
to place a constant structure in a register, use a right shift to
align the structure/padding if required on big-endian targets.

gcc/testsuite/ChangeLog
PR middle-end/124637
* gcc.target/powerpc/pr124637.c: New test case.

gcc/calls.cc
gcc/testsuite/gcc.target/powerpc/pr124637.c [new file with mode: 0644]

index 4cdc2361a4bf031f4c56c828561c2879ec50b805..d491a414611516e38a0b9d2fd1e1b20bcf4ec9b3 100644 (file)
@@ -2252,10 +2252,20 @@ load_register_parameters (struct arg_data *args, int num_actuals,
                   && !TREE_SIDE_EFFECTS (tree_value)
                   && immediate_const_ctor_p (DECL_INITIAL (tree_value)))
            {
+             HOST_WIDE_INT size = int_expr_size (DECL_INITIAL (tree_value));
              rtx target = gen_reg_rtx (word_mode);
              store_constructor (DECL_INITIAL (tree_value), target, 0,
-                                int_expr_size (DECL_INITIAL (tree_value)),
-                                false);
+                                size, false);
+             /* On big-endian targets, store_constructor places fields
+                from the MSB, which places any padding bits in the least
+                significant bytes.  If required, use a logical right shift
+                to place things where expected in a register parameter.  */
+             if (BYTES_BIG_ENDIAN
+                 && size < UNITS_PER_WORD
+                 && args[i].locate.where_pad == PAD_DOWNWARD)
+               target = expand_shift (RSHIFT_EXPR, word_mode, target,
+                                      (UNITS_PER_WORD - size) * BITS_PER_UNIT,
+                                      NULL_RTX, 1);
              reg = gen_rtx_REG (word_mode, REGNO (reg));
              emit_move_insn (reg, target);
            }
diff --git a/gcc/testsuite/gcc.target/powerpc/pr124637.c b/gcc/testsuite/gcc.target/powerpc/pr124637.c
new file mode 100644 (file)
index 0000000..f38c7fa
--- /dev/null
@@ -0,0 +1,26 @@
+/* { dg-do compile { target { powerpc64-*-* } } } */
+/* { dg-options "-O2 -m64 -mbig-endian" } */
+
+typedef struct {
+    unsigned short a;
+    unsigned short b;
+    unsigned short c;
+} S_t;
+
+static const S_t m1 = { 0x20, 1, 1 };
+
+void ext(S_t r);
+void foo() { ext(m1); }
+
+/* Apologies in advance that these may be a little fragile.  */
+
+/* { dg-final { scan-assembler "lis 3,0x20" } } */
+/* { dg-final { scan-assembler "ori 3,3,0x1" } } */
+/* { dg-final { scan-assembler "sldi 3,3,16" } } */
+/* { dg-final { scan-assembler "ori 3,3,0x1" } } */
+
+/* { dg-final { scan-assembler-not "lis 9,0x20" } } */
+/* { dg-final { scan-assembler-not "ori 9,9,0x1" } } */
+/* { dg-final { scan-assembler-not "lis 3,0x1" } } */
+/* { dg-final { scan-assembler-not "rldimi 3,9,32,0" } } */
+