@@ -74,6 +74,7 @@ extern bool riscv_expand_block_move (rtx, rtx, rtx);
extern bool riscv_store_data_bypass_p (rtx_insn *, rtx_insn *);
extern rtx riscv_gen_gpr_save_insn (struct riscv_frame_info *);
extern bool riscv_gpr_save_operation_p (rtx);
+extern rtx riscv_add_offset (rtx, rtx, HOST_WIDE_INT);
/* Routines implemented in riscv-c.cc. */
void riscv_cpu_cpp_builtins (cpp_reader *);
@@ -592,6 +592,260 @@ rvv_const_vec_all_same_in_range_p (rtx x, HOST_WIDE_INT minval,
IN_RANGE (INTVAL (elt), minval, maxval));
}
+/* Report when we try to do something that requires vector when vector is disabled.
+ This is an error of last resort and isn't very high-quality. It usually
+ involves attempts to measure the vector length in some way. */
+void
+rvv_report_required (void)
+{
+ static bool reported_p = false;
+
+ /* Avoid reporting a slew of messages for a single oversight. */
+ if (reported_p)
+ return;
+
+ error ("this operation requires the RVV ISA extension");
+ inform (input_location, "you can enable RVV using the command-line"
+ " option %<-march%>, or by using the %<target%>"
+ " attribute or pragma");
+ reported_p = true;
+}
+
+/* Note: clobber register holds the vlenb or 1/2 vlenb or 1/4 vlenb or 1/8 vlenb value. */
+/* Expand move for quotient. */
+static void
+expand_quotient (int quotient, machine_mode mode, rtx clobber_vlenb, rtx dest)
+{
+ if (quotient == 0)
+ {
+ riscv_emit_move(dest, GEN_INT(0));
+ return;
+ }
+
+ bool is_neg = quotient < 0;
+ quotient = abs(quotient);
+ int log2 = exact_log2 (quotient);
+ int vlenb = BYTES_PER_RISCV_VECTOR.coeffs[1];
+
+ if (GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+ emit_insn (gen_rtx_SET (clobber_vlenb, gen_int_mode (poly_int64 (vlenb, vlenb), mode)));
+ else
+ {
+ riscv_emit_move (gen_highpart (Pmode, clobber_vlenb), GEN_INT (0));
+ emit_insn (gen_rtx_SET (gen_lowpart (Pmode, clobber_vlenb), gen_int_mode (poly_int64 (vlenb, vlenb), Pmode)));
+ }
+
+ if (log2 == 0)
+ {
+ if (is_neg)
+ {
+ if (GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+ emit_insn (gen_rtx_SET (dest, gen_rtx_NEG (mode, clobber_vlenb)));
+ else
+ {
+ /* We should use SImode to simulate DImode negation. */
+ /* prologue and epilogue can not go through this condition. */
+ gcc_assert (can_create_pseudo_p ());
+ rtx reg = gen_reg_rtx (Pmode);
+ riscv_emit_move(dest, clobber_vlenb);
+ emit_insn (gen_rtx_SET (reg,
+ gen_rtx_NE (Pmode, gen_lowpart (Pmode, dest), const0_rtx)));
+ emit_insn (gen_rtx_SET (gen_highpart (Pmode, dest),
+ gen_rtx_NEG (Pmode, gen_highpart (Pmode, dest))));
+ emit_insn (gen_rtx_SET (gen_lowpart (Pmode, dest),
+ gen_rtx_NEG (Pmode, gen_lowpart (Pmode, dest))));
+ emit_insn (gen_rtx_SET (gen_highpart (Pmode, dest),
+ gen_rtx_MINUS (Pmode, gen_highpart (Pmode, dest), reg)));
+ }
+ }
+ else
+ riscv_emit_move(dest, clobber_vlenb);
+ }
+ else if (log2 != -1
+ && GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+ {
+ gcc_assert (IN_RANGE (log2, 0, 31));
+
+ if (is_neg)
+ {
+ emit_insn (gen_rtx_SET (dest, gen_rtx_NEG (mode, clobber_vlenb)));
+ emit_insn (gen_rtx_SET (dest, gen_rtx_ASHIFT (mode, dest, GEN_INT (log2))));
+ }
+ else
+ emit_insn (gen_rtx_SET (dest, gen_rtx_ASHIFT (mode, clobber_vlenb, GEN_INT (log2))));
+ }
+ else if (exact_log2 (quotient + 1) != -1
+ && GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+ {
+ gcc_assert (IN_RANGE (exact_log2 (quotient + 1), 0, 31));
+ emit_insn (gen_rtx_SET (
+ dest,
+ gen_rtx_ASHIFT (mode, clobber_vlenb, GEN_INT (exact_log2 (quotient + 1)))));
+
+ if (is_neg)
+ emit_insn (gen_rtx_SET (dest, gen_rtx_MINUS (mode, clobber_vlenb, dest)));
+ else
+ emit_insn (gen_rtx_SET (dest, gen_rtx_MINUS (mode, dest, clobber_vlenb)));
+ }
+ else if (exact_log2 (quotient - 1) != -1
+ && GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+ {
+ gcc_assert (IN_RANGE (exact_log2 (quotient - 1), 0, 31));
+ emit_insn (gen_rtx_SET (
+ dest, gen_rtx_ASHIFT (mode, clobber_vlenb,
+ GEN_INT (exact_log2 (quotient - 1)))));
+
+ if (is_neg)
+ {
+ emit_insn (gen_rtx_SET (dest, gen_rtx_NEG (mode, dest)));
+ emit_insn (gen_rtx_SET (dest, gen_rtx_MINUS (mode, dest, clobber_vlenb)));
+ }
+ else
+ emit_insn (gen_rtx_SET (dest, gen_rtx_PLUS (mode, dest, clobber_vlenb)));
+ }
+ else
+ {
+ gcc_assert (TARGET_MUL
+ && "M-extension must be enabled to calculate the poly_int "
+ "size/offset.");
+
+ if (is_neg)
+ riscv_emit_move (dest, GEN_INT (-quotient));
+ else
+ riscv_emit_move (dest, GEN_INT (quotient));
+
+ if (GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+ emit_insn (gen_rtx_SET (dest, gen_rtx_MULT (mode, dest, clobber_vlenb)));
+ else
+ {
+ /* We should use SImode to simulate DImode multiplication. */
+ /* prologue and epilogue can not go through this condition. */
+ gcc_assert (can_create_pseudo_p ());
+ rtx reg = gen_reg_rtx (Pmode);
+ emit_insn (gen_umulsi3_highpart (reg, gen_lowpart (Pmode, dest),
+ gen_lowpart (Pmode, clobber_vlenb)));
+ emit_insn (gen_rtx_SET (gen_highpart (Pmode, clobber_vlenb),
+ gen_rtx_MULT (Pmode, gen_highpart (Pmode, clobber_vlenb),
+ gen_lowpart (Pmode, dest))));
+ emit_insn (gen_rtx_SET (gen_highpart (Pmode, dest),
+ gen_rtx_MULT (Pmode, gen_highpart (Pmode, dest),
+ gen_lowpart (Pmode, clobber_vlenb))));
+ emit_insn (gen_rtx_SET (gen_lowpart (Pmode, dest),
+ gen_rtx_MULT (Pmode, gen_lowpart (Pmode, dest),
+ gen_lowpart (Pmode, clobber_vlenb))));
+ emit_insn (gen_rtx_SET (gen_highpart (Pmode, dest),
+ gen_rtx_PLUS (Pmode, gen_highpart (Pmode, dest),
+ gen_highpart (Pmode, clobber_vlenb))));
+ emit_insn (gen_rtx_SET (gen_highpart (Pmode, dest),
+ gen_rtx_PLUS (Pmode, gen_highpart (Pmode, dest), reg)));
+ }
+ }
+}
+
+/* Analyze src and emit const_poly_int mov sequence. */
+
+void
+rvv_expand_poly_move (machine_mode mode, rtx dest, rtx clobber, rtx src)
+{
+ poly_int64 value = rtx_to_poly_int64 (src);
+ int offset = value.coeffs[0];
+ int factor = value.coeffs[1];
+ int vlenb = BYTES_PER_RISCV_VECTOR.coeffs[1];
+ int div_factor = 0;
+
+ if (BYTES_PER_RISCV_VECTOR.is_constant ())
+ {
+ gcc_assert (value.is_constant ());
+ riscv_emit_move (dest, GEN_INT (value.to_constant ()));
+ return;
+ }
+ else if ((factor % vlenb) == 0)
+ expand_quotient (factor / vlenb, mode, clobber, dest);
+ else if ((factor % (vlenb / 2)) == 0)
+ {
+ expand_quotient (factor / (vlenb / 2), mode, clobber, dest);
+ div_factor = 2;
+ }
+ else if ((factor % (vlenb / 4)) == 0)
+ {
+ expand_quotient (factor / (vlenb / 4), mode, clobber, dest);
+ div_factor = 4;
+ }
+ else if ((factor % (vlenb / 8)) == 0)
+ {
+ expand_quotient (factor / (vlenb / 8), mode, clobber, dest);
+ div_factor = 8;
+ }
+ else if ((factor % (vlenb / 16)) == 0)
+ {
+ expand_quotient (factor / (vlenb / 16), mode, clobber, dest);
+ div_factor = 16;
+ }
+ else
+ gcc_unreachable ();
+
+ if (div_factor != 0)
+ {
+ if (GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+ emit_insn (gen_rtx_SET (
+ dest,
+ gen_rtx_ASHIFTRT (mode, dest, GEN_INT (exact_log2 (div_factor)))));
+ else
+ {
+ /* We should use SImode to simulate DImode shift. */
+ /* prologue and epilogue can not go through this condition. */
+ gcc_assert (can_create_pseudo_p ());
+ rtx reg = gen_reg_rtx (Pmode);
+ emit_insn (gen_rtx_SET (
+ reg, gen_rtx_ASHIFT (Pmode, gen_highpart (Pmode, dest),
+ GEN_INT (GET_MODE_BITSIZE (Pmode) -
+ exact_log2 (div_factor)))));
+ emit_insn (gen_rtx_SET (
+ gen_lowpart (Pmode, dest),
+ gen_rtx_LSHIFTRT (Pmode, gen_lowpart (Pmode, dest),
+ GEN_INT (exact_log2 (div_factor)))));
+ emit_insn (gen_rtx_SET (
+ gen_lowpart (Pmode, dest),
+ gen_rtx_IOR (Pmode, reg, gen_lowpart (Pmode, dest))));
+ emit_insn (gen_rtx_SET (
+ gen_highpart (Pmode, dest),
+ gen_rtx_ASHIFTRT (Pmode, gen_highpart (Pmode, dest),
+ GEN_INT (exact_log2 (div_factor)))));
+ }
+ }
+
+ HOST_WIDE_INT constant = offset - factor;
+
+ if (constant == 0)
+ return;
+ else if (SMALL_OPERAND (constant))
+ {
+ if (GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+ emit_insn (
+ gen_rtx_SET (dest, gen_rtx_PLUS (mode, dest, GEN_INT (constant))));
+ else
+ {
+ /* We should use SImode to simulate DImode addition. */
+ /* prologue and epilogue can not go through this condition. */
+ gcc_assert (can_create_pseudo_p ());
+ rtx reg = gen_reg_rtx (Pmode);
+ emit_insn (
+ gen_rtx_SET (reg, gen_rtx_PLUS (Pmode, gen_lowpart (Pmode, dest),
+ GEN_INT (constant))));
+ emit_insn (gen_rtx_SET (
+ gen_lowpart (Pmode, dest),
+ gen_rtx_LTU (Pmode, reg, gen_lowpart (Pmode, dest))));
+ emit_insn (
+ gen_rtx_SET (gen_highpart (Pmode, dest),
+ gen_rtx_PLUS (Pmode, gen_lowpart (Pmode, dest),
+ gen_highpart (Pmode, dest))));
+ riscv_emit_move (gen_lowpart (Pmode, dest), reg);
+ }
+ }
+ else
+ emit_insn (gen_rtx_SET (dest, riscv_add_offset (clobber, dest, constant)));
+}
+
/* Helper functions for handling sew=64 on RV32 system. */
bool
imm32_p (rtx a)
@@ -20,4 +20,6 @@
#ifndef GCC_RISCV_VECTOR_H
#define GCC_RISCV_VECTOR_H
+void rvv_report_required (void);
+void rvv_expand_poly_move (machine_mode, rtx, rtx, rtx);
#endif // GCC_RISCV_VECTOR_H
\ No newline at end of file
@@ -1164,6 +1164,7 @@ riscv_const_insns (rtx x)
factor = 1 + riscv_integer_cost (INTVAL (elt));
}
}
+ return factor;
}
case CONST:
@@ -1467,7 +1468,7 @@ riscv_split_symbol (rtx temp, rtx addr, machine_mode mode, rtx *low_out,
riscv_force_temporary; it is only needed when OFFSET is not a
SMALL_OPERAND. */
-static rtx
+rtx
riscv_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset)
{
if (!SMALL_OPERAND (offset))
@@ -1723,7 +1724,20 @@ riscv_legitimize_const_move (machine_mode mode, rtx dest, rtx src)
riscv_emit_move (dest, riscv_add_offset (NULL, base, INTVAL (offset)));
return;
}
-
+
+ /* Handle (const:SI (plus:SI (symbol_ref:SI)
+ (const_poly_int:SI [16, 16]))) */
+ if (GET_CODE (src) == CONST && GET_CODE (XEXP (src, 0)) == PLUS
+ && CONST_POLY_INT_P (XEXP (XEXP (src, 0), 1)))
+ {
+ rtx reg = gen_reg_rtx (mode);
+ rtx clobber = gen_reg_rtx (mode);
+ riscv_emit_move (dest, XEXP (XEXP (src, 0), 0));
+ rvv_expand_poly_move (mode, reg, clobber, XEXP (XEXP (src, 0), 1));
+ emit_insn (gen_rtx_SET (dest, gen_rtx_PLUS (mode, dest, reg)));
+ return;
+ }
+
src = force_const_mem (mode, src);
/* When using explicit relocs, constant pool references are sometimes
@@ -1738,6 +1752,28 @@ riscv_legitimize_const_move (machine_mode mode, rtx dest, rtx src)
bool
riscv_legitimize_move (machine_mode mode, rtx dest, rtx src)
{
+ scalar_int_mode int_mode;
+ if (CONST_POLY_INT_P (src)
+ && is_a <scalar_int_mode> (mode, &int_mode))
+ {
+ poly_int64 value = rtx_to_poly_int64 (src);
+ if (!value.is_constant () && !TARGET_VECTOR)
+ {
+ rvv_report_required ();
+ return true;
+ }
+ if (GET_MODE_SIZE (mode).to_constant () < GET_MODE_SIZE (Pmode))
+ {
+ rtx clobber = gen_reg_rtx (Pmode);
+ rvv_expand_poly_move (Pmode, gen_lowpart (Pmode, dest), clobber, src);
+ }
+ else
+ {
+ rtx clobber = gen_reg_rtx (mode);
+ rvv_expand_poly_move (mode, dest, clobber, src);
+ }
+ return true;
+ }
/* Expand
(set (reg:QI target) (mem:QI (address)))
to
@@ -4998,7 +5034,9 @@ riscv_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
if (regsize != 1)
return ((regno % regsize) == 0);
- }
+ }
+ else if (regno == VTYPE_REGNUM || regno == VL_REGNUM)
+ return (nregs == 1 && mode == SImode);
else
return false;
@@ -5388,7 +5426,7 @@ riscv_conditional_register_usage (void)
if (!TARGET_VECTOR)
{
for (int regno = V_REG_FIRST; regno <= V_REG_LAST; regno++)
- call_used_regs[regno] = 1;
+ fixed_regs[regno] = call_used_regs[regno] = 1;
fixed_regs[VTYPE_REGNUM] = call_used_regs[VTYPE_REGNUM] = 1;
fixed_regs[VL_REGNUM] = call_used_regs[VL_REGNUM] = 1;