https://gcc.gnu.org/g:538b28cfe1c6157dc63791bbf7bafb949f1c6a85
commit r16-6166-g538b28cfe1c6157dc63791bbf7bafb949f1c6a85 Author: Takayuki 'January June' Suwa <[email protected]> Date: Sun Dec 14 22:08:38 2025 +0900 xtensa: Improve usage of SALT/SALTU instructions In the expansion of cstoresi4 insn patterns, LT[U] comparisons where the second operand is an integer constant are canonicalized to LE[U] ones with one less than the original. /* example */ int test0(int a) { return a < 100; } unsigned int test1(unsigned int a) { return a <= 100u; } void test2(int a[], int b) { int i; for (i = 0; i < 16; ++i) a[i] = (a[i] <= b); } ;; before (TARGET_SALT) test0: entry sp, 32 movi a8, 0x63 salt a2, a8, a2 addi.n a2, a2, -1 ;; unwanted inverting neg a2, a2 ;; retw.n test1: entry sp, 32 movi a8, 0x64 saltu a2, a8, a2 addi.n a2, a2, -1 ;; unwanted inverting neg a2, a2 ;; retw.n test2: entry sp, 32 movi.n a9, 0x10 loop a9, .L5_LEND .L5: l32i.n a8, a2, 0 salt a8, a3, a8 addi.n a8, a8, -1 ;; immediate cannot be hoisted out neg a8, a8 s32i.n a8, a2, 0 addi.n a2, a2, 4 .L5_LEND: retw.n This patch reverts such canonicalization by adding 1 to the comparison value and then converting it back from LE[U] to LT[U], which better matches the output machine instructions. This patch also makes it easier to benefit from other optimizations such as CSE, constant propagation, or loop-invariant hoisting by XORing the result with a register that has a value of 1, rather than subtracting 1 and then negating the sign to invert the truth of the result. ;; after (TARGET_SALT) test0: entry sp, 32 movi a8, 0x64 salt a2, a2, a8 retw.n test1: entry sp, 32 movi a8, 0x65 saltu a2, a2, a8 retw.n test2: entry sp, 32 movi.n a10, 1 ;; hoisted out movi.n a9, 0x10 loop a9, .L5_LEND .L5: l32i.n a8, a2, 0 salt a8, a3, a8 xor a8, a8, a10 s32i.n a8, a2, 0 addi.n a2, a2, 4 .L5_LEND: retw.n gcc/ChangeLog: * config/xtensa/xtensa.cc (xtensa_expand_scc_SALT): New sub-function that emits the SALT/SALTU instructions. (xtensa_expand_scc): Change the part related to the SALT/SALTU instructions to a call to the above sub-function. Diff: --- gcc/config/xtensa/xtensa.cc | 115 ++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/gcc/config/xtensa/xtensa.cc b/gcc/config/xtensa/xtensa.cc index 43df10e4b63e..1299e45bc7d1 100644 --- a/gcc/config/xtensa/xtensa.cc +++ b/gcc/config/xtensa/xtensa.cc @@ -993,70 +993,71 @@ xtensa_expand_conditional_move (rtx *operands, int isflt) } -int -xtensa_expand_scc (rtx operands[4], machine_mode cmp_mode) +static bool +xtensa_expand_scc_SALT (rtx dest, enum rtx_code code, rtx op0, rtx op1) { - rtx dest = operands[0]; - rtx cmp; - rtx one_tmp, zero_tmp; - rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx); + int flags; - if (cmp_mode == SImode && TARGET_SALT) + /* Revert back the canonicalization of '(lt[u]:SI (reg:SI) (const_int N)' + to '(le[u]:SI (reg:SI) (const_int N-1)'. + Note that a comparison like '(le[u]:SI (reg:SI) (const_int [U]INT_MAX))' + will never be passed; because the result of such a comparison is a mere + constant. */ + if (CONST_INT_P (op1)) + switch (code) + { + case LE: + code = LT, op1 = GEN_INT (INTVAL (op1) + 1); + break; + case LEU: + code = LTU, op1 = GEN_INT (UINTVAL (op1) + 1u); + break; + default: + break; + } + + /* b0: inverting, b1: swap(op0,op1), b2: unsigned */ + switch (code) { - rtx a = operands[2], b = force_reg (SImode, operands[3]); - enum rtx_code code = GET_CODE (operands[1]); - bool invert_res = false; + case GE: flags = 1; break; + case GT: flags = 2; break; + case LE: flags = 3; break; + case LT: flags = 0; break; + case GEU: flags = 5; break; + case GTU: flags = 6; break; + case LEU: flags = 7; break; + case LTU: flags = 4; break; + default: return false; + } - switch (code) - { - case GE: - case GEU: - invert_res = true; - break; - case GT: - case GTU: - std::swap (a, b); - break; - case LE: - case LEU: - invert_res = true; - std::swap (a, b); - break; - default: - break; - } + op1 = force_reg (SImode, op1); + if (flags & 2) + std::swap (op0, op1); + emit_insn ((flags & 4) ? gen_saltu (dest, op0, op1) + : gen_salt (dest, op0, op1)); + if (flags & 1) + /* XORing with a temporary register with a value of 1 is advantageous + over negation followed by addition of 1 because the temporary register + assignment can be subject to CSE, constant propagation, or loop- + invariant hoisting. */ + emit_insn (gen_xorsi3 (dest, dest, force_reg (SImode, const1_rtx))); - switch (code) - { - case GE: - case GT: - case LE: - case LT: - emit_insn (gen_salt (dest, a, b)); - if (!invert_res) - return 1; - break; - case GEU: - case GTU: - case LEU: - case LTU: - emit_insn (gen_saltu (dest, a, b)); - if (!invert_res) - return 1; - break; - default: - break; - } + return true; +} - if (invert_res) - { - emit_insn (gen_negsi2 (dest, dest)); - emit_insn (gen_addsi3 (dest, dest, const1_rtx)); - return 1; - } - } +int +xtensa_expand_scc (rtx operands[4], machine_mode cmp_mode) +{ + rtx dest = operands[0]; + rtx cmp, one_tmp, zero_tmp; + rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx); + enum rtx_code code = GET_CODE (operands[1]); + + if (TARGET_SALT && cmp_mode == SImode + && xtensa_expand_scc_SALT (dest, code, operands[2], operands[3])) + return 1; - if (! (cmp = gen_conditional_move (GET_CODE (operands[1]), cmp_mode, + if (! (cmp = gen_conditional_move (code, cmp_mode, operands[2], operands[3]))) return 0;
