commit 428cbef107b559f67006fe84b4522210902b101f Author: Kyrylo Tkachov Date: Wed Jul 15 17:01:13 2015 +0100 [AArch64][1/3] Expand signed mod by power of 2 using CSNEG diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c index 9d88a60..7bb4a55 100644 --- a/gcc/config/aarch64/aarch64.c +++ b/gcc/config/aarch64/aarch64.c @@ -6639,8 +6639,26 @@ cost_plus: if (VECTOR_MODE_P (mode)) *cost += extra_cost->vect.alu; else if (GET_MODE_CLASS (mode) == MODE_INT) - *cost += (extra_cost->mult[mode == DImode].add - + extra_cost->mult[mode == DImode].idiv); + { + /* We can expand signed mod by power of 2 using a + NEGS, two parallel ANDs and a CSNEG. Assume here + that CSNEG is COSTS_N_INSNS (1). This case should + only ever be reached through the set_smod_pow2_cheap check + in expmed.c. */ + if (code == MOD + && CONST_INT_P (XEXP (x, 1)) + && exact_log2 (INTVAL (XEXP (x, 1))) > 0 + && (mode == SImode || mode == DImode)) + { + *cost += COSTS_N_INSNS (3) + + 2 * extra_cost->alu.logical + + extra_cost->alu.arith; + return true; + } + + *cost += (extra_cost->mult[mode == DImode].add + + extra_cost->mult[mode == DImode].idiv); + } else if (mode == DFmode) *cost += (extra_cost->fp[1].mult + extra_cost->fp[1].div); diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md index f264534..3aaf407 100644 --- a/gcc/config/aarch64/aarch64.md +++ b/gcc/config/aarch64/aarch64.md @@ -302,6 +302,62 @@ (define_expand "cmp" } ) +;; AArch64-specific expansion of signed mod by power of 2 using CSNEG. +;; For x0 % n where n is a power of 2 produce: +;; negs x1, x0 +;; and x0, x0, #(n - 1) +;; and x1, x1, #(n - 1) +;; csneg x0, x0, x1, mi + +(define_expand "mod3" + [(match_operand:GPI 0 "register_operand" "") + (match_operand:GPI 1 "register_operand" "") + (match_operand:GPI 2 "const_int_operand" "")] + "" + { + HOST_WIDE_INT val = INTVAL (operands[2]); + + if (val <= 0 + || exact_log2 (INTVAL (operands[2])) <= 0 + || !aarch64_bitmask_imm (INTVAL (operands[2]) - 1, mode)) + FAIL; + + rtx mask = GEN_INT (val - 1); + + /* In the special case of x0 % 2 we can do the even shorter: + cmp x0, xzr + and x0, x0, 1 + csneg x0, x0, x0, ge. */ + if (val == 2) + { + rtx masked = gen_reg_rtx (mode); + rtx ccreg = aarch64_gen_compare_reg (LT, operands[1], const0_rtx); + emit_insn (gen_and3 (masked, operands[1], mask)); + rtx x = gen_rtx_LT (VOIDmode, ccreg, const0_rtx); + emit_insn (gen_csneg3_insn (operands[0], x, masked, masked)); + DONE; + } + + rtx neg_op = gen_reg_rtx (mode); + rtx_insn *insn = emit_insn (gen_neg2_compare0 (neg_op, operands[1])); + + /* Extract the condition register and mode. */ + rtx cmp = XVECEXP (PATTERN (insn), 0, 0); + rtx cc_reg = SET_DEST (cmp); + rtx cond = gen_rtx_GE (VOIDmode, cc_reg, const0_rtx); + + rtx masked_pos = gen_reg_rtx (mode); + emit_insn (gen_and3 (masked_pos, operands[1], mask)); + + rtx masked_neg = gen_reg_rtx (mode); + emit_insn (gen_and3 (masked_neg, neg_op, mask)); + + emit_insn (gen_csneg3_insn (operands[0], cond, + masked_neg, masked_pos)); + DONE; + } +) + (define_insn "*condjump" [(set (pc) (if_then_else (match_operator 0 "aarch64_comparison_operator" [(match_operand 1 "cc_register" "") (const_int 0)]) @@ -2372,7 +2428,7 @@ (define_insn "*ngcsi_uxtw" [(set_attr "type" "adc_reg")] ) -(define_insn "*neg2_compare0" +(define_insn "neg2_compare0" [(set (reg:CC_NZ CC_REGNUM) (compare:CC_NZ (neg:GPI (match_operand:GPI 1 "register_operand" "r")) (const_int 0)))