--- /dev/null
+/* This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#define IN_TARGET_CODE 1
+
+#define INCLUDE_STRING
+#define INCLUDE_MAP
+#define INCLUDE_VECTOR
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "function.h"
+#include "memmodel.h"
+#include "emit-rtl.h"
+#include "tm_p.h"
+#include "expr.h"
+#include "selftest.h"
+#include "selftest-rtl.h"
+
+#if CHECKING_P
+using namespace selftest;
+class riscv_selftest_arch_abi_setter
+{
+private:
+ std::string m_arch_backup;
+ enum riscv_abi_type m_abi_backup;
+
+public:
+ riscv_selftest_arch_abi_setter (const char *arch, enum riscv_abi_type abi)
+ : m_arch_backup (riscv_arch_str ()), m_abi_backup (riscv_abi)
+ {
+ riscv_parse_arch_string (arch, &global_options, UNKNOWN_LOCATION);
+ riscv_abi = abi;
+ riscv_reinit ();
+ }
+ ~riscv_selftest_arch_abi_setter ()
+ {
+ riscv_parse_arch_string (m_arch_backup.c_str (), &global_options,
+ UNKNOWN_LOCATION);
+ riscv_abi = m_abi_backup;
+ riscv_reinit ();
+ }
+};
+
+static poly_int64
+eval_value (rtx x, std::map<unsigned, rtx> ®no_to_rtx)
+{
+ if (!REG_P (x))
+ {
+ debug (x);
+ gcc_unreachable ();
+ }
+
+ rtx expr = NULL_RTX;
+ unsigned regno = REGNO (x);
+ expr = regno_to_rtx[regno];
+
+ poly_int64 op1_val = 0;
+ poly_int64 op2_val = 0;
+ if (UNARY_P (expr))
+ {
+ op1_val = eval_value (XEXP (expr, 0), regno_to_rtx);
+ }
+ if (BINARY_P (expr))
+ {
+ op1_val = eval_value (XEXP (expr, 0), regno_to_rtx);
+ op2_val = eval_value (XEXP (expr, 1), regno_to_rtx);
+ }
+
+ switch (GET_CODE (expr))
+ {
+ case CONST_POLY_INT:
+ return rtx_to_poly_int64 (expr);
+ case CONST_INT:
+ return INTVAL (expr);
+
+ case MULT:
+ if (op1_val.is_constant ())
+ return op1_val.to_constant () * op2_val;
+ else if (op2_val.is_constant ())
+ return op1_val * op2_val.to_constant ();
+ else
+ gcc_unreachable ();
+ case PLUS:
+ return op1_val + op2_val;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Calculate the value of x register in the sequence. */
+static poly_int64
+calculate_x_in_sequence (rtx reg)
+{
+ std::map<unsigned, rtx> regno_to_rtx;
+ rtx_insn *insn;
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ rtx pat = PATTERN (insn);
+ rtx dest = SET_DEST (pat);
+
+ if (GET_CODE (pat) == CLOBBER)
+ continue;
+
+ if (SUBREG_P (dest))
+ continue;
+
+ gcc_assert (REG_P (dest));
+ rtx note = find_reg_equal_equiv_note (insn);
+ unsigned regno = REGNO (dest);
+ if (note)
+ regno_to_rtx[regno] = XEXP (note, 0);
+ else
+ regno_to_rtx[regno] = SET_SRC (pat);
+ }
+
+ return eval_value (reg, regno_to_rtx);
+}
+
+typedef enum
+{
+ POLY_TEST_DIMODE,
+ POLY_TEST_PMODE
+} poly_test_mode_t;
+
+static void
+simple_poly_selftest (const char *arch, enum riscv_abi_type abi,
+ const std::vector<machine_mode> &modes)
+{
+ riscv_selftest_arch_abi_setter rv (arch, abi);
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("riscv/empty-func.rtl"));
+ set_new_first_and_last_insn (NULL, NULL);
+
+ for (machine_mode mode : modes)
+ emit_move_insn (gen_reg_rtx (mode),
+ gen_int_mode (BYTES_PER_RISCV_VECTOR, mode));
+}
+
+static void
+run_poly_int_selftest (const char *arch, enum riscv_abi_type abi,
+ poly_test_mode_t test_mode,
+ const std::vector<poly_int64> &worklist)
+{
+ riscv_selftest_arch_abi_setter rv (arch, abi);
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("riscv/empty-func.rtl"));
+ set_new_first_and_last_insn (NULL, NULL);
+ machine_mode mode = VOIDmode;
+
+ switch (test_mode)
+ {
+ case POLY_TEST_DIMODE:
+ mode = DImode;
+ break;
+ case POLY_TEST_PMODE:
+ mode = Pmode;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ for (const poly_int64 &poly_val : worklist)
+ {
+ start_sequence ();
+ rtx dest = gen_reg_rtx (mode);
+ emit_move_insn (dest, gen_int_mode (poly_val, mode));
+ ASSERT_TRUE (known_eq (calculate_x_in_sequence (dest), poly_val));
+ end_sequence ();
+ }
+}
+
+static void
+run_poly_int_selftests (void)
+{
+ std::vector<poly_int64> worklist
+ = {BYTES_PER_RISCV_VECTOR, BYTES_PER_RISCV_VECTOR * 8,
+ BYTES_PER_RISCV_VECTOR * 32, -BYTES_PER_RISCV_VECTOR * 8,
+ -BYTES_PER_RISCV_VECTOR * 32, BYTES_PER_RISCV_VECTOR * 7,
+ BYTES_PER_RISCV_VECTOR * 31, -BYTES_PER_RISCV_VECTOR * 7,
+ -BYTES_PER_RISCV_VECTOR * 31, BYTES_PER_RISCV_VECTOR * 9,
+ BYTES_PER_RISCV_VECTOR * 33, -BYTES_PER_RISCV_VECTOR * 9,
+ -BYTES_PER_RISCV_VECTOR * 33, poly_int64 (207, 0),
+ poly_int64 (-207, 0), poly_int64 (0, 207),
+ poly_int64 (0, -207), poly_int64 (5555, 0),
+ poly_int64 (0, 5555), poly_int64 (4096, 4096),
+ poly_int64 (17, 4088), poly_int64 (3889, 4104),
+ poly_int64 (-4096, -4096), poly_int64 (219, -4088),
+ poly_int64 (-4309, -4104), poly_int64 (-7337, 88),
+ poly_int64 (9317, -88), poly_int64 (4, 4),
+ poly_int64 (17, 4), poly_int64 (-7337, 4),
+ poly_int64 (-4, -4), poly_int64 (-389, -4),
+ poly_int64 (4789, -4), poly_int64 (-5977, 1508),
+ poly_int64 (219, -1508), poly_int64 (2, 2),
+ poly_int64 (33, 2), poly_int64 (-7337, 2),
+ poly_int64 (-2, -2), poly_int64 (-389, -2),
+ poly_int64 (4789, -2), poly_int64 (-3567, 954),
+ poly_int64 (945, -954), poly_int64 (1, 1),
+ poly_int64 (977, 1), poly_int64 (-339, 1),
+ poly_int64 (-1, -1), poly_int64 (-12, -1),
+ poly_int64 (44, -1), poly_int64 (9567, 77),
+ poly_int64 (3467, -77)};
+
+ simple_poly_selftest ("rv64imafdv", ABI_LP64D,
+ {QImode, HImode, SImode, DImode});
+ simple_poly_selftest ("rv32imafdv", ABI_ILP32D, {QImode, HImode, SImode});
+
+ run_poly_int_selftest ("rv64imafdv", ABI_LP64D, POLY_TEST_PMODE, worklist);
+ run_poly_int_selftest ("rv64imafd_zve32x1p0", ABI_LP64D, POLY_TEST_PMODE,
+ worklist);
+ run_poly_int_selftest ("rv32imafdv", ABI_ILP32, POLY_TEST_PMODE, worklist);
+ run_poly_int_selftest ("rv32imafdv", ABI_ILP32, POLY_TEST_DIMODE, worklist);
+ run_poly_int_selftest ("rv32imafd_zve32x1p0", ABI_ILP32D, POLY_TEST_PMODE,
+ worklist);
+ run_poly_int_selftest ("rv32imafd_zve32x1p0", ABI_ILP32D, POLY_TEST_DIMODE,
+ worklist);
+}
+namespace selftest {
+/* Run all target-specific selftests. */
+void
+riscv_run_selftests (void)
+{
+ run_poly_int_selftests ();
+}
+} // namespace selftest
+#endif /* #if CHECKING_P */
#include "predict.h"
#include "tree-pass.h"
#include "opts.h"
+#include "tm-constrs.h"
+#include "rtl-iter.h"
/* True if X is an UNSPEC wrapper around a SYMBOL_REF or LABEL_REF. */
#define UNSPEC_ADDRESS_P(X) \
enum riscv_symbol_type type;
rtx base, offset;
+ /* There's no way to calculate VL-based values using relocations. */
+ subrtx_iterator::array_type array;
+ FOR_EACH_SUBRTX (iter, array, x, ALL)
+ if (GET_CODE (*iter) == CONST_POLY_INT)
+ return true;
+
/* There is no assembler syntax for expressing an address-sized
high part. */
if (GET_CODE (x) == HIGH)
riscv_emit_move (dest, src);
}
+/* 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. */
+
+static void
+riscv_report_v_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;
+}
+
+/* Helper function to operation for rtx_code CODE. */
+static void
+riscv_expand_op (enum rtx_code code, machine_mode mode, rtx op0, rtx op1,
+ rtx op2)
+{
+ if (can_create_pseudo_p ())
+ {
+ rtx result;
+ if (GET_RTX_CLASS (code) == RTX_UNARY)
+ result = expand_simple_unop (mode, code, op1, NULL_RTX, false);
+ else
+ result = expand_simple_binop (mode, code, op1, op2, NULL_RTX, false,
+ OPTAB_DIRECT);
+ riscv_emit_move (op0, result);
+ }
+ else
+ {
+ rtx pat;
+ /* The following implementation is for prologue and epilogue.
+ Because prologue and epilogue can not use pseudo register.
+ We can't using expand_simple_binop or expand_simple_unop. */
+ if (GET_RTX_CLASS (code) == RTX_UNARY)
+ pat = gen_rtx_fmt_e (code, mode, op1);
+ else
+ pat = gen_rtx_fmt_ee (code, mode, op1, op2);
+ emit_insn (gen_rtx_SET (op0, pat));
+ }
+}
+
+/* Expand mult operation with constant integer, multiplicand also used as a
+ * temporary register. */
+
+static void
+riscv_expand_mult_with_const_int (machine_mode mode, rtx dest, rtx multiplicand,
+ int multiplier)
+{
+ if (multiplier == 0)
+ {
+ riscv_emit_move (dest, GEN_INT (0));
+ return;
+ }
+
+ bool neg_p = multiplier < 0;
+ int multiplier_abs = abs (multiplier);
+
+ if (multiplier_abs == 1)
+ {
+ if (neg_p)
+ riscv_expand_op (NEG, mode, dest, multiplicand, NULL_RTX);
+ else
+ riscv_emit_move (dest, multiplicand);
+ }
+ else
+ {
+ if (pow2p_hwi (multiplier_abs))
+ {
+ /*
+ multiplicand = [BYTES_PER_RISCV_VECTOR].
+ 1. const_poly_int:P [BYTES_PER_RISCV_VECTOR * 8].
+ Sequence:
+ csrr a5, vlenb
+ slli a5, a5, 3
+ 2. const_poly_int:P [-BYTES_PER_RISCV_VECTOR * 8].
+ Sequence:
+ csrr a5, vlenb
+ slli a5, a5, 3
+ neg a5, a5
+ */
+ riscv_expand_op (ASHIFT, mode, dest, multiplicand,
+ gen_int_mode (exact_log2 (multiplier_abs), QImode));
+ if (neg_p)
+ riscv_expand_op (NEG, mode, dest, dest, NULL_RTX);
+ }
+ else if (pow2p_hwi (multiplier_abs + 1))
+ {
+ /*
+ multiplicand = [BYTES_PER_RISCV_VECTOR].
+ 1. const_poly_int:P [BYTES_PER_RISCV_VECTOR * 7].
+ Sequence:
+ csrr a5, vlenb
+ slli a4, a5, 3
+ sub a5, a4, a5
+ 2. const_poly_int:P [-BYTES_PER_RISCV_VECTOR * 7].
+ Sequence:
+ csrr a5, vlenb
+ slli a4, a5, 3
+ sub a5, a4, a5 + neg a5, a5 => sub a5, a5, a4
+ */
+ riscv_expand_op (ASHIFT, mode, dest, multiplicand,
+ gen_int_mode (exact_log2 (multiplier_abs + 1),
+ QImode));
+ if (neg_p)
+ riscv_expand_op (MINUS, mode, dest, multiplicand, dest);
+ else
+ riscv_expand_op (MINUS, mode, dest, dest, multiplicand);
+ }
+ else if (pow2p_hwi (multiplier - 1))
+ {
+ /*
+ multiplicand = [BYTES_PER_RISCV_VECTOR].
+ 1. const_poly_int:P [BYTES_PER_RISCV_VECTOR * 9].
+ Sequence:
+ csrr a5, vlenb
+ slli a4, a5, 3
+ add a5, a4, a5
+ 2. const_poly_int:P [-BYTES_PER_RISCV_VECTOR * 9].
+ Sequence:
+ csrr a5, vlenb
+ slli a4, a5, 3
+ add a5, a4, a5
+ neg a5, a5
+ */
+ riscv_expand_op (ASHIFT, mode, dest, multiplicand,
+ gen_int_mode (exact_log2 (multiplier_abs - 1),
+ QImode));
+ riscv_expand_op (PLUS, mode, dest, dest, multiplicand);
+ if (neg_p)
+ riscv_expand_op (NEG, mode, dest, dest, NULL_RTX);
+ }
+ else
+ {
+ /* We use multiplication for remaining cases. */
+ gcc_assert (
+ TARGET_MUL
+ && "M-extension must be enabled to calculate the poly_int "
+ "size/offset.");
+ riscv_emit_move (dest, gen_int_mode (multiplier, mode));
+ riscv_expand_op (MULT, mode, dest, dest, multiplicand);
+ }
+ }
+}
+
+/* Analyze src and emit const_poly_int mov sequence. */
+
+static void
+riscv_legitimize_poly_move (machine_mode mode, rtx dest, rtx tmp, 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;
+ /* Calculate (const_poly_int:MODE [m, n]) using scalar instructions.
+ For any (const_poly_int:MODE [m, n]), the calculation formula is as
+ follows.
+ constant = m - n.
+ When minimum VLEN = 32, poly of VLENB = (4, 4).
+ base = vlenb(4, 4) or vlenb/2(2, 2) or vlenb/4(1, 1).
+ When minimum VLEN > 32, poly of VLENB = (8, 8).
+ base = vlenb(8, 8) or vlenb/2(4, 4) or vlenb/4(2, 2) or vlenb/8(1, 1).
+ magn = (n, n) / base.
+ (m, n) = base * magn + constant.
+ This calculation doesn't need div operation. */
+
+ emit_move_insn (tmp, gen_int_mode (BYTES_PER_RISCV_VECTOR, mode));
+
+ 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)
+ div_factor = 1;
+ else if ((factor % (vlenb / 2)) == 0)
+ div_factor = 2;
+ else if ((factor % (vlenb / 4)) == 0)
+ div_factor = 4;
+ else if ((factor % (vlenb / 8)) == 0)
+ div_factor = 8;
+ else
+ gcc_unreachable ();
+
+ if (div_factor != 1)
+ riscv_expand_op (LSHIFTRT, mode, tmp, tmp,
+ gen_int_mode (exact_log2 (div_factor), QImode));
+
+ riscv_expand_mult_with_const_int (mode, dest, tmp,
+ factor / (vlenb / div_factor));
+ HOST_WIDE_INT constant = offset - factor;
+
+ if (constant == 0)
+ return;
+ else if (SMALL_OPERAND (constant))
+ riscv_expand_op (PLUS, mode, dest, dest, gen_int_mode (constant, mode));
+ else
+ {
+ /* Handle the constant value is not a 12-bit value. */
+ rtx high;
+
+ /* Leave OFFSET as a 16-bit offset and put the excess in HIGH.
+ The addition inside the macro CONST_HIGH_PART may cause an
+ overflow, so we need to force a sign-extension check. */
+ high = gen_int_mode (CONST_HIGH_PART (constant), mode);
+ constant = CONST_LOW_PART (constant);
+ riscv_emit_move (tmp, high);
+ riscv_expand_op (PLUS, mode, dest, tmp, dest);
+ riscv_expand_op (PLUS, mode, dest, dest, gen_int_mode (constant, mode));
+ }
+}
+
/* If (set DEST SRC) is not a valid move instruction, emit an equivalent
sequence that is valid. */
bool
riscv_legitimize_move (machine_mode mode, rtx dest, rtx src)
{
+ if (CONST_POLY_INT_P (src))
+ {
+ poly_int64 value = rtx_to_poly_int64 (src);
+ if (!value.is_constant () && !TARGET_VECTOR)
+ {
+ riscv_report_v_required ();
+ return false;
+ }
+
+ if (satisfies_constraint_vp (src))
+ return false;
+
+ if (GET_MODE_SIZE (mode).to_constant () < GET_MODE_SIZE (Pmode))
+ {
+ /* In RV32 system, handle (const_poly_int:QI [m, n])
+ (const_poly_int:HI [m, n]).
+ In RV64 system, handle (const_poly_int:QI [m, n])
+ (const_poly_int:HI [m, n])
+ (const_poly_int:SI [m, n]). */
+ rtx tmp = gen_reg_rtx (Pmode);
+ riscv_legitimize_poly_move (Pmode, gen_lowpart (Pmode, dest), tmp,
+ src);
+ }
+ else
+ {
+ /* In RV32 system, handle (const_poly_int:SI [m, n])
+ (const_poly_int:DI [m, n]).
+ In RV64 system, handle (const_poly_int:DI [m, n]).
+ FIXME: Maybe we could gen SImode in RV32 and then sign-extend to DImode,
+ the offset should not exceed 4GiB in general. */
+ rtx tmp = gen_reg_rtx (mode);
+ riscv_legitimize_poly_move (mode, dest, tmp, src);
+ }
+ return true;
+ }
/* Expand
(set (reg:QI target) (mem:QI (address)))
to
if (!riscv_v_ext_vector_mode_p (mode))
return false;
+ if (!V_REG_P (regno + nregs - 1))
+ return false;
+
/* 3.3.2. LMUL = 2,4,8, register numbers should be multiple of 2,4,8.
but for mask vector register, register numbers can be any number. */
int lmul = 1;
if (lmul != 1)
return ((regno % lmul) == 0);
}
+ else if (regno == VL_REGNUM || regno == VTYPE_REGNUM)
+ return true;
else
return false;
static poly_uint16
riscv_convert_vector_bits (void)
{
- /* The runtime invariant is only meaningful when TARGET_VECTOR is enabled. */
- if (!TARGET_VECTOR)
- return 0;
-
if (TARGET_MIN_VLEN > 32)
{
/* When targetting minimum VLEN > 32, we should use 64-bit chunk size.
riscv_bytes_per_vector_chunk = 4;
}
- return poly_uint16 (1, 1);
+ /* Set riscv_vector_chunks as poly (1, 1) run-time constant if TARGET_VECTOR
+ is enabled. Set riscv_vector_chunks as 1 compile-time constant if
+ TARGET_VECTOR is disabled. riscv_vector_chunks is used in "riscv-modes.def"
+ to set RVV mode size. The RVV machine modes size are run-time constant if
+ TARGET_VECTOR is enabled. The RVV machine modes size remains default
+ compile-time constant if TARGET_VECTOR is disabled. */
+ return TARGET_VECTOR ? poly_uint16 (1, 1) : 1;
}
/* Implement TARGET_OPTION_OVERRIDE. */
set_optab_libfunc (unord_optab, HFmode, NULL);
}
+#if CHECKING_P
+void
+riscv_reinit (void)
+{
+ riscv_option_override ();
+ init_adjust_machine_modes ();
+ init_derived_machine_modes ();
+ reinit_regs ();
+ init_optabs ();
+}
+#endif
+
+#if CHECKING_P
+#undef TARGET_RUN_TARGET_SELFTESTS
+#define TARGET_RUN_TARGET_SELFTESTS selftest::riscv_run_selftests
+#endif /* #if CHECKING_P */
+
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"