On 11/13/18 2:53 AM, Eric Botcazou wrote:
>> +static rtx
>> +simple_move_operator (rtx x)
>> +{
>> + /* A word sized rotate of a register pair is equivalent to swapping
>> + the registers in the register pair. */
>> + if (GET_CODE (x) == ROTATE
>> + && GET_MODE (x) == twice_word_mode
>> + && simple_move_operand (XEXP (x, 0))
>> + && CONST_INT_P (XEXP (x, 1))
>> + && INTVAL (XEXP (x, 1)) == BITS_PER_WORD)
>> + return XEXP (x, 0);;
>> +
>> + return NULL_RTX;
>> +}
>> +
>
> Superfluous semi-colon. Given that the function returns an operand, its name
> is IMO misleading, so maybe [get_]operand_for_simple_move_operator.
Fixed and renamed function to operand_for_simple_move_operator.
> Can we factor out the duplicate manipulation into a function here, for
> example
> resolve_operand_for_simple_move_operator?
Good idea. I went with your name, but would swap_concatn_operands() be a
better name, since that is what it is doing? I'll leave it up to you.
Updated patch below.
Peter
gcc/
PR rtl-optimization/87507
* lower-subreg.c (operand_for_simple_move_operator): New function.
(simple_move): Strip simple operators.
(find_pseudo_copy): Likewise.
(resolve_operand_for_simple_move_operator): New function.
(resolve_simple_move): Strip simple operators and swap operands.
gcc/testsuite/
PR rtl-optimization/87507
* gcc.target/powerpc/pr87507.c: New test.
* gcc.target/powerpc/pr68805.c: Update expected results.
Index: gcc/lower-subreg.c
===================================================================
--- gcc/lower-subreg.c (revision 265971)
+++ gcc/lower-subreg.c (working copy)
@@ -320,6 +320,24 @@ simple_move_operand (rtx x)
return true;
}
+/* If X is an operator that can be treated as a simple move that we
+ can split, then return the operand that is operated on. */
+
+static rtx
+operand_for_simple_move_operator (rtx x)
+{
+ /* A word sized rotate of a register pair is equivalent to swapping
+ the registers in the register pair. */
+ if (GET_CODE (x) == ROTATE
+ && GET_MODE (x) == twice_word_mode
+ && simple_move_operand (XEXP (x, 0))
+ && CONST_INT_P (XEXP (x, 1))
+ && INTVAL (XEXP (x, 1)) == BITS_PER_WORD)
+ return XEXP (x, 0);
+
+ return NULL_RTX;
+}
+
/* If INSN is a single set between two objects that we want to split,
return the single set. SPEED_P says whether we are optimizing
INSN for speed or size.
@@ -330,7 +348,7 @@ simple_move_operand (rtx x)
static rtx
simple_move (rtx_insn *insn, bool speed_p)
{
- rtx x;
+ rtx x, op;
rtx set;
machine_mode mode;
@@ -348,6 +366,9 @@ simple_move (rtx_insn *insn, bool speed_
return NULL_RTX;
x = SET_SRC (set);
+ if ((op = operand_for_simple_move_operator (x)) != NULL_RTX)
+ x = op;
+
if (x != recog_data.operand[0] && x != recog_data.operand[1])
return NULL_RTX;
/* For the src we can handle ASM_OPERANDS, and it is beneficial for
@@ -386,9 +407,13 @@ find_pseudo_copy (rtx set)
{
rtx dest = SET_DEST (set);
rtx src = SET_SRC (set);
+ rtx op;
unsigned int rd, rs;
bitmap b;
+ if ((op = operand_for_simple_move_operator (src)) != NULL_RTX)
+ src = op;
+
if (!REG_P (dest) || !REG_P (src))
return false;
@@ -846,6 +871,21 @@ can_decompose_p (rtx x)
return true;
}
+/* OPND is a concatn operand this is used with a simple move operator.
+ Return a new rtx with the concatn's operands swapped. */
+
+static rtx
+resolve_operand_for_simple_move_operator (rtx opnd)
+{
+ gcc_assert (GET_CODE (opnd) == CONCATN);
+ rtx concatn = copy_rtx (opnd);
+ rtx op0 = XVECEXP (concatn, 0, 0);
+ rtx op1 = XVECEXP (concatn, 0, 1);
+ XVECEXP (concatn, 0, 0) = op1;
+ XVECEXP (concatn, 0, 1) = op0;
+ return concatn;
+}
+
/* Decompose the registers used in a simple move SET within INSN. If
we don't change anything, return INSN, otherwise return the start
of the sequence of moves. */
@@ -853,7 +893,7 @@ can_decompose_p (rtx x)
static rtx_insn *
resolve_simple_move (rtx set, rtx_insn *insn)
{
- rtx src, dest, real_dest;
+ rtx src, dest, real_dest, src_op;
rtx_insn *insns;
machine_mode orig_mode, dest_mode;
unsigned int orig_size, words;
@@ -876,6 +916,23 @@ resolve_simple_move (rtx set, rtx_insn *
real_dest = NULL_RTX;
+ if ((src_op = operand_for_simple_move_operator (src)) != NULL_RTX)
+ {
+ if (resolve_reg_p (dest))
+ {
+ /* DEST is a CONCATN, so swap its operands and strip
+ SRC's operator. */
+ dest = resolve_operand_for_simple_move_operator (dest);
+ src = src_op;
+ }
+ else if (resolve_reg_p (src_op))
+ {
+ /* SRC is an operation on a CONCATN, so strip the operator and
+ swap the CONCATN's operands. */
+ src = resolve_operand_for_simple_move_operator (src_op);
+ }
+ }
+
if (GET_CODE (src) == SUBREG
&& resolve_reg_p (SUBREG_REG (src))
&& (maybe_ne (SUBREG_BYTE (src), 0)
Index: gcc/testsuite/gcc.target/powerpc/pr68805.c
===================================================================
--- gcc/testsuite/gcc.target/powerpc/pr68805.c (revision 265971)
+++ gcc/testsuite/gcc.target/powerpc/pr68805.c (working copy)
@@ -9,7 +9,7 @@ typedef struct bar {
void foo (TYPE *p, TYPE *q) { *p = *q; }
-/* { dg-final { scan-assembler "lxvd2x" } } */
-/* { dg-final { scan-assembler "stxvd2x" } } */
+/* { dg-final { scan-assembler-times {\mld\M} 2 } } */
+/* { dg-final { scan-assembler-times {\mstd\M} 2 } } */
/* { dg-final { scan-assembler-not "xxpermdi" } } */
Index: gcc/testsuite/gcc.target/powerpc/pr87507.c
===================================================================
--- gcc/testsuite/gcc.target/powerpc/pr87507.c (nonexistent)
+++ gcc/testsuite/gcc.target/powerpc/pr87507.c (working copy)
@@ -0,0 +1,22 @@
+/* { dg-do compile { target powerpc64le-*-* } } */
+/* { dg-skip-if "do not override -mcpu" { powerpc*-*-* } { "-mcpu=*" } {
"-mcpu=power8" } } */
+/* { dg-options "-O2 -mcpu=power8" } */
+
+typedef struct
+{
+ __int128_t x;
+ __int128_t y;
+} foo_t;
+
+void
+foo (long cond, foo_t *dst, __int128_t src)
+{
+ if (cond)
+ {
+ dst->x = src;
+ dst->y = src;
+ }
+}
+
+/* { dg-final { scan-assembler-times {\mstd\M} 4 } } */
+/* { dg-final { scan-assembler-not {\mld\M} } } */