[RFC, PATCH v2] Expand PARALLEL for function return and parameter passing

"H.J. Lu" <[email protected]> Tue, 4 Aug 2026 13:56:14 +0800
Newsgroups gmane.comp.gcc.patches
Message-ID <CAMe9rOro2K+rWnmxVqXxw8vDGa2SLE=uOLhJEFvNYtoui1MEDQ@mail.gmail.com>
On Wed, Jul 29, 2026 at 4:57 PM Richard Biener
<[email protected]> wrote:
>
> On Wed, Jul 29, 2026 at 10:28 AM H.J. Lu <[email protected]> wrote:
> >
> > On some targets, TImode structs are placed in TImode PARALLEL for function
> > return and parameter passing:
> >
> > (parallel:TI [
> >         (expr_list:REG_DEP_TRUE (reg:DI 20 xmm0 [ arg ])
> >             (const_int 0 [0]))
> >         (expr_list:REG_DEP_TRUE (reg:DI 21 xmm1 [ arg+8 ])
> >             (const_int 8 [0x8]))
> >     ])
> >
> > (parallel:TI [
> >         (expr_list:REG_DEP_TRUE (reg:SI 5 di [ arg ])
> >             (const_int 0 [0]))
> >         (expr_list:REG_DEP_TRUE (reg:DI 4 si [ arg+8 ])
> >             (const_int 8 [0x8]))
> >     ])
> >
> > (parallel:BLK [
> >         (expr_list:REG_DEP_TRUE (reg:TI 102 [ <retval> ])
> >             (const_int 0 [0]))
> >     ])
> >
> > We generate very bad code sequences for moving between such PARALLELs
> > and register or memory since the backend doesn't see the whole picture.
> > Add parallel_mov optab to move between PARALLEL and register or memory.
> > Implement parallel_mov patterns for TImode in x86 backend.
>
> Why is what you do in the x86 implementation in any way special to x86?
> Can this not be done directly in the middle-end and for all targets?
>

Here is the v2 patch to add expand_parallel_load and expand_parallel_store.
It doesn't support big-endian.  Do any big-endian targets use TImode PARALLEL
for function return and parameter passing?


-- 
H.J.
---
On some targets, TImode structs are placed in TImode PARALLEL for function
return and parameter passing:

(parallel:TI [
        (expr_list:REG_DEP_TRUE (reg:DI 20 xmm0 [ arg ])
            (const_int 0 [0]))
        (expr_list:REG_DEP_TRUE (reg:DI 21 xmm1 [ arg+8 ])
            (const_int 8 [0x8]))
    ])

(parallel:TI [
        (expr_list:REG_DEP_TRUE (reg:SI 5 di [ arg ])
            (const_int 0 [0]))
        (expr_list:REG_DEP_TRUE (reg:DI 4 si [ arg+8 ])
            (const_int 8 [0x8]))
    ])

(parallel:BLK [
        (expr_list:REG_DEP_TRUE (reg:TI 102 [ <retval> ])
            (const_int 0 [0]))
    ])

We generate very bad code sequences for moving between such PARALLELs
and register or memory since the backend doesn't see the whole picture.
Add expand_parallel_load to expand PARALLEL load for function return
and add expand_parallel_store to expand PARALLEL store for function
parameter passing.

gcc/

PR target/114134
PR target/120588
PR target/126403
PR target/126424
* expr.cc (expand_parallel_load): New.
(expand_parallel_store): Likewise.
(emit_group_load): Call expand_parallel_load for PARALLEL
destination in BLKmode.
(emit_group_store): Call expand_parallel_store for PARALLEL
source.
* config/i386/sse.md (*vec_extractv2di_1 splitter): New.

gcc/testsuite/

PR target/114134
PR target/120588
PR target/126403
PR target/126424
* g++.target/i386/pr126424-1.C: New test.
* gcc.target/i386/pr114134-1.c: Likewise.
* gcc.target/i386/pr114134-2.c: Likewise.
* gcc.target/i386/pr114134-3.c: Likewise.
* gcc.target/i386/pr120588-1.c: Likewise.
* gcc.target/i386/pr120588-2.c: Likewise.
* gcc.target/i386/pr120588-3.c: Likewise.
* gcc.target/i386/pr120588-4.c: Likewise.
* gcc.target/i386/pr120588-5.c: Likewise.
* gcc.target/i386/pr120588-6.c: Likewise.
* gcc.target/i386/pr120588-7.c: Likewise.
* gcc.target/i386/pr120588-8.c: Likewise.
* gcc.target/i386/pr120588-9.c: Likewise.
* gcc.target/i386/pr120588-10.c: Likewise.
* gcc.target/i386/pr120588-11.c: Likewise.
* gcc.target/i386/pr120588-12.c: Likewise.
* gcc.target/i386/pr120588-13.c: Likewise.
* gcc.target/i386/pr120588-14.c: Likewise.
* gcc.target/i386/pr120588-15.c: Likewise.
* gcc.target/i386/pr120588-16.c: Likewise.
* gcc.target/i386/pr120588-17.c: Likewise.
* gcc.target/i386/pr120588-18.c: Likewise.
* gcc.target/i386/pr120588-19.c: Likewise.
* gcc.target/i386/pr120588-20.c: Likewise.
* gcc.target/i386/pr126403-1.c: Likewise.
* gcc.target/i386/pr126403-2.c: Likewise.
* gcc.target/i386/pr126403-3.c: Likewise.
* gcc.target/i386/pr126403-4.c: Likewise.
* gcc.target/i386/zext-sse-2.c: Updated.
v2-0001-Expand-PARALLEL-for-function-return-and-parameter.patch (text/x-patch, 44.3 KB)
From 2070c8a2c6eed53262af696714f8c8a47ecaa364 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <[email protected]>
Date: Sun, 12 Jul 2026 07:59:57 +0800
Subject: [PATCH v2] Expand PARALLEL for function return and parameter passing

On some targets, TImode structs are placed in TImode PARALLEL for function
return and parameter passing:

