+
+
+/* Expand signed modulus of OP0 by a power of two D in mode MODE. */
+
+static rtx
+expand_smod_pow2 (enum machine_mode mode, rtx op0, HOST_WIDE_INT d)
+{
+ unsigned HOST_WIDE_INT mask;
+ rtx result, temp, shift, label;
+ int logd;
+
+ logd = floor_log2 (d);
+ result = gen_reg_rtx (mode);
+
+ /* Avoid conditional branches when they're expensive. */
+ if (BRANCH_COST >= 2
+ && !optimize_size)
+ {
+ rtx signmask = emit_store_flag (result, LT, op0, const0_rtx,
+ mode, 0, -1);
+ if (signmask)
+ {
+ signmask = force_reg (mode, signmask);
+ mask = ((HOST_WIDE_INT) 1 << logd) - 1;
+ shift = GEN_INT (GET_MODE_BITSIZE (mode) - logd);
+
+ /* Use the rtx_cost of a LSHIFTRT instruction to determine
+ which instruction sequence to use. If logical right shifts
+ are expensive the use 2 XORs, 2 SUBs and an AND, otherwise
+ use a LSHIFTRT, 1 ADD, 1 SUB and an AND. */
+
+ temp = gen_rtx_LSHIFTRT (mode, result, shift);
+ if (lshr_optab->handlers[mode].insn_code == CODE_FOR_nothing
+ || rtx_cost (temp, SET) > COSTS_N_INSNS (2))
+ {
+ temp = expand_binop (mode, xor_optab, op0, signmask,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ temp = expand_binop (mode, sub_optab, temp, signmask,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ temp = expand_binop (mode, and_optab, temp, GEN_INT (mask),
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ temp = expand_binop (mode, xor_optab, temp, signmask,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ temp = expand_binop (mode, sub_optab, temp, signmask,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ }
+ else
+ {
+ signmask = expand_binop (mode, lshr_optab, signmask, shift,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ signmask = force_reg (mode, signmask);
+
+ temp = expand_binop (mode, add_optab, op0, signmask,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ temp = expand_binop (mode, and_optab, temp, GEN_INT (mask),
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ temp = expand_binop (mode, sub_optab, temp, signmask,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ }
+ return temp;
+ }
+ }
+
+ /* Mask contains the mode's signbit and the significant bits of the
+ modulus. By including the signbit in the operation, many targets
+ can avoid an explicit compare operation in the following comparison
+ against zero. */
+
+ mask = (HOST_WIDE_INT) -1 << (GET_MODE_BITSIZE (mode) - 1)
+ | (((HOST_WIDE_INT) 1 << logd) - 1);
+
+ temp = expand_binop (mode, and_optab, op0, GEN_INT (mask), result,
+ 1, OPTAB_LIB_WIDEN);
+ if (temp != result)
+ emit_move_insn (result, temp);
+
+ label = gen_label_rtx ();
+ do_cmp_and_jump (result, const0_rtx, GE, mode, label);
+
+ temp = expand_binop (mode, sub_optab, result, const1_rtx, result,
+ 0, OPTAB_LIB_WIDEN);
+ mask = (HOST_WIDE_INT) -1 << logd;
+ temp = expand_binop (mode, ior_optab, temp, GEN_INT (mask), result,
+ 1, OPTAB_LIB_WIDEN);
+ temp = expand_binop (mode, add_optab, temp, const1_rtx, result,
+ 0, OPTAB_LIB_WIDEN);
+ if (temp != result)
+ emit_move_insn (result, temp);
+ emit_label (label);
+ return result;
+}
+
+/* Expand signed division of OP0 by a power of two D in mode MODE.
+ This routine is only called for positive values of D. */
+
+static rtx
+expand_sdiv_pow2 (enum machine_mode mode, rtx op0, HOST_WIDE_INT d)
+{
+ rtx temp, label;
+ tree shift;
+ int logd;
+
+ logd = floor_log2 (d);
+ shift = build_int_2 (logd, 0);
+
+ if (d == 2 && BRANCH_COST >= 1)
+ {
+ temp = gen_reg_rtx (mode);
+ temp = emit_store_flag (temp, LT, op0, const0_rtx, mode, 0, 1);
+ temp = expand_binop (mode, add_optab, temp, op0, NULL_RTX,
+ 0, OPTAB_LIB_WIDEN);
+ return expand_shift (RSHIFT_EXPR, mode, temp, shift, NULL_RTX, 0);
+ }
+
+ if (BRANCH_COST >= 2)
+ {
+ int ushift = GET_MODE_BITSIZE (mode) - logd;
+
+ temp = gen_reg_rtx (mode);
+ temp = emit_store_flag (temp, LT, op0, const0_rtx, mode, 0, -1);
+ if (shift_cost[mode][ushift] > COSTS_N_INSNS (1))
+ temp = expand_binop (mode, and_optab, temp, GEN_INT (d - 1),
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+ else
+ temp = expand_shift (RSHIFT_EXPR, mode, temp,
+ build_int_2 (ushift, 0), NULL_RTX, 1);
+ temp = expand_binop (mode, add_optab, temp, op0, NULL_RTX,
+ 0, OPTAB_LIB_WIDEN);
+ return expand_shift (RSHIFT_EXPR, mode, temp, shift, NULL_RTX, 0);
+ }
+
+ label = gen_label_rtx ();
+ temp = copy_to_mode_reg (mode, op0);
+ do_cmp_and_jump (temp, const0_rtx, GE, mode, label);
+ expand_inc (temp, GEN_INT (d - 1));
+ emit_label (label);
+ return expand_shift (RSHIFT_EXPR, mode, temp, shift, NULL_RTX, 0);
+}