When the mode of the destination operand selected by the condition
is SImode, explicit sign extension is applied to both selected
source operands, and the destination operand is marked as
sign-extended.
This method can eliminate some of the sign extension instructions
caused by conditional selection optimization.
gcc/ChangeLog:
* config/loongarch/loongarch.cc
(loongarch_sign_extend_if_subreg_prom_p): Determine if the
current operand is SUBREG and if the source of SUBREG is
the sign-extended value.
(loongarch_expand_conditional_move): Optimize.
gcc/testsuite/ChangeLog:
* gcc.target/loongarch/sign-extend-4.c: New test.
* gcc.target/loongarch/sign-extend-5.c: New test.
---
gcc/config/loongarch/loongarch.cc | 133 +++++++++---------
.../gcc.target/loongarch/sign-extend-4.c | 16 +++
.../gcc.target/loongarch/sign-extend-5.c | 35 +++++
3 files changed, 118 insertions(+), 66 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/loongarch/sign-extend-4.c
create mode 100644 gcc/testsuite/gcc.target/loongarch/sign-extend-5.c
diff --git a/gcc/config/loongarch/loongarch.cc
b/gcc/config/loongarch/loongarch.cc
index 064b251f9ca..df5b570d917 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -5375,6 +5375,26 @@ loongarch_zero_if_equal (rtx cmp0, rtx cmp1)
OPTAB_DIRECT);
}
+/* Helper function for loongarch_extend_comparands to Sign-extend the OP.
+ However if the OP is SI subreg promoted with an inner DI, such as
+ (subreg/s/v:SI (reg/v:DI) 0)
+ just peel off the SUBREG to get DI, avoiding extraneous extension.
+ This modification refers to riscv's commit r14-5506. */
+
+static void
+loongarch_sign_extend_if_subreg_prom_p (rtx *op)
+{
+ if (SUBREG_P (*op)
+ && SUBREG_PROMOTED_VAR_P (*op)
+ && SUBREG_PROMOTED_SIGNED_P (*op)
+ && REG_P (XEXP (*op, 0))
+ && (GET_MODE_SIZE (GET_MODE (XEXP (*op, 0)))
+ == GET_MODE_SIZE (word_mode)))
+ *op = XEXP (*op, 0);
+ else
+ *op = gen_rtx_SIGN_EXTEND (word_mode, *op);
+}
+
/* Sign- or zero-extend OP0 and OP1 for integer comparisons. */
static void
@@ -5404,17 +5424,16 @@ loongarch_extend_comparands (rtx_code code, rtx *op0,
rtx *op1)
}
else
{
- *op0 = gen_rtx_SIGN_EXTEND (word_mode, *op0);
+ loongarch_sign_extend_if_subreg_prom_p (op0);
/* Regardless of whether *op1 is any immediate number, it is not
loaded into the register, in order to facilitate the generation
of slt{u}i. */
if (!CONST_INT_P (*op1))
- *op1 = gen_rtx_SIGN_EXTEND (word_mode, *op1);
+ loongarch_sign_extend_if_subreg_prom_p (op1);
}
}
}
-
/* Convert a comparison into something that can be used in a branch. On
entry, *OP0 and *OP1 are the values being compared and *CODE is the code
used to compare them. Update them to describe the final comparison. */
@@ -5555,11 +5574,8 @@ loongarch_expand_conditional_move (rtx *operands)
enum rtx_code code = GET_CODE (operands[1]);
rtx op0 = XEXP (operands[1], 0);
rtx op1 = XEXP (operands[1], 1);
- rtx op0_extend = op0;
- rtx op1_extend = op1;
- /* Record whether operands[2] and operands[3] modes are promoted to
word_mode. */
- bool promote_op[2] = {false, false};
+ /* Record whether operands[0] is extended by SImode. */
bool promote_p = false;
machine_mode mode = GET_MODE (operands[0]);
@@ -5662,25 +5678,10 @@ loongarch_expand_conditional_move (rtx *operands)
}
}
- if (GET_MODE_SIZE (GET_MODE (op0)) < UNITS_PER_WORD)
- {
- promote_op[0] = (REG_P (op0) && REG_P (operands[2]) &&
- REGNO (op0) == REGNO (operands[2]));
- promote_op[1] = (REG_P (op1) && REG_P (operands[3]) &&
- REGNO (op1) == REGNO (operands[3]));
- }
-
- if (promote_op[0] || promote_op[1])
- {
- mode = word_mode;
- promote_p = true;
- }
-
loongarch_extend_comparands (code, &op0, &op1);
op0 = force_reg (word_mode, op0);
- op0_extend = op0;
- op1_extend = force_reg (word_mode, op1);
+ op1 = CONST_INT_P (op1) ? op1 : force_reg (word_mode, op1);
rtx target = gen_reg_rtx (GET_MODE (op0));
@@ -5726,65 +5727,65 @@ loongarch_expand_conditional_move (rtx *operands)
}
}
+ /* If the target of the mov<mode> is SImode, then the two operands are
+ extended to display symbols. */
+ if (TARGET_64BIT && mode == SImode)
+ {
+ loongarch_extend_comparands (code, &operands[2], &operands[3]);
+ operands[2] = force_reg (word_mode, operands[2]);
+ operands[3] = CONST_INT_P (operands[3]) ? operands[3]
+ : force_reg (word_mode, operands[3]);
+
+ promote_p = true;
+ mode = DImode;
+ }
+
rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
/* There is no direct support for general conditional GP move involving
two registers using SEL. */
- if (INTEGRAL_MODE_P (GET_MODE (operands[2]))
- && register_operand (operands[2], VOIDmode)
- && register_operand (operands[3], VOIDmode))
+ if (INTEGRAL_MODE_P (GET_MODE (operands[0])))
{
- rtx op2 = operands[2];
- rtx op3 = operands[3];
+ rtx pdest = promote_p ? gen_reg_rtx (mode) : operands[0];
- if (promote_p)
+ if (register_operand (operands[2], VOIDmode)
+ && register_operand (operands[3], VOIDmode))
{
- if (promote_op[0])
- op2 = op0_extend;
- else
- {
- loongarch_extend_comparands (code, &op2, &const0_rtx);
- op2 = force_reg (mode, op2);
- }
+ rtx sel1 = gen_reg_rtx (mode);
+ rtx sel2 = gen_reg_rtx (mode);
+ emit_insn (gen_rtx_SET (sel1,
+ gen_rtx_IF_THEN_ELSE (mode, cond,
+ operands[2],
+ const0_rtx)));
+ /* Flip the test for the second operand. */
+ cond = gen_rtx_fmt_ee ((code == EQ) ? NE
+ : EQ, GET_MODE (op0),
+ op0, op1);
- if (promote_op[1])
- op3 = op1_extend;
- else
- {
- loongarch_extend_comparands (code, &op3, &const0_rtx);
- op3 = force_reg (mode, op3);
- }
- }
-
- rtx temp = gen_reg_rtx (mode);
- rtx temp2 = gen_reg_rtx (mode);
-
- emit_insn (gen_rtx_SET (temp,
- gen_rtx_IF_THEN_ELSE (mode, cond,
- op2, const0_rtx)));
-
- /* Flip the test for the second operand. */
- cond = gen_rtx_fmt_ee ((code == EQ) ? NE : EQ, GET_MODE (op0), op0, op1);
+ emit_insn (gen_rtx_SET (sel2,
+ gen_rtx_IF_THEN_ELSE (mode, cond,
+ operands[3],
+ const0_rtx)));
- emit_insn (gen_rtx_SET (temp2,
- gen_rtx_IF_THEN_ELSE (mode, cond,
- op3, const0_rtx)));
+ /* Merge the two results, at least one is guaranteed to be zero. */
+ emit_insn (gen_rtx_SET (pdest, gen_rtx_IOR (mode, sel1, sel2)));
+ }
+ else
+ emit_insn (gen_rtx_SET (pdest,
+ gen_rtx_IF_THEN_ELSE (mode, cond,
+ operands[2],
+ operands[3])));
- /* Merge the two results, at least one is guaranteed to be zero. */
if (promote_p)
{
- rtx temp3 = gen_reg_rtx (mode);
- emit_insn (gen_rtx_SET (temp3, gen_rtx_IOR (mode, temp, temp2)));
- temp3 = gen_lowpart (GET_MODE (operands[0]), temp3);
+ pdest = gen_lowpart (GET_MODE (operands[0]), pdest);
/* Nonzero in a subreg if it was made when accessing an object that
was promoted to a wider mode in accord with the PROMOTED_MODE
machine description macro. */
- SUBREG_PROMOTED_VAR_P (temp3) = 1;
+ SUBREG_PROMOTED_VAR_P (pdest) = 1;
/* Sets promoted mode for SUBREG_PROMOTED_VAR_P. */
- SUBREG_PROMOTED_SET (temp3, SRP_SIGNED);
- loongarch_emit_move (operands[0], temp3);
+ SUBREG_PROMOTED_SET (pdest, SRP_SIGNED);
+ loongarch_emit_move (operands[0], pdest);
}
- else
- emit_insn (gen_rtx_SET (operands[0], gen_rtx_IOR (mode, temp, temp2)));
}
else
emit_insn (gen_rtx_SET (operands[0],
diff --git a/gcc/testsuite/gcc.target/loongarch/sign-extend-4.c
b/gcc/testsuite/gcc.target/loongarch/sign-extend-4.c
new file mode 100644
index 00000000000..d716e5bc562
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/sign-extend-4.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O2" } */
+/* { dg-final { scan-assembler-not "slli.w\t\\\$r\[0-9\]+,\\\$r\[0-9\]+,0" } }
*/
+
+extern int items;
+extern int gv_fetchmeth (int);
+int
+Perl_gv_fetchmeth (int level)
+{
+ int gv;
+ while (items--)
+ gv = gv_fetchmeth ((level >= 0) ? level + 1 : level - 1);
+
+ return gv;
+}
+
diff --git a/gcc/testsuite/gcc.target/loongarch/sign-extend-5.c
b/gcc/testsuite/gcc.target/loongarch/sign-extend-5.c
new file mode 100644
index 00000000000..21f5de756f4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/sign-extend-5.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O2" } */
+/* { dg-final { scan-assembler-not "slli.w" } } */
+
+typedef struct xpvav XPVAV;
+struct xpvav
+{
+ long int xav_fill;
+ long int xav_max;
+};
+typedef struct av AV;
+struct av
+{
+ XPVAV *sv_any;
+ unsigned int sv_refcnt;
+ unsigned int sv_flags;
+};
+void Perl_av_extend (AV *ar, int key);
+void
+Perl_av_unshift (AV *av, int num)
+{
+ int i;
+ int slide;
+
+ if (num)
+ {
+ i = ((XPVAV *)(av)->sv_any)->xav_fill;
+
+ slide = i > 0 ? i : 0;
+ num += slide;
+ Perl_av_extend (av, i + num);
+
+ ((XPVAV *)(av)->sv_any)->xav_max -= slide;
+ }
+}
--
2.34.1