static bool nds32_expand_setmem_loop_v3m (rtx dstmem, rtx size, rtx value) { rtx base_reg = copy_to_mode_reg (Pmode, XEXP (dstmem, 0)); rtx need_align_bytes = gen_reg_rtx (SImode); rtx last_2_bit = gen_reg_rtx (SImode); rtx byte_loop_base = gen_reg_rtx (SImode); rtx byte_loop_size = gen_reg_rtx (SImode); rtx remain_size = gen_reg_rtx (SImode); rtx new_base_reg; rtx value4byte, value4doubleword; rtx byte_mode_size; rtx last_byte_loop_label = gen_label_rtx (); size = force_reg (SImode, size); value4doubleword = nds32_gen_dup_8_byte_to_double_word_value (value); value4byte = simplify_gen_subreg (QImode, value4doubleword, DImode, subreg_lowpart_offset (QImode, DImode)); emit_move_insn (byte_loop_size, size); emit_move_insn (byte_loop_base, base_reg); /* Jump to last byte loop if size is less than 16. */ emit_cmp_and_jump_insns (size, gen_int_mode (16, SImode), LE, NULL, SImode, 1, last_byte_loop_label); /* Make sure align to 4 byte first since v3m can't unalign access. */ emit_insn (gen_andsi3 (last_2_bit, base_reg, gen_int_mode (0x3, SImode))); emit_insn (gen_subsi3 (need_align_bytes, gen_int_mode (4, SImode), last_2_bit)); /* Align to 4 byte. */ new_base_reg = emit_setmem_byte_loop (base_reg, need_align_bytes, value4byte, true); /* Calculate remain size. */ emit_insn (gen_subsi3 (remain_size, size, need_align_bytes)); /* Set memory word by word. */ byte_mode_size = emit_setmem_doubleword_loop (new_base_reg, remain_size, value4doubleword); emit_move_insn (byte_loop_base, new_base_reg); emit_move_insn (byte_loop_size, byte_mode_size); emit_label (last_byte_loop_label); /* And set memory for remain bytes. */ emit_setmem_byte_loop (byte_loop_base, byte_loop_size, value4byte, false); 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; }
/* Function to check if 'bitci' instruction can be used with IVAL. */ int nds32_can_use_bitci_p (int ival) { /* If we are using V3 ISA, we have 'bitci' instruction. Try to see if we can present 'andi' semantic with such 'bit-clear-immediate' operation. For example, 'andi $r0,$r0,0xfffffffc' can be presented with 'bitci $r0,$r0,3'. */ return (TARGET_ISA_V3 && (ival < 0) && satisfies_constraint_Iu15 (gen_int_mode (~ival, SImode))); }
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 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 void canonicalize_address (rtx x) { for (;;) switch (GET_CODE (x)) { case ASHIFT: if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (GET_MODE (x)) && INTVAL (XEXP (x, 1)) >= 0) { HOST_WIDE_INT shift = INTVAL (XEXP (x, 1)); PUT_CODE (x, MULT); XEXP (x, 1) = gen_int_mode (HOST_WIDE_INT_1 << shift, GET_MODE (x)); } x = XEXP (x, 0); break; case PLUS: if (GET_CODE (XEXP (x, 0)) == PLUS || GET_CODE (XEXP (x, 0)) == ASHIFT || GET_CODE (XEXP (x, 0)) == CONST) canonicalize_address (XEXP (x, 0)); x = XEXP (x, 1); break; case CONST: x = XEXP (x, 0); break; default: return; } }
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; }
static bool combine_set_extension (ext_cand *cand, rtx curr_insn, rtx *orig_set) { rtx orig_src = SET_SRC (*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. */ enum machine_mode src_mode = GET_MODE (SET_DEST (*orig_set)); rtx new_const_int = gen_int_mode (INTVAL (orig_src) & GET_MODE_MASK (src_mode), GET_MODE (new_reg)); 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)) { if (dump_file) { fprintf (dump_file, "Tentatively merged extension with definition:\n"); print_rtl_single (dump_file, curr_insn); } return true; } return false; }