rtx compare_from_rtx (rtx op0, rtx op1, enum rtx_code code, int unsignedp, enum machine_mode mode, rtx size) { enum rtx_code ucode; rtx tem; /* If one operand is constant, make it the second one. Only do this if the other operand is not constant as well. */ if (swap_commutative_operands_p (op0, op1)) { tem = op0; op0 = op1; op1 = tem; code = swap_condition (code); } if (flag_force_mem) { op0 = force_not_mem (op0); op1 = force_not_mem (op1); } do_pending_stack_adjust (); ucode = unsignedp ? unsigned_condition (code) : code; if ((tem = simplify_relational_operation (ucode, mode, op0, op1)) != 0) return tem; #if 0 /* There's no need to do this now that combine.c can eliminate lots of sign extensions. This can be less efficient in certain cases on other machines. */ /* If this is a signed equality comparison, we can do it as an unsigned comparison since zero-extension is cheaper than sign extension and comparisons with zero are done as unsigned. This is the case even on machines that can do fast sign extension, since zero-extension is easier to combine with other operations than sign-extension is. If we are comparing against a constant, we must convert it to what it would look like unsigned. */ if ((code == EQ || code == NE) && ! unsignedp && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT) { if (GET_CODE (op1) == CONST_INT && (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0))) != INTVAL (op1)) op1 = GEN_INT (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0))); unsignedp = 1; } #endif emit_cmp_insn (op0, op1, code, size, mode, unsignedp); #if HAVE_cc0 return gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx); #else return gen_rtx_fmt_ee (code, VOIDmode, op0, op1); #endif }
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; }
/* Function to check if 'btgl' instruction can be used with IVAL. */ bool nds32_can_use_btgl_p (HOST_WIDE_INT ival) { int one_bit_count; unsigned HOST_WIDE_INT mask = GET_MODE_MASK (SImode); /* Caculate the number of 1-bit of ival, if there is only one 1-bit, it is ok to perform 'btgl' operation. */ one_bit_count = popcount_hwi ((unsigned HOST_WIDE_INT) (ival) & mask); /* 'btgl' is a performance extension instruction. */ return (TARGET_EXT_PERF && (one_bit_count == 1)); }
/* Function to check if 'bclr' instruction can be used with IVAL. */ bool nds32_can_use_bclr_p (HOST_WIDE_INT ival) { int one_bit_count; unsigned HOST_WIDE_INT mask = GET_MODE_MASK (SImode); /* Calculate the number of 1-bit of (~ival), if there is only one 1-bit, it means the original ival has only one 0-bit, So it is ok to perform 'bclr' operation. */ one_bit_count = popcount_hwi ((unsigned HOST_WIDE_INT) (~ival) & mask); /* 'bclr' is a performance extension instruction. */ return (TARGET_EXT_PERF && (one_bit_count == 1)); }
static bool update_reg_equal_equiv_notes (rtx insn, enum machine_mode new_mode, enum machine_mode old_mode, enum rtx_code code) { rtx *loc = ®_NOTES (insn); while (*loc) { enum reg_note kind = REG_NOTE_KIND (*loc); if (kind == REG_EQUAL || kind == REG_EQUIV) { rtx orig_src = XEXP (*loc, 0); /* Update equivalency constants. Recall that RTL constants are sign-extended. */ if (GET_CODE (orig_src) == CONST_INT && HOST_BITS_PER_WIDE_INT >= GET_MODE_BITSIZE (new_mode)) { if (INTVAL (orig_src) >= 0 || code == SIGN_EXTEND) /* Nothing needed. */; else { /* Zero-extend the negative constant by masking out the bits outside the source mode. */ rtx new_const_int = gen_int_mode (INTVAL (orig_src) & GET_MODE_MASK (old_mode), new_mode); if (!validate_change (insn, &XEXP (*loc, 0), new_const_int, true)) return false; } loc = &XEXP (*loc, 1); } /* Drop all other notes, they assume a wrong mode. */ else if (!validate_change (insn, loc, XEXP (*loc, 1), true)) return false; } else loc = &XEXP (*loc, 1); } return true; }
static bool combine_set_extension (ext_cand *cand, rtx curr_insn, rtx *orig_set) { rtx orig_src = SET_SRC (*orig_set); enum machine_mode orig_mode = GET_MODE (SET_DEST (*orig_set)); rtx new_reg = gen_rtx_REG (cand->mode, REGNO (SET_DEST (*orig_set))); rtx new_set; /* Merge constants by directly moving the constant into the register under some conditions. Recall that RTL constants are sign-extended. */ if (GET_CODE (orig_src) == CONST_INT && HOST_BITS_PER_WIDE_INT >= GET_MODE_BITSIZE (cand->mode)) { if (INTVAL (orig_src) >= 0 || cand->code == SIGN_EXTEND) new_set = gen_rtx_SET (VOIDmode, new_reg, orig_src); else { /* Zero-extend the negative constant by masking out the bits outside the source mode. */ rtx new_const_int = GEN_INT (INTVAL (orig_src) & GET_MODE_MASK (orig_mode)); new_set = gen_rtx_SET (VOIDmode, new_reg, new_const_int); } } else if (GET_MODE (orig_src) == VOIDmode) { /* This is mostly due to a call insn that should not be optimized. */ return false; } else if (GET_CODE (orig_src) == cand->code) { /* Here is a sequence of two extensions. Try to merge them. */ rtx temp_extension = gen_rtx_fmt_e (cand->code, cand->mode, XEXP (orig_src, 0)); rtx simplified_temp_extension = simplify_rtx (temp_extension); if (simplified_temp_extension) temp_extension = simplified_temp_extension; new_set = gen_rtx_SET (VOIDmode, new_reg, temp_extension); } else if (GET_CODE (orig_src) == IF_THEN_ELSE) { /* Only IF_THEN_ELSE of phi-type copies are combined. Otherwise, in general, IF_THEN_ELSE should not be combined. */ return false; } else { /* This is the normal case. */ rtx temp_extension = gen_rtx_fmt_e (cand->code, cand->mode, orig_src); rtx simplified_temp_extension = simplify_rtx (temp_extension); if (simplified_temp_extension) temp_extension = simplified_temp_extension; new_set = gen_rtx_SET (VOIDmode, new_reg, temp_extension); } /* This change is a part of a group of changes. Hence, validate_change will not try to commit the change. */ if (validate_change (curr_insn, orig_set, new_set, true) && update_reg_equal_equiv_notes (curr_insn, cand->mode, orig_mode, cand->code)) { if (dump_file) { fprintf (dump_file, "Tentatively merged extension with definition:\n"); print_rtl_single (dump_file, curr_insn); } return true; } return false; }
static bool combine_set_extension (ext_cand *cand, rtx_insn *curr_insn, rtx *orig_set) { rtx orig_src = SET_SRC (*orig_set); machine_mode orig_mode = GET_MODE (SET_DEST (*orig_set)); rtx new_set; rtx cand_pat = PATTERN (cand->insn); /* If the extension's source/destination registers are not the same then we need to change the original load to reference the destination of the extension. Then we need to emit a copy from that destination to the original destination of the load. */ rtx new_reg; bool copy_needed = (REGNO (SET_DEST (cand_pat)) != REGNO (XEXP (SET_SRC (cand_pat), 0))); if (copy_needed) new_reg = gen_rtx_REG (cand->mode, REGNO (SET_DEST (cand_pat))); else new_reg = gen_rtx_REG (cand->mode, REGNO (SET_DEST (*orig_set))); #if 0 /* Rethinking test. Temporarily disabled. */ /* We're going to be widening the result of DEF_INSN, ensure that doing so doesn't change the number of hard registers needed for the result. */ if (HARD_REGNO_NREGS (REGNO (new_reg), cand->mode) != HARD_REGNO_NREGS (REGNO (SET_DEST (*orig_set)), GET_MODE (SET_DEST (*orig_set)))) return false; #endif /* Merge constants by directly moving the constant into the register under some conditions. Recall that RTL constants are sign-extended. */ if (GET_CODE (orig_src) == CONST_INT && HOST_BITS_PER_WIDE_INT >= GET_MODE_BITSIZE (cand->mode)) { if (INTVAL (orig_src) >= 0 || cand->code == SIGN_EXTEND) new_set = gen_rtx_SET (new_reg, orig_src); else { /* Zero-extend the negative constant by masking out the bits outside the source mode. */ rtx new_const_int = gen_int_mode (INTVAL (orig_src) & GET_MODE_MASK (orig_mode), GET_MODE (new_reg)); new_set = gen_rtx_SET (new_reg, new_const_int); } } else if (GET_MODE (orig_src) == VOIDmode) { /* This is mostly due to a call insn that should not be optimized. */ return false; } else if (GET_CODE (orig_src) == cand->code) { /* Here is a sequence of two extensions. Try to merge them. */ rtx temp_extension = gen_rtx_fmt_e (cand->code, cand->mode, XEXP (orig_src, 0)); rtx simplified_temp_extension = simplify_rtx (temp_extension); if (simplified_temp_extension) temp_extension = simplified_temp_extension; new_set = gen_rtx_SET (new_reg, temp_extension); } else if (GET_CODE (orig_src) == IF_THEN_ELSE) { /* Only IF_THEN_ELSE of phi-type copies are combined. Otherwise, in general, IF_THEN_ELSE should not be combined. */ return false; } else { /* This is the normal case. */ rtx temp_extension = gen_rtx_fmt_e (cand->code, cand->mode, orig_src); rtx simplified_temp_extension = simplify_rtx (temp_extension); if (simplified_temp_extension) temp_extension = simplified_temp_extension; new_set = gen_rtx_SET (new_reg, temp_extension); } /* This change is a part of a group of changes. Hence, validate_change will not try to commit the change. */ if (validate_change (curr_insn, orig_set, new_set, true) && update_reg_equal_equiv_notes (curr_insn, cand->mode, orig_mode, cand->code)) { if (dump_file) { fprintf (dump_file, "Tentatively merged extension with definition %s:\n", (copy_needed) ? "(copy needed)" : ""); print_rtl_single (dump_file, curr_insn); } return true; } return false; }
void do_compare_rtx_and_jump (rtx op0, rtx op1, enum rtx_code code, int unsignedp, enum machine_mode mode, rtx size, rtx if_false_label, rtx if_true_label) { enum rtx_code ucode; rtx tem; int dummy_true_label = 0; /* Reverse the comparison if that is safe and we want to jump if it is false. */ if (! if_true_label && ! FLOAT_MODE_P (mode)) { if_true_label = if_false_label; if_false_label = 0; code = reverse_condition (code); } /* If one operand is constant, make it the second one. Only do this if the other operand is not constant as well. */ if (swap_commutative_operands_p (op0, op1)) { tem = op0; op0 = op1; op1 = tem; code = swap_condition (code); } if (flag_force_mem) { op0 = force_not_mem (op0); op1 = force_not_mem (op1); } do_pending_stack_adjust (); ucode = unsignedp ? unsigned_condition (code) : code; if ((tem = simplify_relational_operation (ucode, mode, op0, op1)) != 0) { if (tem == const_true_rtx) { if (if_true_label) emit_jump (if_true_label); } else { if (if_false_label) emit_jump (if_false_label); } return; } #if 0 /* There's no need to do this now that combine.c can eliminate lots of sign extensions. This can be less efficient in certain cases on other machines. */ /* If this is a signed equality comparison, we can do it as an unsigned comparison since zero-extension is cheaper than sign extension and comparisons with zero are done as unsigned. This is the case even on machines that can do fast sign extension, since zero-extension is easier to combine with other operations than sign-extension is. If we are comparing against a constant, we must convert it to what it would look like unsigned. */ if ((code == EQ || code == NE) && ! unsignedp && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT) { if (GET_CODE (op1) == CONST_INT && (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0))) != INTVAL (op1)) op1 = GEN_INT (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0))); unsignedp = 1; } #endif if (! if_true_label) { dummy_true_label = 1; if_true_label = gen_label_rtx (); } emit_cmp_and_jump_insns (op0, op1, code, size, mode, unsignedp, if_true_label); if (if_false_label) emit_jump (if_false_label); if (dummy_true_label) emit_label (if_true_label); }
static bool nds32_expand_setmem_unroll (rtx dstmem, rtx size, rtx value, rtx align ATTRIBUTE_UNUSED, rtx expected_align ATTRIBUTE_UNUSED, rtx expected_size ATTRIBUTE_UNUSED) { unsigned maximum_regs, maximum_bytes, start_regno, regno; rtx value4word; rtx dst_base_reg, new_base_reg; unsigned HOST_WIDE_INT remain_bytes, remain_words, prepare_regs, fill_per_smw; unsigned HOST_WIDE_INT real_size; if (TARGET_REDUCED_REGS) { maximum_regs = 4; maximum_bytes = 64; start_regno = 2; } else { maximum_regs = 8; maximum_bytes = 128; start_regno = 16; } real_size = UINTVAL (size) & GET_MODE_MASK(SImode); if (!(CONST_INT_P (size) && real_size <= maximum_bytes)) return false; remain_bytes = real_size; gcc_assert (GET_MODE (value) == QImode || CONST_INT_P (value)); value4word = nds32_gen_dup_4_byte_to_word_value (value); prepare_regs = remain_bytes / UNITS_PER_WORD; dst_base_reg = copy_to_mode_reg (SImode, XEXP (dstmem, 0)); if (prepare_regs > maximum_regs) prepare_regs = maximum_regs; fill_per_smw = prepare_regs * UNITS_PER_WORD; regno = start_regno; switch (prepare_regs) { case 2: default: { rtx reg0 = gen_rtx_REG (SImode, regno); rtx reg1 = gen_rtx_REG (SImode, regno+1); unsigned last_regno = start_regno + prepare_regs - 1; emit_move_insn (reg0, value4word); emit_move_insn (reg1, value4word); rtx regd = gen_rtx_REG (DImode, regno); regno += 2; /* Try to utilize movd44! */ while (regno <= last_regno) { if ((regno + 1) <=last_regno) { rtx reg = gen_rtx_REG (DImode, regno); emit_move_insn (reg, regd); regno += 2; } else { rtx reg = gen_rtx_REG (SImode, regno); emit_move_insn (reg, reg0); regno += 1; } } break; } case 1: { rtx reg = gen_rtx_REG (SImode, regno++); emit_move_insn (reg, value4word); } break; case 0: break; } if (fill_per_smw) for (;remain_bytes >= fill_per_smw;remain_bytes -= fill_per_smw) { emit_insn (nds32_expand_store_multiple (start_regno, prepare_regs, dst_base_reg, dstmem, true, &new_base_reg)); dst_base_reg = new_base_reg; dstmem = gen_rtx_MEM (SImode, dst_base_reg); } remain_words = remain_bytes / UNITS_PER_WORD; if (remain_words) { emit_insn (nds32_expand_store_multiple (start_regno, remain_words, dst_base_reg, dstmem, true, &new_base_reg)); dst_base_reg = new_base_reg; dstmem = gen_rtx_MEM (SImode, dst_base_reg); } remain_bytes = remain_bytes - (remain_words * UNITS_PER_WORD); if (remain_bytes) { value = simplify_gen_subreg (QImode, value4word, SImode, subreg_lowpart_offset(QImode, SImode)); int offset = 0; for (;remain_bytes;--remain_bytes, ++offset) { nds32_emit_load_store (value, dstmem, QImode, offset, false); } } return true; }
static int reload_cse_simplify_set (rtx set, rtx insn) { int did_change = 0; int dreg; rtx src; enum reg_class dclass; int old_cost; cselib_val *val; struct elt_loc_list *l; #ifdef LOAD_EXTEND_OP enum rtx_code extend_op = UNKNOWN; #endif bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn)); dreg = true_regnum (SET_DEST (set)); if (dreg < 0) return 0; src = SET_SRC (set); if (side_effects_p (src) || true_regnum (src) >= 0) return 0; dclass = REGNO_REG_CLASS (dreg); #ifdef LOAD_EXTEND_OP /* When replacing a memory with a register, we need to honor assumptions that combine made wrt the contents of sign bits. We'll do this by generating an extend instruction instead of a reg->reg copy. Thus the destination must be a register that we can widen. */ if (MEM_P (src) && GET_MODE_BITSIZE (GET_MODE (src)) < BITS_PER_WORD && (extend_op = LOAD_EXTEND_OP (GET_MODE (src))) != UNKNOWN && !REG_P (SET_DEST (set))) return 0; #endif val = cselib_lookup (src, GET_MODE (SET_DEST (set)), 0); if (! val) return 0; /* If memory loads are cheaper than register copies, don't change them. */ if (MEM_P (src)) old_cost = MEMORY_MOVE_COST (GET_MODE (src), dclass, 1); else if (REG_P (src)) old_cost = REGISTER_MOVE_COST (GET_MODE (src), REGNO_REG_CLASS (REGNO (src)), dclass); else old_cost = rtx_cost (src, SET, speed); for (l = val->locs; l; l = l->next) { rtx this_rtx = l->loc; int this_cost; if (CONSTANT_P (this_rtx) && ! references_value_p (this_rtx, 0)) { #ifdef LOAD_EXTEND_OP if (extend_op != UNKNOWN) { HOST_WIDE_INT this_val; /* ??? I'm lazy and don't wish to handle CONST_DOUBLE. Other constants, such as SYMBOL_REF, cannot be extended. */ if (GET_CODE (this_rtx) != CONST_INT) continue; this_val = INTVAL (this_rtx); switch (extend_op) { case ZERO_EXTEND: this_val &= GET_MODE_MASK (GET_MODE (src)); break; case SIGN_EXTEND: /* ??? In theory we're already extended. */ if (this_val == trunc_int_for_mode (this_val, GET_MODE (src))) break; default: gcc_unreachable (); } this_rtx = GEN_INT (this_val); } #endif this_cost = rtx_cost (this_rtx, SET, speed); } else if (REG_P (this_rtx)) { #ifdef LOAD_EXTEND_OP if (extend_op != UNKNOWN) { this_rtx = gen_rtx_fmt_e (extend_op, word_mode, this_rtx); this_cost = rtx_cost (this_rtx, SET, speed); } else #endif this_cost = REGISTER_MOVE_COST (GET_MODE (this_rtx), REGNO_REG_CLASS (REGNO (this_rtx)), dclass); } else continue; /* If equal costs, prefer registers over anything else. That tends to lead to smaller instructions on some machines. */ if (this_cost < old_cost || (this_cost == old_cost && REG_P (this_rtx) && !REG_P (SET_SRC (set)))) { #ifdef LOAD_EXTEND_OP if (GET_MODE_BITSIZE (GET_MODE (SET_DEST (set))) < BITS_PER_WORD && extend_op != UNKNOWN #ifdef CANNOT_CHANGE_MODE_CLASS && !CANNOT_CHANGE_MODE_CLASS (GET_MODE (SET_DEST (set)), word_mode, REGNO_REG_CLASS (REGNO (SET_DEST (set)))) #endif ) { rtx wide_dest = gen_rtx_REG (word_mode, REGNO (SET_DEST (set))); ORIGINAL_REGNO (wide_dest) = ORIGINAL_REGNO (SET_DEST (set)); validate_change (insn, &SET_DEST (set), wide_dest, 1); } #endif validate_unshare_change (insn, &SET_SRC (set), this_rtx, 1); old_cost = this_cost, did_change = 1; } } return did_change; }