(parallel:TI [
        (expr_list:REG_DEP_TRUE (reg:DI 20 xmm0 [ arg ])
            (const_int 0 [0]))
        (expr_list:REG_DEP_TRUE (reg:DI 21 xmm1 [ arg+8 ])
            (const_int 8 [0x8]))
    ])

(parallel:TI [
        (expr_list:REG_DEP_TRUE (reg:SI 5 di [ arg ])
            (const_int 0 [0]))
        (expr_list:REG_DEP_TRUE (reg:DI 4 si [ arg+8 ])
            (const_int 8 [0x8]))
    ])

(parallel:BLK [
        (expr_list:REG_DEP_TRUE (reg:TI 102 [ <retval> ])
            (const_int 0 [0]))
    ])

We generate very bad code sequences for moving between such PARALLELs
and register or memory since the backend doesn't see the whole picture.
Add expand_parallel_load to expand PARALLEL load for function return
and add expand_parallel_store to expand PARALLEL store for function
parameter passing.

gcc/

	PR target/114134
	PR target/120588
	PR target/126403
	PR target/126424
	* expr.cc (expand_parallel_load): New.
	(expand_parallel_store): Likewise.
	(emit_group_load): Call expand_parallel_load for PARALLEL
	destination in BLKmode.
	(emit_group_store): Call expand_parallel_store for PARALLEL
	source.
	* config/i386/sse.md (*vec_extractv2di_1 splitter): New.

gcc/testsuite/

	PR target/114134
	PR target/120588
	PR target/126403
	PR target/126424
	* g++.target/i386/pr126424-1.C: New test.
	* gcc.target/i386/pr114134-1.c: Likewise.
	* gcc.target/i386/pr114134-2.c: Likewise.
	* gcc.target/i386/pr114134-3.c: Likewise.
	* gcc.target/i386/pr120588-1.c: Likewise.
	* gcc.target/i386/pr120588-2.c: Likewise.
	* gcc.target/i386/pr120588-3.c: Likewise.
	* gcc.target/i386/pr120588-4.c: Likewise.
	* gcc.target/i386/pr120588-5.c: Likewise.
	* gcc.target/i386/pr120588-6.c: Likewise.
	* gcc.target/i386/pr120588-7.c: Likewise.
	* gcc.target/i386/pr120588-8.c: Likewise.
	* gcc.target/i386/pr120588-9.c: Likewise.
	* gcc.target/i386/pr120588-10.c: Likewise.
	* gcc.target/i386/pr120588-11.c: Likewise.
	* gcc.target/i386/pr120588-12.c: Likewise.
	* gcc.target/i386/pr120588-13.c: Likewise.
	* gcc.target/i386/pr120588-14.c: Likewise.
	* gcc.target/i386/pr120588-15.c: Likewise.
	* gcc.target/i386/pr120588-16.c: Likewise.
	* gcc.target/i386/pr120588-17.c: Likewise.
	* gcc.target/i386/pr120588-18.c: Likewise.
	* gcc.target/i386/pr120588-19.c: Likewise.
	* gcc.target/i386/pr120588-20.c: Likewise.
	* gcc.target/i386/pr126403-1.c: Likewise.
	* gcc.target/i386/pr126403-2.c: Likewise.
	* gcc.target/i386/pr126403-3.c: Likewise.
	* gcc.target/i386/pr126403-4.c: Likewise.
	* gcc.target/i386/zext-sse-2.c: Updated.

Signed-off-by: H.J. Lu <[email protected]>
---
 gcc/config/i386/sse.md                      |  15 +
 gcc/expr.cc                                 | 458 ++++++++++++++++++++
 gcc/testsuite/g++.target/i386/pr126424-1.C  |  80 ++++
 gcc/testsuite/gcc.target/i386/pr114134-1.c  |  27 ++
 gcc/testsuite/gcc.target/i386/pr114134-2.c  |  22 +
 gcc/testsuite/gcc.target/i386/pr114134-3.c  |  22 +
 gcc/testsuite/gcc.target/i386/pr120588-1.c  |  26 ++
 gcc/testsuite/gcc.target/i386/pr120588-10.c |  26 ++
 gcc/testsuite/gcc.target/i386/pr120588-11.c |  26 ++
 gcc/testsuite/gcc.target/i386/pr120588-12.c |  36 ++
 gcc/testsuite/gcc.target/i386/pr120588-13.c |  28 ++
 gcc/testsuite/gcc.target/i386/pr120588-14.c |  28 ++
 gcc/testsuite/gcc.target/i386/pr120588-15.c |  27 ++
 gcc/testsuite/gcc.target/i386/pr120588-16.c |  27 ++
 gcc/testsuite/gcc.target/i386/pr120588-17.c |  27 ++
 gcc/testsuite/gcc.target/i386/pr120588-18.c |  35 ++
 gcc/testsuite/gcc.target/i386/pr120588-19.c |  31 ++
 gcc/testsuite/gcc.target/i386/pr120588-2.c  |  26 ++
 gcc/testsuite/gcc.target/i386/pr120588-20.c |  25 ++
 gcc/testsuite/gcc.target/i386/pr120588-3.c  |  27 ++
 gcc/testsuite/gcc.target/i386/pr120588-4.c  |  29 ++
 gcc/testsuite/gcc.target/i386/pr120588-5.c  |  27 ++
 gcc/testsuite/gcc.target/i386/pr120588-6.c  |  26 ++
 gcc/testsuite/gcc.target/i386/pr120588-7.c  |  26 ++
 gcc/testsuite/gcc.target/i386/pr120588-8.c  |  26 ++
 gcc/testsuite/gcc.target/i386/pr120588-9.c  |  26 ++
 gcc/testsuite/gcc.target/i386/pr126403-1.c  |  31 ++
 gcc/testsuite/gcc.target/i386/pr126403-2.c  |  31 ++
 gcc/testsuite/gcc.target/i386/pr126403-3.c  |  31 ++
 gcc/testsuite/gcc.target/i386/pr126403-4.c  |  33 ++
 gcc/testsuite/gcc.target/i386/zext-sse-2.c  |  10 +-
 31 files changed, 1308 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/g++.target/i386/pr126424-1.C
 create mode 100644 gcc/testsuite/gcc.target/i386/pr114134-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr114134-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr114134-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-10.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-11.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-12.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-13.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-14.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-15.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-16.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-17.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-18.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-19.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-20.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-5.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-6.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-7.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-8.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120588-9.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr126403-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr126403-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr126403-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr126403-4.c

diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 7d71f8ba546..f13538a9dc2 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -22340,6 +22340,21 @@ (define_split
     }
 })
 
+;; Turn the second DImode extraction from V4SF and V2DF modes into
+;; vec_extract*.
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+        (subreg:DI (match_operand:VFH_128 1 "register_operand") 8))]
+  "can_create_pseudo_p ()
+   && TARGET_SSE
+   && TARGET_64BIT"
+  [(set (match_dup 0)
+        (vec_select:DI (match_dup 1)
+                       (parallel [(const_int 1)])))]
+{
+  operands[1] = gen_lowpart (V2DImode, operands[1]);
+})
+
 (define_insn "*vec_concatv2si_sse4_1"
   [(set (match_operand:V2SI 0 "register_operand"
 	  "=Yr,*x, x, v,Yr,*x, v, v, *y,*y")
diff --git a/gcc/expr.cc b/gcc/expr.cc
index 210a7bc0888..8cf9d6de369 100644
--- a/gcc/expr.cc
+++ b/gcc/expr.cc
@@ -2929,6 +2929,454 @@ move_block_from_reg (int regno, rtx x, int nregs)
     }
 }
 
+/* Expand TImode PARALLEL load for function return.  */
+
+static bool
+expand_parallel_load (rtx op0, rtx op1)
+{
+  /* Skip the naked function to avoid trashing callee-saved registers
+     since they won't be preserved without prologue nor epilogue.  */
+  if (lookup_attribute ("naked",
+			DECL_ATTRIBUTES (current_function_decl)))
+    return false;
+
+  /* FIXME: How to support big-endian?  */
+  if (BITS_BIG_ENDIAN || BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
+    return false;
+
+  if (!MEM_P (op1) || word_mode != E_DImode)
+    return false;
+
+  /* Load a memory operand, like
+
+     (mem/c:BLK (plus:DI (reg/f:DI 93 virtual-stack-vars)
+       (const_int -16 [0xfffffffffffffff0])) [1 D.2978+0 S12 A128])
+
+     into function return
+
+     (parallel:BLK [
+       (expr_list:REG_DEP_TRUE (reg:TI 98 [ <retval> ])
+		(const_int 0 [0]))])
+
+   */
+  if (GET_MODE (op1) != BLKmode
+      || !MEM_SIZE_KNOWN_P (op1)
+      || GET_CODE (op0) != PARALLEL
+      || XVECLEN (op0, 0) != 1)
+    return false;
+
+  rtx dest = XVECEXP (op0, 0, 0);
+  if (GET_CODE (dest) != EXPR_LIST)
+    return false;
+
+  dest = XEXP (dest, 0);
+  if (!REG_P (dest) || GET_MODE (dest) != TImode)
+    return false;
+
+  /* Skip if this isn't function return.  */
+  tree reg_expr = REG_EXPR (dest);
+  if (!reg_expr || TREE_CODE (reg_expr) != RESULT_DECL)
+    return false;
+
+  /* We can optimize a 12-byte load with an 8-byte DImode load and
+     a 4-byte SImode load, which zero-extends to 8 bytes, to fill a
+     TImode register.  9/10/11/13/14/15 byte loads require more than
+     2 loads to fill a TImode register.  FIXME: How can we improve
+     9/10/11/13/14/15 byte loads?  */
+  if (!MEM_SIZE_KNOWN_P (op1))
+    return false;
+
+  poly_int64 mem_size = MEM_SIZE (op1);
+  if (!known_eq (mem_size, 12))
+    return false;
+
+  rtx low = simplify_gen_subreg (DImode, dest, TImode, 0);
+  if (!low)
+    return false;
+  rtx high = simplify_gen_subreg (SImode, dest, TImode, 8);
+  if (!high)
+    return false;
+
+  rtx_insn *insn, *seq;
+
+  start_sequence ();
+
+  /* First load the first 8-byte memory into the register with a
+     paradoxical subreg.  */
+  rtx mem = adjust_address (op1, DImode, 0);
+  rtx src = gen_reg_rtx (DImode);
+  insn = emit_move_insn (src, mem);
+  if (recog (PATTERN (insn), insn, nullptr) < 0)
+    {
+      end_sequence ();
+      return false;
+    }
+  insn = emit_move_insn (low, src);
+  if (recog (PATTERN (insn), insn, nullptr) < 0)
+    {
+      end_sequence ();
+      return false;
+    }
+
+  /* Load the remaining 4-byte memory into the subreg.  */
+  mem = adjust_address (op1, SImode, 8);
+  src = gen_reg_rtx (SImode);
+  insn = emit_move_insn (src, mem);
+  if (recog (PATTERN (insn), insn, nullptr) < 0)
+    {
+      end_sequence ();
+      return false;
+    }
+
+  insn = emit_move_insn (high, src);
+  if (recog (PATTERN (insn), insn, nullptr) < 0)
+    {
+      end_sequence ();
+      return false;
+    }
+
+  seq = end_sequence ();
+  emit_insn (seq);
+  return true;
+}
+
+/* Expand TImode PARALLEL store for function parameter passing.  */
+
+static bool
+expand_parallel_store (rtx op0, rtx op1)
+{
+  /* Skip the naked function to avoid trashing callee-saved registers
+     since they won't be preserved without prologue nor epilogue.  */
+  if (lookup_attribute ("naked",
+			DECL_ATTRIBUTES (current_function_decl)))
+    return false;
+
+  /* FIXME: How to support big-endian?  */
+  if (BITS_BIG_ENDIAN || BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
+    return false;
+
+  if (word_mode != E_DImode)
+    return false;
+
+  /* Move a TImode function parameter in 2 hard registers:
+
+     (parallel:TI [
+	(expr_list:REG_DEP_TRUE (reg:SI 5 di [ x ])
+		(const_int 0 [0]))
+	(expr_list:REG_DEP_TRUE (reg:DI 4 si [ x+8 ])
+		(const_int 8 [0x8]))])
+
+     or
+
+     (parallel:TI [
+	(expr_list:REG_DEP_TRUE (reg:DI 20 xmm0 [ a ])
+		(const_int 0 [0]))
+	(expr_list:REG_DEP_TRUE (reg:DI 21 xmm1 [ a+8 ])
+		(const_int 8 [0x8]))
+
+     into a TImode register.  */
+  if (!REG_P (op0) || GET_MODE (op0) != TImode)
+    return false;
+
+  if (GET_CODE (op1) != PARALLEL || XVECLEN (op1, 0) != 2)
+    return false;
+
+  rtx x0 = XVECEXP (op1, 0, 0);
+  rtx x1 = XVECEXP (op1, 0, 1);
+  if (GET_CODE (x0) != EXPR_LIST || GET_CODE (x1) != EXPR_LIST)
+    return false;
+
+  rtx reg0 = XEXP (x0, 0);
+  rtx reg1 = XEXP (x1, 0);
+  if (!reg0
+      || !REG_P (reg0)
+      || !HARD_REGISTER_P (reg0)
+      || !reg1
+      || !REG_P (reg1)
+      || !HARD_REGISTER_P (reg1))
+    return false;
+
+  /* Skip
+
+     (parallel:TI [
+	(expr_list:REG_DEP_TRUE (reg:DI 5 di [ b ])
+		(const_int 0 [0]))
+	(expr_list:REG_DEP_TRUE (reg:SF 20 xmm0 [ b+8 ])
+		(const_int 8 [0x8]))])
+
+   */
+  if (GET_MODE_CLASS (GET_MODE (reg0))
+      != GET_MODE_CLASS (GET_MODE(reg1)))
+    return false;
+
+  /* Skip if OP1 isn't a function parameter.  */
+  tree reg_expr = REG_EXPR (reg0);
+  if (!reg_expr || TREE_CODE (reg_expr) != PARM_DECL)
+    return false;
+
+  /* Verify that REG0 and REG1 come from the same function parameter.  */
+  if (reg_expr != REG_EXPR (reg1))
+    return false;
+
+  /* Return the first field.  */
+  auto first_field = [] (tree field)
+    {
+      for (; field; field = DECL_CHAIN (field))
+	if (TREE_CODE (field) == FIELD_DECL
+	    && TREE_TYPE (field) != error_mark_node)
+	  break;
+      return field;
+    };
+
+  tree type = TREE_TYPE (reg_expr);
+  if (!RECORD_OR_UNION_TYPE_P (type))
+    return false;
+
+  rtx off0 = XEXP (x0, 1);
+  rtx off1 = XEXP (x1, 1);
+  if (!CONST_INT_P (off0)
+      || UINTVAL (off0) != 0
+      || !CONST_INT_P (off1)
+      || UINTVAL (off1) != 8)
+    return false;
+
+  tree field, field_type = type;
+
+  /* Look for the inner-most record or union.  The loop isn't unbounded
+     since both DECL_CHAIN traversal and nesting depth are finite.  */
+  do
+    {
+      field = first_field (TYPE_FIELDS (field_type));
+      /* Skip empty record or union.  */
+      if (!field)
+	return false;
+      field_type = TREE_TYPE (field);
+
+      /* Stop if there is another field.  */
+      if (first_field (DECL_CHAIN (field)))
+	break;
+    }
+  while (RECORD_OR_UNION_TYPE_P (field_type));
+
+  rtx_insn *insn, *seq;
+
+  machine_mode mode0, mode1;
+  if (TREE_CODE (field_type) == ARRAY_TYPE)
+    {
+      /* Skip if the record has more than 1 field.  */
+      if (first_field (DECL_CHAIN (field)))
+	return false;
+
+      /* Support
+
+	 (parallel:TI [
+	   (expr_list:REG_DEP_TRUE (reg:DI 20 xmm0 [ a ])
+		(const_int 0 [0]))
+	   (expr_list:REG_DEP_TRUE (reg:DI 21 xmm1 [ a+8 ])
+		(const_int 8 [0x8]))
+
+	 where A is a floating point array.  */
+      tree element_type = TREE_TYPE (field_type);
+      /* Set MODE1 to the scalar element mode.  */
+      mode1 = TYPE_MODE (element_type);
+      /* Skip if this isn't a floating point array.  */
+      if (GET_MODE_CLASS (mode1) != MODE_FLOAT)
+	return false;
+
+      /* Skip if the array size isn't 16 bytes.  */
+      poly_uint64 array_size;
+      unsigned HOST_WIDE_INT size;
+      if (!poly_int_tree_p (TYPE_SIZE_UNIT (field_type),
+			    &array_size)
+	  || !array_size.is_constant (&size)
+	  || size != 16)
+	return false;
+
+      unsigned int s_size = GET_MODE_SIZE (mode1).to_constant ();
+      unsigned int nunits = 16 / s_size;
+      scalar_mode smode = as_a <scalar_mode> (mode1);
+
+      /* Set MODE0 to the full vector mode and MODE1 to the half-width
+	 vector mode if there are more than 2 elements per vector.  */
+      if (!mode_for_vector (smode, nunits).exists (&mode0)
+	  || !targetm.vector_mode_supported_p (mode0)
+	  || (nunits > 2
+	      && (!mode_for_vector (smode, nunits / 2).exists (&mode1)
+		  || !targetm.vector_mode_supported_p (mode1))))
+	return false;
+
+      start_sequence ();
+
+      /* Move the first register to the 8-byte mode.  */
+      rtx src0 = gen_reg_rtx (mode1);
+      reg0 = gen_lowpart (mode1, reg0);
+      insn = emit_move_insn (src0, reg0);
+      if (recog (PATTERN (insn), insn, nullptr) < 0)
+	{
+	  end_sequence ();
+	  return false;
+	}
+
+      /* Move the second register to the 8-byte mode.  */
+      rtx src1 = gen_reg_rtx (mode1);
+      reg1 = gen_lowpart (mode1, reg1);
+      insn = emit_move_insn (src1, reg1);
+      if (recog (PATTERN (insn), insn, nullptr) < 0)
+	{
+	  end_sequence ();
+	  return false;
+	}
+
+      /* Concatenate 2 8-byte registers into OP0.  */
+      rtx op = gen_rtx_VEC_CONCAT (mode0, src0, src1);
+      rtx dest = gen_reg_rtx (mode0);
+      insn = emit_move_insn (dest, op);
+      if (recog (PATTERN (insn), insn, nullptr) < 0)
+	{
+	  end_sequence ();
+	  return false;
+	}
+
+      op = gen_lowpart (TImode, dest);
+      insn = emit_move_insn (op0, op);
+      if (recog (PATTERN (insn), insn, nullptr) < 0)
+	{
+	  end_sequence ();
+	  return false;
+	}
+
+      seq = end_sequence ();
+      emit_insn (seq);
+    }
+  else
+    {
+      if (TREE_CODE (DECL_FIELD_CONTEXT (field)) == RECORD_TYPE)
+	{
+	  /* Check modes on 2 fields to see if we can use SImode move,
+	     instead of DImode move.  */
+
+	  mode0 = DECL_MODE (field);
+	  /* C++ has VOIDmode field.  */
+	  if (GET_MODE_CLASS (mode0) != MODE_INT)
+	    return false;
+
+	  field = first_field (DECL_CHAIN (field));
+	  /* Skip if the record has only one field.  */
+	  if (!field)
+	    return false;
+
+	  mode1 = DECL_MODE (field);
+	  if (GET_MODE_CLASS (mode1) != MODE_INT)
+	    return false;
+
+	  /* Skip if the record have more than 2 fields.  */
+	  field = first_field (DECL_CHAIN (field));
+	  if (field)
+	    return false;
+
+	  /* Skip struct with 2 DImode fields:
+
+	     struct X
+	     {
+	       long long f1;
+	       long long f2;
+	     };
+
+	     with
+
+	     (parallel:TI [
+	       (expr_list:REG_DEP_TRUE (reg:DI 5 di [ x ])
+		(const_int 0 [0]))
+	       (expr_list:REG_DEP_TRUE (reg:DI 4 si [ x+8 ])
+		(const_int 8 [0x8]))])
+
+	   */
+	  if (mode0 == DImode && mode1 == DImode)
+	    return false;
+
+	  /* For struct fields with aligned attribute:
+
+	     struct S
+	     {
+	       char a __attribute__ ((aligned(4)));
+	       char b __attribute__ ((aligned(8)));
+	     };
+
+	     if the field mode is narrower than SImode, use SImode to
+	     avoid QImode and HImode register shuffle.  */
+	  if (mode0 == QImode || mode0 == HImode)
+	    mode0 = SImode;
+	  if (mode1 == QImode || mode1 == HImode)
+	    mode1 = SImode;
+	}
+      else
+	{
+	  /* The two eightbytes are fixed at offsets 0 and 8.  Use
+	     DImode for bit copy, the actual union member types don't
+	     matter.  */
+	  mode0 = DImode;
+	  mode1 = DImode;
+	}
+
+      rtx low = simplify_gen_subreg (mode0, op0, TImode, 0);
+      if (!low)
+	return false;
+      rtx high = simplify_gen_subreg (mode1, op0, TImode, 8);
+      if (!high)
+	return false;
+
+      start_sequence ();
+
+      /* Move a TImode PARALLEL into a register.  Use pseudo registers
+	 to avoid moving a hard register to a subreg.  First move the
+	 first register into the first word in the register with a
+	 paradoxical subreg.  */
+      rtx src = gen_reg_rtx (mode0);
+      reg0 = gen_lowpart (mode0, reg0);
+      insn = emit_move_insn (src, reg0);
+      if (recog (PATTERN (insn), insn, nullptr) < 0)
+	{
+	  end_sequence ();
+	  return false;
+	}
+
+      insn = emit_move_insn (low, src);
+      if (recog (PATTERN (insn), insn, nullptr) < 0)
+	{
+	  end_sequence ();
+	  return false;
+	}
+
+      /* Move the second register into the subreg.  */
+      src = gen_reg_rtx (mode1);
+      reg1 = gen_lowpart (mode1, reg1);
+      insn = emit_move_insn (src, reg1);
+      if (recog (PATTERN (insn), insn, nullptr) < 0)
+	{
+	  end_sequence ();
+	  return false;
+	}
+
+      /* NB: Use gen_rtx_SET and emit_insn to generate
+
+	 (set (subreg:DI (reg:TI 102) 8) (reg:DI 104))
+
+	 for parameter load.  */
+      rtx set = gen_rtx_SET (high, src);
+      insn = emit_insn (set);
+      if (recog (PATTERN (insn), insn, nullptr) < 0)
+	{
+	  end_sequence ();
+	  return false;
+	}
+
+      seq = end_sequence ();
+      emit_insn (seq);
+    }
+
+  return true;
+}
+
 /* Generate a PARALLEL rtx for a new non-consecutive group of registers from
    ORIG, where ORIG is a non-consecutive group of registers represented by
    a PARALLEL.  The clone is identical to the original except in that the
@@ -3164,6 +3612,12 @@ emit_group_load (rtx dst, rtx src, tree type, poly_int64 ssize)
   rtx *tmps;
   int i;
 
+  if (GET_CODE (dst) == PARALLEL
+      && GET_MODE (dst) == BLKmode
+      && GET_MODE (src) == BLKmode
+      && expand_parallel_load (dst, src))
+    return;
+
   tmps = XALLOCAVEC (rtx, XVECLEN (dst, 0));
   emit_group_load_1 (tmps, dst, src, type, ssize);
 
@@ -3281,6 +3735,10 @@ emit_group_store (rtx orig_dst, rtx src, tree type ATTRIBUTE_UNUSED,
       return;
     }
 
+  if (GET_CODE (src) == PARALLEL
+      && expand_parallel_store (orig_dst, src))
+    return;
+
   /* Check for a NULL entry, used to indicate that the parameter goes
      both on the stack and in registers.  */
   if (XEXP (XVECEXP (src, 0, 0), 0))
diff --git a/gcc/testsuite/g++.target/i386/pr126424-1.C b/gcc/testsuite/g++.target/i386/pr126424-1.C
new file mode 100644
index 00000000000..ad813524fde
--- /dev/null
+++ b/gcc/testsuite/g++.target/i386/pr126424-1.C
@@ -0,0 +1,80 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -std=c++20" } */
+
+template <typename> constexpr bool is_lvalue_reference_v = true;
+template <typename _Tp, typename>
+concept __weakly_eq_cmp_with = requires (_Tp __t) { __t; };
+struct less
+{
+};
+template <typename _Sent, typename _Iter>
+concept sentinel_for = __weakly_eq_cmp_with<_Sent, _Iter>;
+template <typename _Iter>
+concept random_access_iterator = requires (_Iter __n) { __n; };
+struct __normal_iterator
+{
+  friend void operator- (__normal_iterator, __normal_iterator &);
+};
+template <typename _Tp>
+concept __maybe_borrowed_range = is_lvalue_reference_v<_Tp>;
+struct
+{
+  template <__maybe_borrowed_range _Tp>
+  auto
+  operator() (_Tp __t)
+  {
+    return __t.end ();
+  }
+} begin, end;
+template <typename _Tp>
+concept random_access_range = random_access_iterator<_Tp>;
+struct foo1
+{
+  __normal_iterator end ();
+} links;
+struct time_zone_link
+{
+  void name ();
+};
+template <typename _Comp, typename _Proj> struct _Comp_proj
+{
+  [[no_unique_address]] _Comp _M_comp;
+  _Proj _M_proj;
+};
+template <typename _Comp, typename _Proj>
+_Comp_proj<_Comp, _Proj> __make_comp_proj (_Comp, _Proj);
+template <typename _Iter, typename _Comp>
+void __unguarded_partition_pivot (_Iter, _Comp);
+template <typename _Iter, typename _Comp>
+void
+__introsort_loop (_Iter __last, _Comp __comp)
+{
+  _Iter __first;
+  __last - __first;
+  __unguarded_partition_pivot (__last, __comp);
+  __introsort_loop (__last, __comp);
+}
+struct
+{
+  template <random_access_iterator _Iter, sentinel_for<_Iter> _Sent,
+            typename _Comp, typename _Proj>
+  _Iter
+  operator() (_Iter _i, _Sent __last, _Comp __comp, _Proj __proj)
+  {
+    auto __comp_proj = __make_comp_proj (__comp, __proj);
+    __introsort_loop (__last, __comp_proj);
+    return _i;
+  }
+  template <random_access_range _Range, typename _Comp = less, typename _Proj>
+  _Range
+  operator() (_Range __r, _Comp __comp, _Proj __proj)
+  {
+    (*this) (begin, end (__r), __comp, __proj);
+    return __r;
+  }
+} sort;
+void
+reload_tzdb ()
+{
+  sort (links, {}, &time_zone_link::name);
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr114134-1.c b/gcc/testsuite/gcc.target/i386/pr114134-1.c
new file mode 100644
index 00000000000..f6d09e2d9ec
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr114134-1.c
@@ -0,0 +1,27 @@
+/* { dg-do compile { target { *-*-linux* && lp64 } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**Key:
+**.LFB0:
+**	.cfi_startproc
+**	movupd	\(%rdi\), %xmm1
+**	cvttsd2sil	16\(%rdi\), %edx
+**	cvttpd2dq	%xmm1, %xmm0
+**	movq	%xmm0, %rax
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct TVec3D { double x, y, z; };
+
+struct TKey { int i, j, k; };
+
+struct TKey
+Key(struct TVec3D *r)
+{ 
+  return __extension__ (struct TKey) {r->x, r->y, r->z};
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr114134-2.c b/gcc/testsuite/gcc.target/i386/pr114134-2.c
new file mode 100644
index 00000000000..c98b4668058
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr114134-2.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB0:
+**	.cfi_startproc
+**	cvttsd2sil	%xmm0, %eax
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct foo { int i, j; };
+
+struct foo
+func (double x)
+{ 
+  return __extension__ (struct foo) {x, 0};
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr114134-3.c b/gcc/testsuite/gcc.target/i386/pr114134-3.c
new file mode 100644
index 00000000000..95ca71fc654
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr114134-3.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB0:
+**	.cfi_startproc
+**	cvttss2sil	%xmm0, %eax
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct foo { int i, j; };
+
+struct foo
+func (float x)
+{ 
+  return __extension__ (struct foo) {x, 0};
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-1.c b/gcc/testsuite/gcc.target/i386/pr120588-1.c
new file mode 100644
index 00000000000..5f34dd0e71f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-1.c
@@ -0,0 +1,26 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rsi, %rdx
+**	movl	%edi, %eax
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct X {
+  int l1;
+  long long l2;
+};
+
+struct X
+func (struct X x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-10.c b/gcc/testsuite/gcc.target/i386/pr120588-10.c
new file mode 100644
index 00000000000..ee400bc59a5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-10.c
@@ -0,0 +1,26 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rdi, %rax
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct S386
+{
+  _Decimal64 a;
+  int b;
+};
+
+struct S386
+func (struct S386 x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-11.c b/gcc/testsuite/gcc.target/i386/pr120588-11.c
new file mode 100644
index 00000000000..3ded34dfe7d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-11.c
@@ -0,0 +1,26 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movdqa	%xmm3, %xmm1
+**	movdqa	%xmm2, %xmm0
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct S386
+{
+  _Decimal64 a[2];
+};
+
+struct S386
+func (struct S386 y, struct S386 x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-12.c b/gcc/testsuite/gcc.target/i386/pr120588-12.c
new file mode 100644
index 00000000000..b951c73d39a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-12.c
@@ -0,0 +1,36 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rsi, %rdx
+**	movl	%edi, %eax
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct Y1 {
+  int l1;
+  long long l2;
+};
+
+struct Y2
+{
+  struct Y1 y;
+};
+
+struct X
+{
+  struct Y2 y;
+};
+
+struct X
+func (struct X x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-13.c b/gcc/testsuite/gcc.target/i386/pr120588-13.c
new file mode 100644
index 00000000000..2041fffb1ee
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-13.c
@@ -0,0 +1,28 @@
+/* { dg-do compile { target { *-*-linux* && lp64 } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rdi, %rax
+**	movq	%rsi, %rdx
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct basic_format_args
+{
+  int f0 : 4;
+  int f1 : 4;
+  int *_M_args;
+};
+
+struct basic_format_args
+func (struct basic_format_args x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-14.c b/gcc/testsuite/gcc.target/i386/pr120588-14.c
new file mode 100644
index 00000000000..99dc4056e28
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-14.c
@@ -0,0 +1,28 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rdi, %rax
+**	movq	%rsi, %rdx
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct S {
+    union {
+	char addr8[16];
+	int addr32[4];
+    } u;
+};
+
+struct S
+func (struct S ip6)
+{
+  return ip6;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-15.c b/gcc/testsuite/gcc.target/i386/pr120588-15.c
new file mode 100644
index 00000000000..6a073adbc66
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-15.c
@@ -0,0 +1,27 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movl	%esi, %edx
+**	movl	%edi, %eax
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct S347
+{
+  int a;
+  char b __attribute__((aligned(8)));
+};
+
+struct S347
+func (struct S347 x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-16.c b/gcc/testsuite/gcc.target/i386/pr120588-16.c
new file mode 100644
index 00000000000..30995b0c884
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-16.c
@@ -0,0 +1,27 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movl	%esi, %edx
+**	movl	%edi, %eax
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct S347
+{
+  char a __attribute__((aligned(4)));
+  char b __attribute__((aligned(8)));
+};
+
+struct S347
+func (struct S347 x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-17.c b/gcc/testsuite/gcc.target/i386/pr120588-17.c
new file mode 100644
index 00000000000..6f4e375d85a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-17.c
@@ -0,0 +1,27 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rdi, %rax
+**	movl	%esi, %edx
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct S347
+{
+  long long a;
+  char b __attribute__((aligned(8)));
+};
+
+struct S347
+func (struct S347 x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-18.c b/gcc/testsuite/gcc.target/i386/pr120588-18.c
new file mode 100644
index 00000000000..11b47cb7dcd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-18.c
@@ -0,0 +1,35 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rsi, %rdx
+**	movq	%rdi, %rax
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct Y1 {
+  int l1;
+  long long l2;
+};
+
+struct X
+{
+  union U
+    {
+      struct Y1 y;
+      int x;
+    } u;
+};
+
+struct X
+func (struct X x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-19.c b/gcc/testsuite/gcc.target/i386/pr120588-19.c
new file mode 100644
index 00000000000..eb53d80f6c1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-19.c
@@ -0,0 +1,31 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rsi, %rdx
+**	movq	%rdi, %rax
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct Y1 {
+  int l1;
+  long long l2;
+};
+
+union U {
+  struct Y1 y;
+  int x;
+};
+
+union U
+func (union U x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-2.c b/gcc/testsuite/gcc.target/i386/pr120588-2.c
new file mode 100644
index 00000000000..2e975bdb220
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-2.c
@@ -0,0 +1,26 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rdi, %rax
+**	movl	%esi, %edx
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct X {
+  long long l2;
+  int l1;
+};
+
+struct X
+func (struct X x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-20.c b/gcc/testsuite/gcc.target/i386/pr120588-20.c
new file mode 100644
index 00000000000..a9e5f2c988c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-20.c
@@ -0,0 +1,25 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rdi, %rax
+**	movq	%rsi, %rdx
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct X {
+  __int128 l;
+};
+
+struct X
+func (struct X x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-3.c b/gcc/testsuite/gcc.target/i386/pr120588-3.c
new file mode 100644
index 00000000000..ca13826cbd1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-3.c
@@ -0,0 +1,27 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rdi, %rax
+**	movq	%rsi, %rdx
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct X
+{
+  long long l1;
+  long long l2;
+};
+
+struct X
+func (struct X x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-4.c b/gcc/testsuite/gcc.target/i386/pr120588-4.c
new file mode 100644
index 00000000000..51d84d59633
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-4.c
@@ -0,0 +1,29 @@
+/* { dg-do compile { target { *-*-linux* && lp64 } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+#include <stdbool.h>
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	cmpl	%edx, %edi
+**	sete	%al
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct error_condition 
+{
+  int _M_value;
+  int *_M_cat;
+};
+
+bool
+func (struct error_condition x, struct error_condition y)
+{
+  return x._M_value == y._M_value;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-5.c b/gcc/testsuite/gcc.target/i386/pr120588-5.c
new file mode 100644
index 00000000000..a36f79032f5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-5.c
@@ -0,0 +1,27 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rdi, %rax
+**	movl	%esi, %edx
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct X {
+  int f1;
+  int f2;
+  int f3;
+};
+
+struct X
+func (struct X x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-6.c b/gcc/testsuite/gcc.target/i386/pr120588-6.c
new file mode 100644
index 00000000000..8e366172d44
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-6.c
@@ -0,0 +1,26 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rdi, %rax
+**	movl	%esi, %edx
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct X
+{
+  int f1[3];
+};
+
+struct X
+func (struct X x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-7.c b/gcc/testsuite/gcc.target/i386/pr120588-7.c
new file mode 100644
index 00000000000..4f23eb8c976
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-7.c
@@ -0,0 +1,26 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rdi, %rax
+**	movq	%rsi, %rdx
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct foo
+{
+  long long w[2];
+};
+
+struct foo
+func (struct foo x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-8.c b/gcc/testsuite/gcc.target/i386/pr120588-8.c
new file mode 100644
index 00000000000..87b62fce3c9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-8.c
@@ -0,0 +1,26 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movq	%rdi, %rax
+**	movaps	%xmm1, %xmm0
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct X {
+  float l2;
+  long long l1;
+};
+
+struct X
+func (float f, struct X x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120588-9.c b/gcc/testsuite/gcc.target/i386/pr120588-9.c
new file mode 100644
index 00000000000..6afc936500c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-9.c
@@ -0,0 +1,26 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movaps	%xmm1, %xmm0
+**	movq	%rdi, %rax
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+struct X {
+  long long l1;
+  float l2;
+};
+
+struct X
+func (float f, struct X x)
+{
+  return x;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr126403-1.c b/gcc/testsuite/gcc.target/i386/pr126403-1.c
new file mode 100644
index 00000000000..afa394e3587
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr126403-1.c
@@ -0,0 +1,31 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movlhps	%xmm1, %xmm0
+**	movlhps	%xmm3, %xmm2
+**	mulps	%xmm0, %xmm2
+**	movhlps	%xmm2, %xmm1
+**	movdqa	%xmm2, %xmm0
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+#define N 4
+
+typedef struct { float values[N]; } vector;
+
+vector
+func (vector a, vector b)
+{
+  vector r;
+  for (int i = 0; i < N; i++)
+    r.values[i] = a.values[i] * b.values[i];
+  return r;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr126403-2.c b/gcc/testsuite/gcc.target/i386/pr126403-2.c
new file mode 100644
index 00000000000..5b44b623df1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr126403-2.c
@@ -0,0 +1,31 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	unpcklpd	%xmm1, %xmm0
+**	unpcklpd	%xmm3, %xmm2
+**	mulpd	%xmm2, %xmm0
+**	movapd	%xmm0, %xmm1
+**	unpckhpd	%xmm1, %xmm1
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+#define N 2
+
+typedef struct { double values[N]; } vector;
+
+vector
+func (vector a, vector b)
+{
+  vector r;
+  for (int i = 0; i < N; i++)
+    r.values[i] = a.values[i] * b.values[i];
+  return r;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr126403-3.c b/gcc/testsuite/gcc.target/i386/pr126403-3.c
new file mode 100644
index 00000000000..d72c9f85a35
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr126403-3.c
@@ -0,0 +1,31 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64-v4 -mavx512fp16 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	vmovlhps	%xmm1, %xmm0, %xmm1
+**	vmovlhps	%xmm3, %xmm2, %xmm3
+**	vmulph	%xmm3, %xmm1, %xmm1
+**	vmovdqa	%xmm1, %xmm0
+**	vpsrldq	\$8, %xmm1, %xmm1
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+#define N 8
+
+typedef struct { _Float16 values[N]; } vector;
+
+vector
+func (vector a, vector b)
+{
+  vector r;
+  for (int i = 0; i < N; i++)
+    r.values[i] = a.values[i] * b.values[i];
+  return r;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr126403-4.c b/gcc/testsuite/gcc.target/i386/pr126403-4.c
new file mode 100644
index 00000000000..9b14810a78d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr126403-4.c
@@ -0,0 +1,33 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -march=x86-64 -fomit-frame-pointer" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  } } */
+
+/*
+**func:
+**.LFB[0-9]+:
+**	.cfi_startproc
+**	movlhps	%xmm1, %xmm0
+**	movlhps	%xmm3, %xmm2
+**	mulps	%xmm0, %xmm2
+**	movhlps	%xmm2, %xmm1
+**	movdqa	%xmm2, %xmm0
+**	ret
+**	.cfi_endproc
+**...
+*/
+
+#define N 4
+
+typedef struct { float values[N]; } vector1;
+typedef struct { vector1 v; } vector2;
+typedef struct { vector2 v; } vector;
+
+vector
+func (vector a, vector b)
+{
+  vector r;
+  for (int i = 0; i < N; i++)
+    r.v.v.values[i] = a.v.v.values[i] * b.v.v.values[i];
+  return r;
+}
diff --git a/gcc/testsuite/gcc.target/i386/zext-sse-2.c b/gcc/testsuite/gcc.target/i386/zext-sse-2.c
index 7d917cfd5de..e8815c2515c 100644
--- a/gcc/testsuite/gcc.target/i386/zext-sse-2.c
+++ b/gcc/testsuite/gcc.target/i386/zext-sse-2.c
@@ -29,11 +29,10 @@ struct bar2
 **func1:
 **.LFB[0-9]+:
 **	.cfi_startproc
-**	cvttsd2sil	16\(%rdi\), %ecx
 **	movupd	\(%rdi\), %xmm1
+**	cvttsd2sil	16\(%rdi\), %edx
 **	cvttpd2dq	%xmm1, %xmm0
 **	movq	%xmm0, %rax
-**	movq	%rcx, %rdx
 **	ret
 **	.cfi_endproc
 **...
@@ -49,13 +48,10 @@ func1 (struct bar1 *x)
 **func2:
 **.LFB[0-9]+:
 **	.cfi_startproc
-**	cvttss2sil	8\(%rdi\), %eax
 **	movq	\(%rdi\), %xmm0
+**	cvttss2sil	8\(%rdi\), %edx
 **	cvttps2dq	%xmm0, %xmm0
-**	movq	%xmm0, %rsi
-**	movq	%rax, %rdi
-**	movq	%rsi, %rax
-**	movq	%rdi, %rdx
+**	movq	%xmm0, %rax
 **	ret
 **	.cfi_endproc
 **...
-- 
2.55.0