bool nds32_expand_strlen (rtx result, rtx str, rtx target_char, rtx align ATTRIBUTE_UNUSED) { rtx base_reg, backup_base_reg; rtx ffb_result; rtx target_char_ptr, length; rtx loop_label, tmp; if (optimize_size || optimize < 3) return false; gcc_assert (MEM_P (str)); gcc_assert (CONST_INT_P (target_char) || REG_P (target_char)); base_reg = copy_to_mode_reg (SImode, XEXP (str, 0)); loop_label = gen_label_rtx (); ffb_result = gen_reg_rtx (Pmode); tmp = gen_reg_rtx (SImode); backup_base_reg = gen_reg_rtx (SImode); /* Emit loop version of strlen. move $backup_base, $base .Lloop: lmw.bim $tmp, [$base], $tmp, 0 ffb $ffb_result, $tmp, $target_char ! is there $target_char? beqz $ffb_result, .Lloop add $last_char_ptr, $base, $ffb_result sub $length, $last_char_ptr, $backup_base */ /* move $backup_base, $base */ emit_move_insn (backup_base_reg, base_reg); /* .Lloop: */ emit_label (loop_label); /* lmw.bim $tmp, [$base], $tmp, 0 */ emit_insn (gen_unaligned_load_update_base_w (base_reg, tmp, base_reg)); /* ffb $ffb_result, $tmp, $target_char ! is there $target_char? */ emit_insn (gen_unspec_ffb (ffb_result, tmp, target_char)); /* beqz $ffb_result, .Lloop */ emit_cmp_and_jump_insns (ffb_result, const0_rtx, EQ, NULL, SImode, 1, loop_label); /* add $target_char_ptr, $base, $ffb_result */ target_char_ptr = expand_binop (Pmode, add_optab, base_reg, ffb_result, NULL_RTX, 0, OPTAB_WIDEN); /* sub $length, $target_char_ptr, $backup_base */ length = expand_binop (Pmode, sub_optab, target_char_ptr, backup_base_reg, NULL_RTX, 0, OPTAB_WIDEN); emit_move_insn (result, length); return true; }
static rtx nds32_gen_dup_4_byte_to_word_value_aux (rtx value, rtx value4word) { gcc_assert (GET_MODE (value) == QImode || CONST_INT_P (value)); if (CONST_INT_P (value)) { unsigned HOST_WIDE_INT val = UINTVAL (value) & GET_MODE_MASK(QImode); rtx new_val = gen_int_mode (val | (val << 8) | (val << 16) | (val << 24), SImode); /* Just calculate at here if it's constant value. */ emit_move_insn (value4word, new_val); } else { if (NDS32_EXT_DSP_P ()) { /* ! prepare word insb $tmp, $value, 1 ! $tmp <- 0x0000abab pkbb16 $tmp6, $tmp2, $tmp2 ! $value4word <- 0xabababab */ rtx tmp = gen_reg_rtx (SImode); convert_move (tmp, value, true); emit_insn ( gen_insvsi_internal (tmp, gen_int_mode (0x8, SImode), tmp)); emit_insn (gen_pkbbsi_1 (value4word, tmp, tmp)); } else { /* ! prepare word andi $tmp1, $value, 0xff ! $tmp1 <- 0x000000ab slli $tmp2, $tmp1, 8 ! $tmp2 <- 0x0000ab00 or $tmp3, $tmp1, $tmp2 ! $tmp3 <- 0x0000abab slli $tmp4, $tmp3, 16 ! $tmp4 <- 0xabab0000 or $val4word, $tmp3, $tmp4 ! $value4word <- 0xabababab */ rtx tmp1, tmp2, tmp3, tmp4; tmp1 = expand_binop (SImode, and_optab, value, gen_int_mode (0xff, SImode), NULL_RTX, 0, OPTAB_WIDEN); tmp2 = expand_binop (SImode, ashl_optab, tmp1, gen_int_mode (8, SImode), NULL_RTX, 0, OPTAB_WIDEN); tmp3 = expand_binop (SImode, ior_optab, tmp1, tmp2, NULL_RTX, 0, OPTAB_WIDEN); tmp4 = expand_binop (SImode, ashl_optab, tmp3, gen_int_mode (16, SImode), NULL_RTX, 0, OPTAB_WIDEN); emit_insn (gen_iorsi3 (value4word, tmp3, tmp4)); } } return value4word; }
static rtx emit_setmem_doubleword_loop (rtx itr, rtx size, rtx value) { rtx word_mode_label = gen_label_rtx (); rtx word_mode_end_label = gen_label_rtx (); rtx byte_mode_size = gen_reg_rtx (SImode); rtx byte_mode_size_tmp = gen_reg_rtx (SImode); rtx word_mode_end = gen_reg_rtx (SImode); rtx size_for_word = gen_reg_rtx (SImode); /* and $size_for_word, $size, #~0x7 */ size_for_word = expand_binop (SImode, and_optab, size, gen_int_mode (~0x7, SImode), NULL_RTX, 0, OPTAB_WIDEN); emit_move_insn (byte_mode_size, size); /* beqz $size_for_word, .Lbyte_mode_entry */ emit_cmp_and_jump_insns (size_for_word, const0_rtx, EQ, NULL, SImode, 1, word_mode_end_label); /* add $word_mode_end, $dst, $size_for_word */ word_mode_end = expand_binop (Pmode, add_optab, itr, size_for_word, NULL_RTX, 0, OPTAB_WIDEN); /* andi $byte_mode_size, $size, 0x7 */ byte_mode_size_tmp = expand_binop (SImode, and_optab, size, GEN_INT (0x7), NULL_RTX, 0, OPTAB_WIDEN); emit_move_insn (byte_mode_size, byte_mode_size_tmp); /* .Lword_mode: */ emit_label (word_mode_label); /* ! word-mode set loop smw.bim $value4word, [$dst_itr], $value4word, 0 bne $word_mode_end, $dst_itr, .Lword_mode */ emit_insn (gen_unaligned_store_update_base_dw (itr, itr, value)); emit_cmp_and_jump_insns (word_mode_end, itr, NE, NULL, Pmode, 1, word_mode_label); emit_label (word_mode_end_label); return byte_mode_size; }
static rtx emit_setmem_byte_loop (rtx itr, rtx size, rtx value, bool need_end) { rtx end = gen_reg_rtx (Pmode); rtx byte_mode_label = gen_label_rtx (); rtx end_label = gen_label_rtx (); value = force_reg (QImode, value); if (need_end) end = expand_binop (Pmode, add_optab, itr, size, NULL_RTX, 0, OPTAB_WIDEN); /* beqz $byte_mode_size, .Lend add $byte_mode_end, $dst_itr, $byte_mode_size */ emit_cmp_and_jump_insns (size, const0_rtx, EQ, NULL, SImode, 1, end_label); if (!need_end) end = expand_binop (Pmode, add_optab, itr, size, NULL_RTX, 0, OPTAB_WIDEN); /* .Lbyte_mode: */ emit_label (byte_mode_label); /* ! byte-mode set loop sbi.bi $value, [$dst_itr] ,1 bne $byte_mode_end, $dst_itr, .Lbyte_mode */ nds32_emit_post_inc_load_store (value, itr, QImode, false); emit_cmp_and_jump_insns (end, itr, NE, NULL, Pmode, 1, byte_mode_label); /* .Lend: */ emit_label (end_label); if (need_end) return end; else return NULL_RTX; }
static void do_jump_by_parts_zero_rtx (enum machine_mode mode, rtx op0, rtx if_false_label, rtx if_true_label) { int nwords = GET_MODE_SIZE (mode) / UNITS_PER_WORD; rtx part; int i; rtx drop_through_label = 0; /* The fastest way of doing this comparison on almost any machine is to "or" all the words and compare the result. If all have to be loaded from memory and this is a very wide item, it's possible this may be slower, but that's highly unlikely. */ part = gen_reg_rtx (word_mode); emit_move_insn (part, operand_subword_force (op0, 0, GET_MODE (op0))); for (i = 1; i < nwords && part != 0; i++) part = expand_binop (word_mode, ior_optab, part, operand_subword_force (op0, i, GET_MODE (op0)), part, 1, OPTAB_WIDEN); if (part != 0) { do_compare_rtx_and_jump (part, const0_rtx, EQ, 1, word_mode, NULL_RTX, if_false_label, if_true_label); return; } /* If we couldn't do the "or" simply, do this with a series of compares. */ if (! if_false_label) drop_through_label = if_false_label = gen_label_rtx (); for (i = 0; i < nwords; i++) do_compare_rtx_and_jump (operand_subword_force (op0, i, GET_MODE (op0)), const0_rtx, EQ, 1, word_mode, NULL_RTX, if_false_label, NULL_RTX); if (if_true_label) emit_jump (if_true_label); if (drop_through_label) emit_label (drop_through_label); }
void ubsan_expand_si_overflow_addsub_check (tree_code code, gimple stmt) { rtx res, op0, op1; tree lhs, fn, arg0, arg1; rtx_code_label *done_label, *do_error; rtx target = NULL_RTX; lhs = gimple_call_lhs (stmt); arg0 = gimple_call_arg (stmt, 0); arg1 = gimple_call_arg (stmt, 1); done_label = gen_label_rtx (); do_error = gen_label_rtx (); do_pending_stack_adjust (); op0 = expand_normal (arg0); op1 = expand_normal (arg1); machine_mode mode = TYPE_MODE (TREE_TYPE (arg0)); if (lhs) target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE); enum insn_code icode = optab_handler (code == PLUS_EXPR ? addv4_optab : subv4_optab, mode); if (icode != CODE_FOR_nothing) { struct expand_operand ops[4]; rtx_insn *last = get_last_insn (); res = gen_reg_rtx (mode); create_output_operand (&ops[0], res, mode); create_input_operand (&ops[1], op0, mode); create_input_operand (&ops[2], op1, mode); create_fixed_operand (&ops[3], do_error); if (maybe_expand_insn (icode, 4, ops)) { last = get_last_insn (); if (profile_status_for_fn (cfun) != PROFILE_ABSENT && JUMP_P (last) && any_condjump_p (last) && !find_reg_note (last, REG_BR_PROB, 0)) add_int_reg_note (last, REG_BR_PROB, PROB_VERY_UNLIKELY); emit_jump (done_label); } else { delete_insns_since (last); icode = CODE_FOR_nothing; } } if (icode == CODE_FOR_nothing) { rtx_code_label *sub_check = gen_label_rtx (); int pos_neg = 3; /* Compute the operation. On RTL level, the addition is always unsigned. */ res = expand_binop (mode, code == PLUS_EXPR ? add_optab : sub_optab, op0, op1, NULL_RTX, false, OPTAB_LIB_WIDEN); /* If we can prove one of the arguments (for MINUS_EXPR only the second operand, as subtraction is not commutative) is always non-negative or always negative, we can do just one comparison and conditional jump instead of 2 at runtime, 3 present in the emitted code. If one of the arguments is CONST_INT, all we need is to make sure it is op1, then the first emit_cmp_and_jump_insns will be just folded. Otherwise try to use range info if available. */ if (code == PLUS_EXPR && CONST_INT_P (op0)) { rtx tem = op0; op0 = op1; op1 = tem; } else if (CONST_INT_P (op1)) ; else if (code == PLUS_EXPR && TREE_CODE (arg0) == SSA_NAME) { wide_int arg0_min, arg0_max; if (get_range_info (arg0, &arg0_min, &arg0_max) == VR_RANGE) { if (!wi::neg_p (arg0_min, TYPE_SIGN (TREE_TYPE (arg0)))) pos_neg = 1; else if (wi::neg_p (arg0_max, TYPE_SIGN (TREE_TYPE (arg0)))) pos_neg = 2; } if (pos_neg != 3) { rtx tem = op0; op0 = op1; op1 = tem; } } if (pos_neg == 3 && !CONST_INT_P (op1) && TREE_CODE (arg1) == SSA_NAME) { wide_int arg1_min, arg1_max; if (get_range_info (arg1, &arg1_min, &arg1_max) == VR_RANGE) { if (!wi::neg_p (arg1_min, TYPE_SIGN (TREE_TYPE (arg1)))) pos_neg = 1; else if (wi::neg_p (arg1_max, TYPE_SIGN (TREE_TYPE (arg1)))) pos_neg = 2; } } /* If the op1 is negative, we have to use a different check. */ if (pos_neg == 3) emit_cmp_and_jump_insns (op1, const0_rtx, LT, NULL_RTX, mode, false, sub_check, PROB_EVEN); /* Compare the result of the operation with one of the operands. */ if (pos_neg & 1) emit_cmp_and_jump_insns (res, op0, code == PLUS_EXPR ? GE : LE, NULL_RTX, mode, false, done_label, PROB_VERY_LIKELY); /* If we get here, we have to print the error. */ if (pos_neg == 3) { emit_jump (do_error); emit_label (sub_check); } /* We have k = a + b for b < 0 here. k <= a must hold. */ if (pos_neg & 2) emit_cmp_and_jump_insns (res, op0, code == PLUS_EXPR ? LE : GE, NULL_RTX, mode, false, done_label, PROB_VERY_LIKELY); } emit_label (do_error); /* Expand the ubsan builtin call. */ push_temp_slots (); fn = ubsan_build_overflow_builtin (code, gimple_location (stmt), TREE_TYPE (arg0), arg0, arg1); expand_normal (fn); pop_temp_slots (); do_pending_stack_adjust (); /* We're done. */ emit_label (done_label); if (lhs) emit_move_insn (target, res); }
static bool nds32_expand_movmemsi_loop_known_size (rtx dstmem, rtx srcmem, rtx size, rtx alignment) { rtx dst_base_reg, src_base_reg; rtx dst_itr, src_itr; rtx dstmem_m, srcmem_m, dst_itr_m, src_itr_m; rtx dst_end; rtx double_word_mode_loop, byte_mode_loop; rtx tmp; int start_regno; bool align_to_4_bytes = (INTVAL (alignment) & 3) == 0; unsigned HOST_WIDE_INT total_bytes = UINTVAL (size); if (TARGET_ISA_V3M && !align_to_4_bytes) return 0; if (TARGET_REDUCED_REGS) start_regno = 2; else start_regno = 16; dst_itr = gen_reg_rtx (Pmode); src_itr = gen_reg_rtx (Pmode); dst_end = gen_reg_rtx (Pmode); tmp = gen_reg_rtx (QImode); double_word_mode_loop = gen_label_rtx (); byte_mode_loop = gen_label_rtx (); dst_base_reg = copy_to_mode_reg (Pmode, XEXP (dstmem, 0)); src_base_reg = copy_to_mode_reg (Pmode, XEXP (srcmem, 0)); if (total_bytes < 8) { /* Emit total_bytes less than 8 loop version of movmem. add $dst_end, $dst, $size move $dst_itr, $dst .Lbyte_mode_loop: lbi.bi $tmp, [$src_itr], #1 sbi.bi $tmp, [$dst_itr], #1 ! Not readch upper bound. Loop. bne $dst_itr, $dst_end, .Lbyte_mode_loop */ /* add $dst_end, $dst, $size */ dst_end = expand_binop (Pmode, add_optab, dst_base_reg, size, NULL_RTX, 0, OPTAB_WIDEN); /* move $dst_itr, $dst move $src_itr, $src */ emit_move_insn (dst_itr, dst_base_reg); emit_move_insn (src_itr, src_base_reg); /* .Lbyte_mode_loop: */ emit_label (byte_mode_loop); /* lbi.bi $tmp, [$src_itr], #1 */ nds32_emit_post_inc_load_store (tmp, src_itr, QImode, true); /* sbi.bi $tmp, [$dst_itr], #1 */ nds32_emit_post_inc_load_store (tmp, dst_itr, QImode, false); /* ! Not readch upper bound. Loop. bne $dst_itr, $dst_end, .Lbyte_mode_loop */ emit_cmp_and_jump_insns (dst_itr, dst_end, NE, NULL, SImode, 1, byte_mode_loop); return true; } else if (total_bytes % 8 == 0) { /* Emit multiple of 8 loop version of movmem. add $dst_end, $dst, $size move $dst_itr, $dst move $src_itr, $src .Ldouble_word_mode_loop: lmw.bim $tmp-begin, [$src_itr], $tmp-end, #0 ! $src_itr' = $src_itr smw.bim $tmp-begin, [$dst_itr], $tmp-end, #0 ! $dst_itr' = $dst_itr ! move will delete after register allocation move $src_itr, $src_itr' move $dst_itr, $dst_itr' ! Not readch upper bound. Loop. bne $double_word_end, $dst_itr, .Ldouble_word_mode_loop */ /* add $dst_end, $dst, $size */ dst_end = expand_binop (Pmode, add_optab, dst_base_reg, size, NULL_RTX, 0, OPTAB_WIDEN); /* move $dst_itr, $dst move $src_itr, $src */ emit_move_insn (dst_itr, dst_base_reg); emit_move_insn (src_itr, src_base_reg); /* .Ldouble_word_mode_loop: */ emit_label (double_word_mode_loop); /* lmw.bim $tmp-begin, [$src_itr], $tmp-end, #0 ! $src_itr' = $src_itr smw.bim $tmp-begin, [$dst_itr], $tmp-end, #0 ! $dst_itr' = $dst_itr */ src_itr_m = src_itr; dst_itr_m = dst_itr; srcmem_m = srcmem; dstmem_m = dstmem; nds32_emit_mem_move_block (start_regno, 2, &dst_itr_m, &dstmem_m, &src_itr_m, &srcmem_m, true); /* move $src_itr, $src_itr' move $dst_itr, $dst_itr' */ emit_move_insn (dst_itr, dst_itr_m); emit_move_insn (src_itr, src_itr_m); /* ! Not readch upper bound. Loop. bne $double_word_end, $dst_itr, .Ldouble_word_mode_loop */ emit_cmp_and_jump_insns (dst_end, dst_itr, NE, NULL, Pmode, 1, double_word_mode_loop); } else { /* Handle size greater than 8, and not a multiple of 8. */ return nds32_expand_movmemsi_loop_unknown_size (dstmem, srcmem, size, alignment); } return true; }
static bool nds32_expand_movmemsi_loop_unknown_size (rtx dstmem, rtx srcmem, rtx size, rtx alignment) { /* Emit loop version of movmem. andi $size_least_3_bit, $size, #~7 add $dst_end, $dst, $size move $dst_itr, $dst move $src_itr, $src beqz $size_least_3_bit, .Lbyte_mode_entry ! Not large enough. add $double_word_end, $dst, $size_least_3_bit .Ldouble_word_mode_loop: lmw.bim $tmp-begin, [$src_itr], $tmp-end, #0 ! $src_itr' = $src_itr smw.bim $tmp-begin, [$dst_itr], $tmp-end, #0 ! $dst_itr' = $dst_itr ! move will delete after register allocation move $src_itr, $src_itr' move $dst_itr, $dst_itr' ! Not readch upper bound. Loop. bne $double_word_end, $dst_itr, .Ldouble_word_mode_loop .Lbyte_mode_entry: beq $dst_itr, $dst_end, .Lend_label .Lbyte_mode_loop: lbi.bi $tmp, [$src_itr], #1 sbi.bi $tmp, [$dst_itr], #1 ! Not readch upper bound. Loop. bne $dst_itr, $dst_end, .Lbyte_mode_loop .Lend_label: */ rtx dst_base_reg, src_base_reg; rtx dst_itr, src_itr; rtx dstmem_m, srcmem_m, dst_itr_m, src_itr_m; rtx dst_end; rtx size_least_3_bit; rtx double_word_end; rtx double_word_mode_loop, byte_mode_entry, byte_mode_loop, end_label; rtx tmp; rtx mask_least_3_bit; int start_regno; bool align_to_4_bytes = (INTVAL (alignment) & 3) == 0; if (TARGET_ISA_V3M && !align_to_4_bytes) return 0; if (TARGET_REDUCED_REGS) start_regno = 2; else start_regno = 16; dst_itr = gen_reg_rtx (Pmode); src_itr = gen_reg_rtx (Pmode); dst_end = gen_reg_rtx (Pmode); tmp = gen_reg_rtx (QImode); mask_least_3_bit = GEN_INT (~7); double_word_mode_loop = gen_label_rtx (); byte_mode_entry = gen_label_rtx (); byte_mode_loop = gen_label_rtx (); end_label = gen_label_rtx (); dst_base_reg = copy_to_mode_reg (Pmode, XEXP (dstmem, 0)); src_base_reg = copy_to_mode_reg (Pmode, XEXP (srcmem, 0)); /* andi $size_least_3_bit, $size, #~7 */ size_least_3_bit = expand_binop (SImode, and_optab, size, mask_least_3_bit, NULL_RTX, 0, OPTAB_WIDEN); /* add $dst_end, $dst, $size */ dst_end = expand_binop (Pmode, add_optab, dst_base_reg, size, NULL_RTX, 0, OPTAB_WIDEN); /* move $dst_itr, $dst move $src_itr, $src */ emit_move_insn (dst_itr, dst_base_reg); emit_move_insn (src_itr, src_base_reg); /* beqz $size_least_3_bit, .Lbyte_mode_entry ! Not large enough. */ emit_cmp_and_jump_insns (size_least_3_bit, const0_rtx, EQ, NULL, SImode, 1, byte_mode_entry); /* add $double_word_end, $dst, $size_least_3_bit */ double_word_end = expand_binop (Pmode, add_optab, dst_base_reg, size_least_3_bit, NULL_RTX, 0, OPTAB_WIDEN); /* .Ldouble_word_mode_loop: */ emit_label (double_word_mode_loop); /* lmw.bim $tmp-begin, [$src_itr], $tmp-end, #0 ! $src_itr' = $src_itr smw.bim $tmp-begin, [$dst_itr], $tmp-end, #0 ! $dst_itr' = $dst_itr */ src_itr_m = src_itr; dst_itr_m = dst_itr; srcmem_m = srcmem; dstmem_m = dstmem; nds32_emit_mem_move_block (start_regno, 2, &dst_itr_m, &dstmem_m, &src_itr_m, &srcmem_m, true); /* move $src_itr, $src_itr' move $dst_itr, $dst_itr' */ emit_move_insn (dst_itr, dst_itr_m); emit_move_insn (src_itr, src_itr_m); /* ! Not readch upper bound. Loop. bne $double_word_end, $dst_itr, .Ldouble_word_mode_loop */ emit_cmp_and_jump_insns (double_word_end, dst_itr, NE, NULL, Pmode, 1, double_word_mode_loop); /* .Lbyte_mode_entry: */ emit_label (byte_mode_entry); /* beq $dst_itr, $dst_end, .Lend_label */ emit_cmp_and_jump_insns (dst_itr, dst_end, EQ, NULL, Pmode, 1, end_label); /* .Lbyte_mode_loop: */ emit_label (byte_mode_loop); /* lbi.bi $tmp, [$src_itr], #1 */ nds32_emit_post_inc_load_store (tmp, src_itr, QImode, true); /* sbi.bi $tmp, [$dst_itr], #1 */ nds32_emit_post_inc_load_store (tmp, dst_itr, QImode, false); /* ! Not readch upper bound. Loop. bne $dst_itr, $dst_end, .Lbyte_mode_loop */ emit_cmp_and_jump_insns (dst_itr, dst_end, NE, NULL, SImode, 1, byte_mode_loop); /* .Lend_label: */ emit_label (end_label); return true; }