From: Lulu Cheng Date: Wed, 29 Oct 2025 09:55:14 +0000 (+0800) Subject: LoongArch: Redundant sign extension instruction optimization. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f6bca888663cc8d451b0f01ea459454b2ebc4c81;p=thirdparty%2Fgcc.git LoongArch: Redundant sign extension instruction optimization. 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. --- diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc index 4a9604eb1eb..462743f8399 100644 --- a/gcc/config/loongarch/loongarch.cc +++ b/gcc/config/loongarch/loongarch.cc @@ -5388,6 +5388,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 @@ -5417,17 +5437,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. */ @@ -5568,11 +5587,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]); @@ -5675,25 +5691,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)); @@ -5739,65 +5740,65 @@ loongarch_expand_conditional_move (rtx *operands) } } + /* If the target of the mov 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; + } +}