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.
gcc/
PR target/114134
PR target/120588
PR target/126403
PR target/126424
* expr.cc (emit_group_load): Try parallel_mov optab for
PARALLEL destination.
(emit_group_store): Try parallel_mov optab for PARALLEL source.
* optabs.def (parallel_mov_optab): New.
(ix86_expand_parallel_move): New.
* config/i386/i386-protos.h (ix86_expand_parallel_move): Likewise.
* config/i386/i386.md (parallel_movti): Likewise.
(parallel_movblk): Likewise.
* config/i386/predicates.md (any_parallel_operand): Likewise.
* config/i386/sse.md (*vec_extractv2di_1 splitter): New.
* doc/md.texi: Document parallel_mov.
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.
--
H.J.
From 7b92e7af019244a82494d70fcf7b0e5ddb1c0d1e Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <[email protected]>
Date: Sun, 12 Jul 2026 07:59:57 +0800
Subject: [PATCH] Add parallel_mov optab
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.
gcc/
PR target/114134
PR target/120588
PR target/126403
PR target/126424
* expr.cc (emit_group_load): Try parallel_mov optab for
PARALLEL destination.
(emit_group_store): Try parallel_mov optab for PARALLEL source.
* optabs.def (parallel_mov_optab): New.
(ix86_expand_parallel_move): New.
* config/i386/i386-protos.h (ix86_expand_parallel_move): Likewise.
* config/i386/i386.md (parallel_movti): Likewise.
(parallel_movblk): Likewise.
* config/i386/predicates.md (any_parallel_operand): Likewise.
* config/i386/sse.md (*vec_extractv2di_1 splitter): New.
* doc/md.texi: Document parallel_mov.
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/i386-expand.cc | 340 ++++++++++++++++++++
gcc/config/i386/i386-protos.h | 1 +
gcc/config/i386/i386.md | 22 ++
gcc/config/i386/predicates.md | 4 +
gcc/config/i386/sse.md | 15 +
gcc/doc/md.texi | 7 +
gcc/expr.cc | 28 ++
gcc/optabs.def | 1 +
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 +-
37 files changed, 1253 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/i386-expand.cc b/gcc/config/i386/i386-expand.cc
index 20914fcbad9..f06481bbd6f 100644
--- a/gcc/config/i386/i386-expand.cc
+++ b/gcc/config/i386/i386-expand.cc
@@ -590,6 +590,346 @@ ix86_expand_move (machine_mode mode, rtx operands[])
emit_insn (gen_rtx_SET (op0, op1));
}
+/* Expand TImode PARALLEL moves for function return and parameter
+ passing. */
+
+bool
+ix86_expand_parallel_move (rtx operands[])
+{
+ /* Skip the naked function to avoid trashing callee-saved registers
+ since they won't be preserved without prologue nor epilogue. */
+ if (ix86_function_naked (current_function_decl))
+ return false;
+
+ rtx op0 = operands[0];
+ rtx op1 = operands[1];
+ if (MEM_P (op1))
+ {
+ /* 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 (op1) != 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;
+
+ /* 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);
+ emit_move_insn (src, mem);
+ emit_move_insn (low, src);
+
+ /* Load the remaining 4-byte memory into the subreg. */
+ mem = adjust_address (op1, SImode, 8);
+ src = gen_reg_rtx (SImode);
+ emit_move_insn (src, mem);
+ emit_move_insn (high, src);
+ return true;
+ }
+
+ /* 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)
+ || (!GENERAL_REGNO_P (REGNO (reg0))
+ && !SSE_REGNO_P (REGNO (reg0))))
+ || (!reg1
+ || !REG_P (reg1)
+ || (!GENERAL_REGNO_P (REGNO (reg1))
+ && !SSE_REGNO_P (REGNO (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 (SSE_REGNO_P (REGNO (reg0)) != SSE_REGNO_P (REGNO (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));
+
+ 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 nunits = 16 / GET_MODE_SIZE (mode1);
+ 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;
+
+ /* Move the first register to the 8-byte mode. */
+ rtx src0 = gen_reg_rtx (mode1);
+ reg0 = gen_lowpart (mode1, reg0);
+ emit_move_insn (src0, reg0);
+
+ /* Move the second register to the 8-byte mode. */
+ rtx src1 = gen_reg_rtx (mode1);
+ reg1 = gen_lowpart (mode1, reg1);
+ emit_move_insn (src1, reg1);
+
+ /* Concatenate 2 8-byte registers into OP0. */
+ rtx op = gen_rtx_VEC_CONCAT (mode0, src0, src1);
+ rtx dest = gen_reg_rtx (mode0);
+ emit_move_insn (dest, op);
+ op = gen_lowpart (TImode, dest);
+ emit_move_insn (op0, op);
+ }
+ 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
+ matterr. */
+ 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;
+
+ /* 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);
+ emit_move_insn (src, reg0);
+ emit_move_insn (low, src);
+
+ /* Move the second register into the subreg. */
+ src = gen_reg_rtx (mode1);
+ reg1 = gen_lowpart (mode1, reg1);
+ emit_move_insn (src, reg1);
+ /* 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);
+ emit_insn (set);
+ }
+
+ return true;
+}
+
/* OP is a memref of CONST_VECTOR, return scalar constant mem
if CONST_VECTOR is a vec_duplicate, else return NULL. */
rtx
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 21994d3d498..4565cc5bfdb 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -108,6 +108,7 @@ extern void ix86_output_patchable_area (unsigned int, bool);
extern void ix86_expand_clear (rtx);
extern void ix86_expand_move (machine_mode, rtx[]);
+extern bool ix86_expand_parallel_move (rtx[]);
extern void ix86_expand_vector_move (machine_mode, rtx[]);
extern void ix86_expand_vector_move_misalign (machine_mode, rtx[]);
extern rtx ix86_broadcast_from_constant (machine_mode, rtx);
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 230fcb576d0..94ae21dbe3e 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -2438,6 +2438,28 @@ (define_expand "movti"
DONE;
})
+(define_expand "parallel_movti"
+ [(set (match_operand:TI 0 "nonimmediate_operand")
+ (match_operand:TI 1 "any_parallel_operand"))]
+ "TARGET_64BIT"
+{
+ if (ix86_expand_parallel_move (operands))
+ DONE;
+ else
+ FAIL;
+})
+
+(define_expand "parallel_movblk"
+ [(set (match_operand:BLK 0 "any_parallel_operand")
+ (match_operand:BLK 1 "nonimmediate_operand"))]
+ "TARGET_64BIT"
+{
+ if (ix86_expand_parallel_move (operands))
+ DONE;
+ else
+ FAIL;
+})
+
;; This expands to what emit_move_complex would generate if we didn't
;; have a movti pattern. Having this avoids problems with reload on
;; 32-bit targets when SSE is present, but doesn't seem to be harmful
diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md
index 6a2ced03604..23e749be47c 100644
--- a/gcc/config/i386/predicates.md
+++ b/gcc/config/i386/predicates.md
@@ -2396,3 +2396,7 @@ (define_predicate "apx_evex_add_memory_operand"
return true;
})
+
+;; Return true if OP is any PARALLEL operand.
+(define_predicate "any_parallel_operand"
+ (match_code "parallel"))
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index bd6ce2ac70c..ccc002c82e3 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -22325,6 +22325,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/doc/md.texi b/gcc/doc/md.texi
index c0026c17318..e4c0077d066 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -4878,6 +4878,13 @@ memory, so that it's easy to tell whether this is a load or store.
This pattern is used by the autovectorizer, and when expanding a
@code{MISALIGNED_INDIRECT_REF} expression.
+@mdindex parallel_mov@var{m}
+@item @samp{parallel_mov@var{m}}
+Like @samp{mov@var{m}} except that one of operands must be
+@code{PARALLEL}. If operand 1 is a memory, it must be in @code{BLKmode}
+and operand 0 must be @code{PARALLEL}. If operand 1 is @code{PARALLEL},
+operand 0 must be a register.
+
@mdindex load_multiple
@item @samp{load_multiple}
Load several consecutive memory locations into consecutive registers.
diff --git a/gcc/expr.cc b/gcc/expr.cc
index 210a7bc0888..43eb74a8b57 100644
--- a/gcc/expr.cc
+++ b/gcc/expr.cc
@@ -3164,6 +3164,21 @@ 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)
+ {
+ insn_code icode = optab_handler (parallel_mov_optab, BLKmode);
+ if (icode != CODE_FOR_nothing)
+ {
+ expand_operand ops[2];
+ create_fixed_operand (&ops[0], dst);
+ create_fixed_operand (&ops[1], src);
+ if (maybe_expand_insn (icode, 2, ops))
+ return;
+ }
+ }
+
tmps = XALLOCAVEC (rtx, XVECLEN (dst, 0));
emit_group_load_1 (tmps, dst, src, type, ssize);
@@ -3281,6 +3296,19 @@ emit_group_store (rtx orig_dst, rtx src, tree type ATTRIBUTE_UNUSED,
return;
}
+ if (GET_CODE (src) == PARALLEL)
+ {
+ insn_code icode = optab_handler (parallel_mov_optab, m);
+ if (icode != CODE_FOR_nothing)
+ {
+ expand_operand ops[2];
+ create_fixed_operand (&ops[0], orig_dst);
+ create_fixed_operand (&ops[1], src);
+ if (maybe_expand_insn (icode, 2, ops))
+ 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/optabs.def b/gcc/optabs.def
index 7ccea18543f..f14c2312445 100644
--- a/gcc/optabs.def
+++ b/gcc/optabs.def
@@ -237,6 +237,7 @@ OPTAB_D (extzvmisalign_optab, "extzvmisalign$a")
OPTAB_D (push_optab, "push$a1")
OPTAB_D (reload_in_optab, "reload_in$a")
OPTAB_D (reload_out_optab, "reload_out$a")
+OPTAB_D (parallel_mov_optab, "parallel_mov$a")
OPTAB_DC(cbranch_optab, "cbranch$a4", COMPARE)
OPTAB_D (tbranch_eq_optab, "tbranch_eq$a3")
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..5bdd7606dfd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120588-13.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 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