/* 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
. */
#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"
#include "insn-attr.h"
#include "target.h"
#include "optabs.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 ®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 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 &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 &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 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);
simple_poly_selftest ("rv64imafdv_zvl256b", ABI_LP64D,
{QImode, HImode, SImode, DImode});
simple_poly_selftest ("rv64imafdv_zvl512b", ABI_LP64D,
{QImode, HImode, SImode, DImode});
simple_poly_selftest ("rv64imafdv_zvl1024b", ABI_LP64D,
{QImode, HImode, SImode, DImode});
simple_poly_selftest ("rv64imafdv_zvl2048b", ABI_LP64D,
{QImode, HImode, SImode, DImode});
simple_poly_selftest ("rv64imafdv_zvl4096b", ABI_LP64D,
{QImode, HImode, SImode, DImode});
}
static void
run_const_vector_selftests (void)
{
/* We dont't need to do the redundant tests in different march && mabi.
Just pick up the march && mabi which fully support all RVV modes. */
riscv_selftest_arch_abi_setter rv ("rv64imafdcv", ABI_LP64D);
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("riscv/empty-func.rtl"));
set_new_first_and_last_insn (NULL, NULL);
machine_mode mode;
std::vector worklist = {-111, -17, -16, 7, 15, 16, 111};
FOR_EACH_MODE_IN_CLASS (mode, MODE_VECTOR_INT)
{
if (riscv_v_ext_vector_mode_p (mode))
{
for (const HOST_WIDE_INT &val : worklist)
{
start_sequence ();
rtx dest = gen_reg_rtx (mode);
rtx dup = gen_const_vec_duplicate (mode, GEN_INT (val));
emit_move_insn (dest, dup);
rtx_insn *insn = get_last_insn ();
rtx src = SET_SRC (PATTERN (insn));
/* 1. Should be vmv.v.i for in rang of -16 ~ 15.
2. Should be vmv.v.x for exceed -16 ~ 15. */
if (IN_RANGE (val, -16, 15))
ASSERT_TRUE (
rtx_equal_p (XEXP (SET_SRC (PATTERN (insn)), 1), dup));
else
ASSERT_TRUE (GET_CODE (src) == VEC_DUPLICATE);
end_sequence ();
}
}
}
FOR_EACH_MODE_IN_CLASS (mode, MODE_VECTOR_FLOAT)
{
if (riscv_v_ext_vector_mode_p (mode))
{
scalar_mode inner_mode = GET_MODE_INNER (mode);
REAL_VALUE_TYPE f = REAL_VALUE_ATOF ("0.2928932", inner_mode);
rtx ele = const_double_from_real_value (f, inner_mode);
start_sequence ();
rtx dest = gen_reg_rtx (mode);
rtx dup = gen_const_vec_duplicate (mode, ele);
emit_move_insn (dest, dup);
rtx_insn *insn = get_last_insn ();
rtx src = SET_SRC (PATTERN (insn));
/* Should always be vfmv.v.f. */
ASSERT_TRUE (GET_CODE (src) == VEC_DUPLICATE);
end_sequence ();
}
}
FOR_EACH_MODE_IN_CLASS (mode, MODE_VECTOR_BOOL)
{
/* Test vmset.m. */
if (riscv_v_ext_vector_mode_p (mode))
{
start_sequence ();
rtx dest = gen_reg_rtx (mode);
emit_move_insn (dest, CONSTM1_RTX (mode));
rtx_insn *insn = get_last_insn ();
rtx src = XEXP (SET_SRC (PATTERN (insn)), 1);
ASSERT_TRUE (rtx_equal_p (src, CONSTM1_RTX (mode)));
end_sequence ();
}
}
}
static void
run_broadcast_selftests (void)
{
/* We dont't need to do the redundant tests in different march && mabi.
Just pick up the march && mabi which fully support all RVV modes. */
riscv_selftest_arch_abi_setter rv ("rv64imafdcv", ABI_LP64D);
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("riscv/empty-func.rtl"));
set_new_first_and_last_insn (NULL, NULL);
machine_mode mode;
#define BROADCAST_TEST(MODE_CLASS) \
FOR_EACH_MODE_IN_CLASS (mode, MODE_VECTOR_INT) \
{ \
if (riscv_v_ext_vector_mode_p (mode)) \
{ \
rtx_insn *insn; \
rtx src; \
scalar_mode inner_mode = GET_MODE_INNER (mode); \
/* Test vlse.v with zero stride. */ \
start_sequence (); \
rtx addr = gen_reg_rtx (Pmode); \
rtx mem = gen_rtx_MEM (inner_mode, addr); \
expand_vector_broadcast (mode, mem); \
insn = get_last_insn (); \
src = SET_SRC (PATTERN (insn)); \
ASSERT_TRUE (MEM_P (XEXP (src, 0))); \
ASSERT_TRUE ( \
rtx_equal_p (src, gen_rtx_VEC_DUPLICATE (mode, XEXP (src, 0)))); \
end_sequence (); \
/* Test vmv.v.x or vfmv.v.f. */ \
start_sequence (); \
rtx reg = gen_reg_rtx (inner_mode); \
expand_vector_broadcast (mode, reg); \
insn = get_last_insn (); \
src = SET_SRC (PATTERN (insn)); \
ASSERT_TRUE (REG_P (XEXP (src, 0))); \
ASSERT_TRUE ( \
rtx_equal_p (src, gen_rtx_VEC_DUPLICATE (mode, XEXP (src, 0)))); \
end_sequence (); \
} \
}
BROADCAST_TEST (MODE_VECTOR_INT)
BROADCAST_TEST (MODE_VECTOR_FLOAT)
}
namespace selftest {
/* Run all target-specific selftests. */
void
riscv_run_selftests (void)
{
if (!BYTES_PER_RISCV_VECTOR.is_constant ())
/* We can know POLY value = [4, 4] when BYTES_PER_RISCV_VECTOR
is !is_constant () since we can use csrr vlenb and scalar shift
instruction to compute such POLY value and store it into a scalar
register. Wheras, we can't know [4, 4] on it is specified as
FIXED-VLMAX since BYTES_PER_RISCV_VECTOR = 16 for -march=rv64gcv
and csrr vlenb is 16 which is totally unrelated to any
compile-time unknown POLY value.
Since we never need to compute a compile-time unknown POLY value
when -mrvv-vector-bits=zvl, disable poly
selftests in such situation. */
run_poly_int_selftests ();
run_const_vector_selftests ();
run_broadcast_selftests ();
}
} // namespace selftest
#endif /* #if CHECKING_P */