v8/test/cctest/test-assembler-riscv64.cc
Yujie Wang 4240985a1e [riscv64] Add tests for RVV VI VF instructions
Implement `LiftoffAssembler::emit_i16x8_sconvert_i32x4` for riscv.
Add tests for rvv integer and floating-point instructions.
Add simulator support for rvv instructions, e.g. `vfmadd`, `vnclip`.
Fixed order of operands for `vfdiv.vv`.

Bug: v8:11976
Change-Id: I0691ac66771468533c5994be1fc8a86b09d3c738
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3225319
Reviewed-by: Yahan Lu <yahan@iscas.ac.cn>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Commit-Queue: Yahan Lu <yahan@iscas.ac.cn>
Cr-Commit-Position: refs/heads/main@{#77595}
2021-10-28 13:57:31 +00:00

2407 lines
93 KiB
C++

// Copyright 2021 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <math.h>
#include <iostream>
#include "src/base/utils/random-number-generator.h"
#include "src/codegen/assembler-inl.h"
#include "src/codegen/macro-assembler.h"
#include "src/diagnostics/disassembler.h"
#include "src/execution/simulator.h"
#include "src/heap/factory.h"
#include "src/init/v8.h"
#include "src/utils/utils.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/value-helper.h"
#include "test/cctest/test-helper-riscv64.h"
namespace v8 {
namespace internal {
// Define these function prototypes to match JSEntryFunction in execution.cc
using F1 = void*(int x, int p1, int p2, int p3, int p4);
using F2 = void*(int x, int y, int p2, int p3, int p4);
using F3 = void*(void* p, int p1, int p2, int p3, int p4);
using F4 = void*(int64_t x, int64_t y, int64_t p2, int64_t p3, int64_t p4);
using F5 = void*(void* p0, void* p1, int p2, int p3, int p4);
#define MIN_VAL_IMM12 -(1 << 11)
#define LARGE_INT_EXCEED_32_BIT 0x01C9'1075'0321'FB01LL
#define LARGE_INT_UNDER_32_BIT 0x1234'5678
#define LARGE_UINT_EXCEED_32_BIT 0xFDCB'1234'A034'5691ULL
#define __ assm.
#define UTEST_R2_FORM_WITH_RES(instr_name, type, rs1_val, rs2_val, \
expected_res) \
TEST(RISCV_UTEST_##instr_name) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { __ instr_name(a0, a0, a1); }; \
auto res = GenAndRunTest<type, type>(rs1_val, rs2_val, fn); \
CHECK_EQ(expected_res, res); \
}
#define UTEST_R1_FORM_WITH_RES(instr_name, in_type, out_type, rs1_val, \
expected_res) \
TEST(RISCV_UTEST_##instr_name) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { __ instr_name(a0, a0); }; \
auto res = GenAndRunTest<out_type, in_type>(rs1_val, fn); \
CHECK_EQ(expected_res, res); \
}
#define UTEST_R1_FORM_WITH_RES_C(instr_name, in_type, out_type, rs1_val, \
expected_res) \
TEST(RISCV_UTEST_##instr_name) { \
i::FLAG_riscv_c_extension = true; \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { __ instr_name(a0, a0); }; \
auto res = GenAndRunTest<out_type, in_type>(rs1_val, fn); \
CHECK_EQ(expected_res, res); \
}
#define UTEST_I_FORM_WITH_RES(instr_name, type, rs1_val, imm12, expected_res) \
TEST(RISCV_UTEST_##instr_name) { \
CcTest::InitializeVM(); \
CHECK_EQ(is_intn(imm12, 12), true); \
auto fn = [](MacroAssembler& assm) { __ instr_name(a0, a0, imm12); }; \
auto res = GenAndRunTest<type, type>(rs1_val, fn); \
CHECK_EQ(expected_res, res); \
}
#define UTEST_AMO_WITH_RES(instr_name, aq, rl, inout_type, rs1_val, rs2_val, \
expected_res) \
TEST(RISCV_UTEST_##instr_name) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { __ instr_name(aq, rl, a1, a0, a2); }; \
auto res = \
GenAndRunTestForAMO<inout_type, inout_type>(rs1_val, rs2_val, fn); \
CHECK_EQ(expected_res, res); \
}
#define UTEST_LOAD_STORE(ldname, stname, value_type, value) \
TEST(RISCV_UTEST_##stname##ldname) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { \
__ stname(a1, a0, 0); \
__ ldname(a0, a0, 0); \
}; \
GenAndRunTestForLoadStore<value_type>(value, fn); \
}
// Since f.Call() is implemented as vararg calls and RISCV calling convention
// passes all vararg arguments and returns (including floats) in GPRs, we have
// to move from GPR to FPR and back in all floating point tests
#define UTEST_LOAD_STORE_F(ldname, stname, value_type, store_value) \
TEST(RISCV_UTEST_##stname##ldname) { \
DCHECK(std::is_floating_point<value_type>::value); \
\
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { \
__ stname(fa0, a0, 0); \
__ ldname(fa0, a0, 0); \
}; \
GenAndRunTestForLoadStore<value_type>(store_value, fn); \
}
#define UTEST_LR_SC(ldname, stname, aq, rl, value_type, value) \
TEST(RISCV_UTEST_##stname##ldname) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { \
__ ldname(aq, rl, a1, a0); \
__ stname(aq, rl, a0, a0, a1); \
}; \
GenAndRunTestForLRSC<value_type>(value, fn); \
}
#define UTEST_R1_FORM_WITH_RES_F(instr_name, type, rs1_fval, expected_fres) \
TEST(RISCV_UTEST_##instr_name) { \
DCHECK(std::is_floating_point<type>::value); \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { __ instr_name(fa0, fa0); }; \
auto res = GenAndRunTest<type, type>(rs1_fval, fn); \
CHECK_EQ(expected_fres, res); \
}
#define UTEST_R2_FORM_WITH_RES_F(instr_name, type, rs1_fval, rs2_fval, \
expected_fres) \
TEST(RISCV_UTEST_##instr_name) { \
DCHECK(std::is_floating_point<type>::value); \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { __ instr_name(fa0, fa0, fa1); }; \
auto res = GenAndRunTest<type, type>(rs1_fval, rs2_fval, fn); \
CHECK_EQ(expected_fres, res); \
}
#define UTEST_R3_FORM_WITH_RES_F(instr_name, type, rs1_fval, rs2_fval, \
rs3_fval, expected_fres) \
TEST(RISCV_UTEST_##instr_name) { \
DCHECK(std::is_floating_point<type>::value); \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { __ instr_name(fa0, fa0, fa1, fa2); }; \
auto res = GenAndRunTest<type, type>(rs1_fval, rs2_fval, rs3_fval, fn); \
CHECK_EQ(expected_fres, res); \
}
#define UTEST_COMPARE_WITH_RES_F(instr_name, input_type, rs1_fval, rs2_fval, \
expected_res) \
TEST(RISCV_UTEST_##instr_name) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { __ instr_name(a0, fa0, fa1); }; \
auto res = GenAndRunTest<int32_t, input_type>(rs1_fval, rs2_fval, fn); \
CHECK_EQ(expected_res, res); \
}
#define UTEST_CONV_F_FROM_I(instr_name, input_type, output_type, rs1_val, \
expected_fres) \
TEST(RISCV_UTEST_##instr_name) { \
DCHECK(std::is_integral<input_type>::value&& \
std::is_floating_point<output_type>::value); \
\
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { __ instr_name(fa0, a0); }; \
auto res = GenAndRunTest<output_type, input_type>(rs1_val, fn); \
CHECK_EQ(expected_fres, res); \
}
#define UTEST_CONV_I_FROM_F(instr_name, input_type, output_type, \
rounding_mode, rs1_fval, expected_res) \
TEST(RISCV_UTEST_##instr_name) { \
DCHECK(std::is_floating_point<input_type>::value&& \
std::is_integral<output_type>::value); \
\
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { \
__ instr_name(a0, fa0, rounding_mode); \
}; \
auto res = GenAndRunTest<output_type, input_type>(rs1_fval, fn); \
CHECK_EQ(expected_res, res); \
} \
\
TEST(RISCV_UTEST_dyn_##instr_name) { \
DCHECK(std::is_floating_point<input_type>::value&& \
std::is_integral<output_type>::value); \
\
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { \
__ csrwi(csr_frm, rounding_mode); \
__ instr_name(a0, fa0, DYN); \
}; \
auto res = GenAndRunTest<output_type, input_type>(rs1_fval, fn); \
CHECK_EQ(expected_res, res); \
}
#define UTEST_CONV_F_FROM_F(instr_name, input_type, output_type, rs1_val, \
expected_fres) \
TEST(RISCV_UTEST_##instr_name) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { __ instr_name(fa0, fa0); }; \
auto res = GenAndRunTest<output_type, input_type>(rs1_val, fn); \
CHECK_EQ(expected_fres, res); \
}
#define UTEST_CSRI(csr_reg, csr_write_val, csr_set_clear_val) \
TEST(RISCV_UTEST_CSRI_##csr_reg) { \
CHECK_EQ(is_uint5(csr_write_val) && is_uint5(csr_set_clear_val), true); \
\
CcTest::InitializeVM(); \
int64_t expected_res = 111; \
Label exit, error; \
auto fn = [&exit, &error, expected_res](MacroAssembler& assm) { \
/* test csr-write and csr-read */ \
__ csrwi(csr_reg, csr_write_val); \
__ csrr(a0, csr_reg); \
__ RV_li(a1, csr_write_val); \
__ bne(a0, a1, &error); \
/* test csr_set */ \
__ csrsi(csr_reg, csr_set_clear_val); \
__ csrr(a0, csr_reg); \
__ RV_li(a1, (csr_write_val) | (csr_set_clear_val)); \
__ bne(a0, a1, &error); \
/* test csr_clear */ \
__ csrci(csr_reg, csr_set_clear_val); \
__ csrr(a0, csr_reg); \
__ RV_li(a1, (csr_write_val) & (~(csr_set_clear_val))); \
__ bne(a0, a1, &error); \
/* everyhing runs correctly, return 111 */ \
__ RV_li(a0, expected_res); \
__ j(&exit); \
\
__ bind(&error); \
/* got an error, return 666 */ \
__ RV_li(a0, 666); \
\
__ bind(&exit); \
}; \
auto res = GenAndRunTest(fn); \
CHECK_EQ(expected_res, res); \
}
#define UTEST_CSR(csr_reg, csr_write_val, csr_set_clear_val) \
TEST(RISCV_UTEST_CSR_##csr_reg) { \
Label exit, error; \
int64_t expected_res = 111; \
auto fn = [&exit, &error, expected_res](MacroAssembler& assm) { \
/* test csr-write and csr-read */ \
__ RV_li(t0, csr_write_val); \
__ csrw(csr_reg, t0); \
__ csrr(a0, csr_reg); \
__ RV_li(a1, csr_write_val); \
__ bne(a0, a1, &error); \
/* test csr_set */ \
__ RV_li(t0, csr_set_clear_val); \
__ csrs(csr_reg, t0); \
__ csrr(a0, csr_reg); \
__ RV_li(a1, (csr_write_val) | (csr_set_clear_val)); \
__ bne(a0, a1, &error); \
/* test csr_clear */ \
__ RV_li(t0, csr_set_clear_val); \
__ csrc(csr_reg, t0); \
__ csrr(a0, csr_reg); \
__ RV_li(a1, (csr_write_val) & (~(csr_set_clear_val))); \
__ bne(a0, a1, &error); \
/* everyhing runs correctly, return 111 */ \
__ RV_li(a0, expected_res); \
__ j(&exit); \
\
__ bind(&error); \
/* got an error, return 666 */ \
__ RV_li(a0, 666); \
\
__ bind(&exit); \
}; \
\
auto res = GenAndRunTest(fn); \
CHECK_EQ(expected_res, res); \
}
#define UTEST_R2_FORM_WITH_OP(instr_name, type, rs1_val, rs2_val, tested_op) \
UTEST_R2_FORM_WITH_RES(instr_name, type, rs1_val, rs2_val, \
((rs1_val)tested_op(rs2_val)))
#define UTEST_I_FORM_WITH_OP(instr_name, type, rs1_val, imm12, tested_op) \
UTEST_I_FORM_WITH_RES(instr_name, type, rs1_val, imm12, \
((rs1_val)tested_op(imm12)))
#define UTEST_R2_FORM_WITH_OP_F(instr_name, type, rs1_fval, rs2_fval, \
tested_op) \
UTEST_R2_FORM_WITH_RES_F(instr_name, type, rs1_fval, rs2_fval, \
((rs1_fval)tested_op(rs2_fval)))
#define UTEST_COMPARE_WITH_OP_F(instr_name, input_type, rs1_fval, rs2_fval, \
tested_op) \
UTEST_COMPARE_WITH_RES_F(instr_name, input_type, rs1_fval, rs2_fval, \
((rs1_fval)tested_op(rs2_fval)))
// -- test load-store --
UTEST_LOAD_STORE(ld, sd, int64_t, 0xFBB10A9C12345678)
// due to sign-extension of lw
// instruction, value-to-stored must have
// its 32th least significant bit be 0
UTEST_LOAD_STORE(lw, sw, int32_t, 0x456AF894)
// set the 32th least significant bit of
// value-to-store to 1 to test
// zero-extension by lwu
UTEST_LOAD_STORE(lwu, sw, uint32_t, 0x856AF894)
// due to sign-extension of lh
// instruction, value-to-stored must have
// its 16th least significant bit be 0
UTEST_LOAD_STORE(lh, sh, int32_t, 0x7894)
// set the 16th least significant bit of
// value-to-store to 1 to test
// zero-extension by lhu
UTEST_LOAD_STORE(lhu, sh, uint32_t, 0xF894)
// due to sign-extension of lb
// instruction, value-to-stored must have
// its 8th least significant bit be 0
UTEST_LOAD_STORE(lb, sb, int32_t, 0x54)
// set the 8th least significant bit of
// value-to-store to 1 to test
// zero-extension by lbu
UTEST_LOAD_STORE(lbu, sb, uint32_t, 0x94)
// -- arithmetic w/ immediate --
UTEST_I_FORM_WITH_OP(addi, int64_t, LARGE_INT_EXCEED_32_BIT, MIN_VAL_IMM12, +)
UTEST_I_FORM_WITH_OP(slti, int64_t, LARGE_INT_EXCEED_32_BIT, MIN_VAL_IMM12, <)
UTEST_I_FORM_WITH_OP(sltiu, uint64_t, LARGE_UINT_EXCEED_32_BIT, 0x4FB, <)
UTEST_I_FORM_WITH_OP(xori, int64_t, LARGE_INT_EXCEED_32_BIT, MIN_VAL_IMM12, ^)
UTEST_I_FORM_WITH_OP(ori, int64_t, LARGE_INT_EXCEED_32_BIT, MIN_VAL_IMM12, |)
UTEST_I_FORM_WITH_OP(andi, int64_t, LARGE_INT_EXCEED_32_BIT, MIN_VAL_IMM12, &)
UTEST_I_FORM_WITH_OP(slli, int64_t, 0x1234'5678ULL, 33, <<)
UTEST_I_FORM_WITH_OP(srli, int64_t, 0x8234'5678'0000'0000ULL, 33, >>)
UTEST_I_FORM_WITH_OP(srai, int64_t, -0x1234'5678'0000'0000LL, 33, >>)
// -- arithmetic --
UTEST_R2_FORM_WITH_OP(add, int64_t, LARGE_INT_EXCEED_32_BIT, MIN_VAL_IMM12, +)
UTEST_R2_FORM_WITH_OP(sub, int64_t, LARGE_INT_EXCEED_32_BIT, MIN_VAL_IMM12, -)
UTEST_R2_FORM_WITH_OP(slt, int64_t, MIN_VAL_IMM12, LARGE_INT_EXCEED_32_BIT, <)
UTEST_R2_FORM_WITH_OP(sltu, uint64_t, 0x4FB, LARGE_UINT_EXCEED_32_BIT, <)
UTEST_R2_FORM_WITH_OP(xor_, int64_t, LARGE_INT_EXCEED_32_BIT, MIN_VAL_IMM12, ^)
UTEST_R2_FORM_WITH_OP(or_, int64_t, LARGE_INT_EXCEED_32_BIT, MIN_VAL_IMM12, |)
UTEST_R2_FORM_WITH_OP(and_, int64_t, LARGE_INT_EXCEED_32_BIT, MIN_VAL_IMM12, &)
UTEST_R2_FORM_WITH_OP(sll, int64_t, 0x12345678ULL, 33, <<)
UTEST_R2_FORM_WITH_OP(srl, int64_t, 0x8234567800000000ULL, 33, >>)
UTEST_R2_FORM_WITH_OP(sra, int64_t, -0x1234'5678'0000'0000LL, 33, >>)
// -- Memory fences --
// void fence(uint8_t pred, uint8_t succ);
// void fence_tso();
// -- Environment call / break --
// void ecall();
// void ebreak();
// void unimp();
// -- CSR --
UTEST_CSRI(csr_frm, DYN, RUP)
UTEST_CSRI(csr_fflags, kInexact | kInvalidOperation, kInvalidOperation)
UTEST_CSRI(csr_fcsr, kDivideByZero | kOverflow, kUnderflow)
UTEST_CSR(csr_frm, DYN, RUP)
UTEST_CSR(csr_fflags, kInexact | kInvalidOperation, kInvalidOperation)
UTEST_CSR(csr_fcsr, kDivideByZero | kOverflow | (RDN << kFcsrFrmShift),
kUnderflow | (RNE << kFcsrFrmShift))
// -- RV64I --
UTEST_I_FORM_WITH_OP(addiw, int32_t, LARGE_INT_UNDER_32_BIT, MIN_VAL_IMM12, +)
UTEST_I_FORM_WITH_OP(slliw, int32_t, 0x12345678U, 12, <<)
UTEST_I_FORM_WITH_OP(srliw, int32_t, 0x82345678U, 12, >>)
UTEST_I_FORM_WITH_OP(sraiw, int32_t, -123, 12, >>)
UTEST_R2_FORM_WITH_OP(addw, int32_t, LARGE_INT_UNDER_32_BIT, MIN_VAL_IMM12, +)
UTEST_R2_FORM_WITH_OP(subw, int32_t, LARGE_INT_UNDER_32_BIT, MIN_VAL_IMM12, -)
UTEST_R2_FORM_WITH_OP(sllw, int32_t, 0x12345678U, 12, <<)
UTEST_R2_FORM_WITH_OP(srlw, int32_t, 0x82345678U, 12, >>)
UTEST_R2_FORM_WITH_OP(sraw, int32_t, -123, 12, >>)
// -- RV32M Standard Extension --
UTEST_R2_FORM_WITH_OP(mul, int64_t, 0x0F945001L, MIN_VAL_IMM12, *)
UTEST_R2_FORM_WITH_RES(mulh, int64_t, 0x1234567800000000LL,
-0x1234'5617'0000'0000LL, 0x12345678LL * -0x1234'5617LL)
UTEST_R2_FORM_WITH_RES(mulhu, int64_t, 0x1234'5678'0000'0000ULL,
0xF896'7021'0000'0000ULL,
0x1234'5678ULL * 0xF896'7021ULL)
UTEST_R2_FORM_WITH_RES(mulhsu, int64_t, -0x1234'56780000'0000LL,
0xF234'5678'0000'0000ULL,
static_cast<int64_t>(-0x1234'5678LL * 0xF234'5678ULL))
UTEST_R2_FORM_WITH_OP(div, int64_t, LARGE_INT_EXCEED_32_BIT, MIN_VAL_IMM12, /)
UTEST_R2_FORM_WITH_OP(divu, uint64_t, LARGE_UINT_EXCEED_32_BIT, 100, /)
UTEST_R2_FORM_WITH_OP(rem, int64_t, LARGE_INT_EXCEED_32_BIT, MIN_VAL_IMM12, %)
UTEST_R2_FORM_WITH_OP(remu, uint64_t, LARGE_UINT_EXCEED_32_BIT, 100, %)
// -- RV64M Standard Extension (in addition to RV32M) --
UTEST_R2_FORM_WITH_OP(mulw, int32_t, -20, 56, *)
UTEST_R2_FORM_WITH_OP(divw, int32_t, 200, -10, /)
UTEST_R2_FORM_WITH_OP(divuw, uint32_t, 1000, 100, /)
UTEST_R2_FORM_WITH_OP(remw, int32_t, 1234, -91, %)
UTEST_R2_FORM_WITH_OP(remuw, uint32_t, 1234, 43, %)
// -- RV32A Standard Extension --
UTEST_LR_SC(lr_w, sc_w, false, false, int32_t, 0xFBB1A75C)
UTEST_AMO_WITH_RES(amoswap_w, false, false, uint32_t, 0xFBB1A75C, 0xA75C0A9C,
(uint32_t)0xA75C0A9C)
UTEST_AMO_WITH_RES(amoadd_w, false, false, uint32_t, 0xFBB1A75C, 0xA75C0A9C,
(uint32_t)0xFBB1A75C + (uint32_t)0xA75C0A9C)
UTEST_AMO_WITH_RES(amoxor_w, false, false, uint32_t, 0xFBB1A75C, 0xA75C0A9C,
(uint32_t)0xFBB1A75C ^ (uint32_t)0xA75C0A9C)
UTEST_AMO_WITH_RES(amoand_w, false, false, uint32_t, 0xFBB1A75C, 0xA75C0A9C,
(uint32_t)0xFBB1A75C & (uint32_t)0xA75C0A9C)
UTEST_AMO_WITH_RES(amoor_w, false, false, uint32_t, 0xFBB1A75C, 0xA75C0A9C,
(uint32_t)0xFBB1A75C | (uint32_t)0xA75C0A9C)
UTEST_AMO_WITH_RES(amomin_w, false, false, int32_t, 0xFBB1A75C, 0xA75C0A9C,
std::min((int32_t)0xFBB1A75C, (int32_t)0xA75C0A9C))
UTEST_AMO_WITH_RES(amomax_w, false, false, int32_t, 0xFBB1A75C, 0xA75C0A9C,
std::max((int32_t)0xFBB1A75C, (int32_t)0xA75C0A9C))
UTEST_AMO_WITH_RES(amominu_w, false, false, uint32_t, 0xFBB1A75C, 0xA75C0A9C,
std::min((uint32_t)0xFBB1A75C, (uint32_t)0xA75C0A9C))
UTEST_AMO_WITH_RES(amomaxu_w, false, false, uint32_t, 0xFBB1A75C, 0xA75C0A9C,
std::max((uint32_t)0xFBB1A75C, (uint32_t)0xA75C0A9C))
// -- RV64A Standard Extension (in addition to RV32A) --
UTEST_LR_SC(lr_d, sc_d, false, false, int64_t, 0xFBB10A9Cbfb76aa6)
UTEST_AMO_WITH_RES(amoswap_d, false, false, int64_t, 0xFBB10A9Cbfb76aa6,
0x284ff922346ad35c, (int64_t)0x284ff922346ad35c)
UTEST_AMO_WITH_RES(amoadd_d, false, false, int64_t, 0xFBB10A9Cbfb76aa6,
0x284ff922346ad35c,
(int64_t)0xFBB10A9Cbfb76aa6 + (int64_t)0x284ff922346ad35c)
UTEST_AMO_WITH_RES(amoxor_d, false, false, int64_t, 0xFBB10A9Cbfb76aa6,
0x284ff922346ad35c,
(int64_t)0xFBB10A9Cbfb76aa6 ^ (int64_t)0x284ff922346ad35c)
UTEST_AMO_WITH_RES(amoand_d, false, false, int64_t, 0xFBB10A9Cbfb76aa6,
0x284ff922346ad35c,
(int64_t)0xFBB10A9Cbfb76aa6 & (int64_t)0x284ff922346ad35c)
UTEST_AMO_WITH_RES(amoor_d, false, false, int64_t, 0xFBB10A9Cbfb76aa6,
0x284ff922346ad35c,
(int64_t)0xFBB10A9Cbfb76aa6 | (int64_t)0x284ff922346ad35c)
UTEST_AMO_WITH_RES(amomin_d, false, false, int64_t, 0xFBB10A9Cbfb76aa6,
0x284ff922346ad35c,
std::min((int64_t)0xFBB10A9Cbfb76aa6,
(int64_t)0x284ff922346ad35c))
UTEST_AMO_WITH_RES(amomax_d, false, false, int64_t, 0xFBB10A9Cbfb76aa6,
0x284ff922346ad35c,
std::max((int64_t)0xFBB10A9Cbfb76aa6,
(int64_t)0x284ff922346ad35c))
UTEST_AMO_WITH_RES(amominu_d, false, false, uint64_t, 0xFBB10A9Cbfb76aa6,
0x284ff922346ad35c,
std::min((uint64_t)0xFBB10A9Cbfb76aa6,
(uint64_t)0x284ff922346ad35c))
UTEST_AMO_WITH_RES(amomaxu_d, false, false, uint64_t, 0xFBB10A9Cbfb76aa6,
0x284ff922346ad35c,
std::max((uint64_t)0xFBB10A9Cbfb76aa6,
(uint64_t)0x284ff922346ad35c))
// -- RV32F Standard Extension --
UTEST_LOAD_STORE_F(flw, fsw, float, -2345.678f)
UTEST_R2_FORM_WITH_OP_F(fadd_s, float, -1012.01f, 3456.13f, +)
UTEST_R2_FORM_WITH_OP_F(fsub_s, float, -1012.01f, 3456.13f, -)
UTEST_R2_FORM_WITH_OP_F(fmul_s, float, -10.01f, 56.13f, *)
UTEST_R2_FORM_WITH_OP_F(fdiv_s, float, -10.01f, 34.13f, /)
UTEST_R1_FORM_WITH_RES_F(fsqrt_s, float, 34.13f, sqrtf(34.13f))
UTEST_R2_FORM_WITH_RES_F(fmin_s, float, -1012.0f, 3456.13f, -1012.0f)
UTEST_R2_FORM_WITH_RES_F(fmax_s, float, -1012.0f, 3456.13f, 3456.13f)
UTEST_R3_FORM_WITH_RES_F(fmadd_s, float, 67.56f, -1012.01f, 3456.13f,
std::fma(67.56f, -1012.01f, 3456.13f))
UTEST_R3_FORM_WITH_RES_F(fmsub_s, float, 67.56f, -1012.01f, 3456.13f,
std::fma(67.56f, -1012.01f, -3456.13f))
UTEST_R3_FORM_WITH_RES_F(fnmsub_s, float, 67.56f, -1012.01f, 3456.13f,
-std::fma(67.56f, -1012.01f, -3456.13f))
UTEST_R3_FORM_WITH_RES_F(fnmadd_s, float, 67.56f, -1012.01f, 3456.13f,
-std::fma(67.56f, -1012.01f, 3456.13f))
UTEST_COMPARE_WITH_OP_F(feq_s, float, -3456.56, -3456.56, ==)
UTEST_COMPARE_WITH_OP_F(flt_s, float, -3456.56, -3456.56, <)
UTEST_COMPARE_WITH_OP_F(fle_s, float, -3456.56, -3456.56, <=)
UTEST_CONV_F_FROM_I(fcvt_s_w, int32_t, float, -100, (float)(-100))
UTEST_CONV_F_FROM_I(fcvt_s_wu, uint32_t, float,
std::numeric_limits<uint32_t>::max(),
(float)(std::numeric_limits<uint32_t>::max()))
UTEST_CONV_I_FROM_F(fcvt_w_s, float, int32_t, RMM, -100.5f, -101)
UTEST_CONV_I_FROM_F(fcvt_wu_s, float, uint32_t, RUP, 256.1f, 257)
UTEST_R2_FORM_WITH_RES_F(fsgnj_s, float, -100.0f, 200.0f, 100.0f)
UTEST_R2_FORM_WITH_RES_F(fsgnjn_s, float, 100.0f, 200.0f, -100.0f)
UTEST_R2_FORM_WITH_RES_F(fsgnjx_s, float, -100.0f, 200.0f, -100.0f)
// -- RV64F Standard Extension (in addition to RV32F) --
UTEST_LOAD_STORE_F(fld, fsd, double, -3456.678)
UTEST_R2_FORM_WITH_OP_F(fadd_d, double, -1012.01, 3456.13, +)
UTEST_R2_FORM_WITH_OP_F(fsub_d, double, -1012.01, 3456.13, -)
UTEST_R2_FORM_WITH_OP_F(fmul_d, double, -10.01, 56.13, *)
UTEST_R2_FORM_WITH_OP_F(fdiv_d, double, -10.01, 34.13, /)
UTEST_R1_FORM_WITH_RES_F(fsqrt_d, double, 34.13, std::sqrt(34.13))
UTEST_R2_FORM_WITH_RES_F(fmin_d, double, -1012.0, 3456.13, -1012.0)
UTEST_R2_FORM_WITH_RES_F(fmax_d, double, -1012.0, 3456.13, 3456.13)
UTEST_R3_FORM_WITH_RES_F(fmadd_d, double, 67.56, -1012.01, 3456.13,
std::fma(67.56, -1012.01, 3456.13))
UTEST_R3_FORM_WITH_RES_F(fmsub_d, double, 67.56, -1012.01, 3456.13,
std::fma(67.56, -1012.01, -3456.13))
UTEST_R3_FORM_WITH_RES_F(fnmsub_d, double, 67.56, -1012.01, 3456.13,
-std::fma(67.56, -1012.01, -3456.13))
UTEST_R3_FORM_WITH_RES_F(fnmadd_d, double, 67.56, -1012.01, 3456.13,
-std::fma(67.56, -1012.01, 3456.13))
UTEST_COMPARE_WITH_OP_F(feq_d, double, -3456.56, -3456.56, ==)
UTEST_COMPARE_WITH_OP_F(flt_d, double, -3456.56, -3456.56, <)
UTEST_COMPARE_WITH_OP_F(fle_d, double, -3456.56, -3456.56, <=)
UTEST_CONV_F_FROM_I(fcvt_d_w, int32_t, double, -100, -100.0)
UTEST_CONV_F_FROM_I(fcvt_d_wu, uint32_t, double,
std::numeric_limits<uint32_t>::max(),
(double)(std::numeric_limits<uint32_t>::max()))
UTEST_CONV_I_FROM_F(fcvt_w_d, double, int32_t, RTZ, -100.0, -100)
UTEST_CONV_I_FROM_F(fcvt_wu_d, double, uint32_t, RTZ,
(double)(std::numeric_limits<uint32_t>::max()),
std::numeric_limits<uint32_t>::max())
// -- RV64F Standard Extension (in addition to RV32F) --
UTEST_CONV_I_FROM_F(fcvt_l_s, float, int64_t, RDN, -100.5f, -101)
UTEST_CONV_I_FROM_F(fcvt_lu_s, float, uint64_t, RTZ, 1000001.0f, 1000001)
UTEST_CONV_F_FROM_I(fcvt_s_l, int64_t, float, (-0x1234'5678'0000'0001LL),
(float)(-0x1234'5678'0000'0001LL))
UTEST_CONV_F_FROM_I(fcvt_s_lu, uint64_t, float,
std::numeric_limits<uint64_t>::max(),
(float)(std::numeric_limits<uint64_t>::max()))
// -- RV32D Standard Extension --
UTEST_CONV_F_FROM_F(fcvt_s_d, double, float, 100.0, 100.0f)
UTEST_CONV_F_FROM_F(fcvt_d_s, float, double, 100.0f, 100.0)
UTEST_R2_FORM_WITH_RES_F(fsgnj_d, double, -100.0, 200.0, 100.0)
UTEST_R2_FORM_WITH_RES_F(fsgnjn_d, double, 100.0, 200.0, -100.0)
UTEST_R2_FORM_WITH_RES_F(fsgnjx_d, double, -100.0, 200.0, -100.0)
// -- RV64D Standard Extension (in addition to RV32D) --
UTEST_CONV_I_FROM_F(fcvt_l_d, double, int64_t, RNE, -100.5, -100)
UTEST_CONV_I_FROM_F(fcvt_lu_d, double, uint64_t, RTZ, 2456.5, 2456)
UTEST_CONV_F_FROM_I(fcvt_d_l, int64_t, double, (-0x1234'5678'0000'0001LL),
(double)(-0x1234'5678'0000'0001LL))
UTEST_CONV_F_FROM_I(fcvt_d_lu, uint64_t, double,
std::numeric_limits<uint64_t>::max(),
(double)(std::numeric_limits<uint64_t>::max()))
// -- RV64C Standard Extension --
UTEST_R1_FORM_WITH_RES_C(c_mv, int64_t, int64_t, 0x0f5600ab123400,
0x0f5600ab123400)
// -- Assembler Pseudo Instructions --
UTEST_R1_FORM_WITH_RES(mv, int64_t, int64_t, 0x0f5600ab123400, 0x0f5600ab123400)
UTEST_R1_FORM_WITH_RES(not_, int64_t, int64_t, 0, ~0)
UTEST_R1_FORM_WITH_RES(neg, int64_t, int64_t, 0x0f5600ab123400LL,
-(0x0f5600ab123400LL))
UTEST_R1_FORM_WITH_RES(negw, int32_t, int32_t, 0xab123400, -(0xab123400))
UTEST_R1_FORM_WITH_RES(sext_w, int32_t, int64_t, 0xFA01'1234,
static_cast<int64_t>(0xFFFFFFFFFA011234LL))
UTEST_R1_FORM_WITH_RES(seqz, int64_t, int64_t, 20, 20 == 0)
UTEST_R1_FORM_WITH_RES(snez, int64_t, int64_t, 20, 20 != 0)
UTEST_R1_FORM_WITH_RES(sltz, int64_t, int64_t, -20, -20 < 0)
UTEST_R1_FORM_WITH_RES(sgtz, int64_t, int64_t, -20, -20 > 0)
UTEST_R1_FORM_WITH_RES_F(fmv_s, float, -23.5f, -23.5f)
UTEST_R1_FORM_WITH_RES_F(fabs_s, float, -23.5f, 23.5f)
UTEST_R1_FORM_WITH_RES_F(fneg_s, float, 23.5f, -23.5f)
UTEST_R1_FORM_WITH_RES_F(fmv_d, double, -23.5, -23.5)
UTEST_R1_FORM_WITH_RES_F(fabs_d, double, -23.5, 23.5)
UTEST_R1_FORM_WITH_RES_F(fneg_d, double, 23.5, -23.5)
// Test LI
TEST(RISCV0) {
CcTest::InitializeVM();
FOR_INT64_INPUTS(i) {
auto fn = [i](MacroAssembler& assm) { __ RV_li(a0, i); };
auto res = GenAndRunTest(fn);
CHECK_EQ(i, res);
}
}
TEST(RISCV1) {
CcTest::InitializeVM();
Label L, C;
auto fn = [&L, &C](MacroAssembler& assm) {
__ mv(a1, a0);
__ RV_li(a0, 0l);
__ j(&C);
__ bind(&L);
__ add(a0, a0, a1);
__ addi(a1, a1, -1);
__ bind(&C);
__ xori(a2, a1, 0);
__ bnez(a2, &L);
};
int64_t input = 50;
int64_t expected_res = 1275L;
auto res = GenAndRunTest<int64_t>(input, fn);
CHECK_EQ(expected_res, res);
}
TEST(RISCV2) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Label exit, error;
int64_t expected_res = 0x31415926L;
// ----- Test all instructions.
// Test lui, ori, and addiw, used in the
// li pseudo-instruction. This way we
// can then safely load registers with
// chosen values.
auto fn = [&exit, &error, expected_res](MacroAssembler& assm) {
__ ori(a4, zero_reg, 0);
__ lui(a4, 0x12345);
__ ori(a4, a4, 0);
__ ori(a4, a4, 0xF0F);
__ ori(a4, a4, 0x0F0);
__ addiw(a5, a4, 1);
__ addiw(a6, a5, -0x10);
// Load values in temporary registers.
__ RV_li(a4, 0x00000004);
__ RV_li(a5, 0x00001234);
__ RV_li(a6, 0x12345678);
__ RV_li(a7, 0x7FFFFFFF);
__ RV_li(t0, 0xFFFFFFFC);
__ RV_li(t1, 0xFFFFEDCC);
__ RV_li(t2, 0xEDCBA988);
__ RV_li(t3, 0x80000000);
__ srliw(t0, a6, 8); // 0x00123456
__ slliw(t0, t0, 11); // 0x91A2B000
__ sraiw(t0, t0, 3); // 0xFFFFFFFF F2345600
__ sraw(t0, t0, a4); // 0xFFFFFFFF FF234560
__ sllw(t0, t0, a4); // 0xFFFFFFFF F2345600
__ srlw(t0, t0, a4); // 0x0F234560
__ RV_li(t5, 0x0F234560);
__ bne(t0, t5, &error);
__ addw(t0, a4, a5); // 0x00001238
__ subw(t0, t0, a4); // 0x00001234
__ RV_li(t5, 0x00001234);
__ bne(t0, t5, &error);
__ addw(a1, a7,
a4); // 32bit addu result is sign-extended into 64bit reg.
__ RV_li(t5, 0xFFFFFFFF80000003);
__ bne(a1, t5, &error);
__ subw(a1, t3, a4); // 0x7FFFFFFC
__ RV_li(t5, 0x7FFFFFFC);
__ bne(a1, t5, &error);
__ and_(t0, a5, a6); // 0x0000000000001230
__ or_(t0, t0, a5); // 0x0000000000001234
__ xor_(t0, t0, a6); // 0x000000001234444C
__ or_(t0, t0, a6);
__ not_(t0, t0); // 0xFFFFFFFFEDCBA983
__ RV_li(t5, 0xFFFFFFFFEDCBA983);
__ bne(t0, t5, &error);
// Shift both 32bit number to left, to
// preserve meaning of next comparison.
__ slli(a7, a7, 32);
__ slli(t3, t3, 32);
__ slt(t0, t3, a7);
__ RV_li(t5, 1);
__ bne(t0, t5, &error);
__ sltu(t0, t3, a7);
__ bne(t0, zero_reg, &error);
// Restore original values in registers.
__ srli(a7, a7, 32);
__ srli(t3, t3, 32);
__ RV_li(t0, 0x7421); // 0x00007421
__ addi(t0, t0, -0x1); // 0x00007420
__ addi(t0, t0, -0x20); // 0x00007400
__ RV_li(t5, 0x00007400);
__ bne(t0, t5, &error);
__ addiw(a1, a7, 0x1); // 0x80000000 - result is sign-extended.
__ RV_li(t5, 0xFFFFFFFF80000000);
__ bne(a1, t5, &error);
__ RV_li(t5, 0x00002000);
__ slt(t0, a5, t5); // 0x1
__ RV_li(t6, 0xFFFFFFFFFFFF8000);
__ slt(t0, t0, t6); // 0x0
__ bne(t0, zero_reg, &error);
__ sltu(t0, a5, t5); // 0x1
__ RV_li(t6, 0x00008000);
__ sltu(t0, t0, t6); // 0x1
__ RV_li(t5, 1);
__ bne(t0, t5, &error);
__ andi(t0, a5, 0x0F0); // 0x00000030
__ ori(t0, t0, 0x200); // 0x00000230
__ xori(t0, t0, 0x3CC); // 0x000001FC
__ RV_li(t5, 0x000001FC);
__ bne(t0, t5, &error);
__ lui(a1, -519628); // Result is sign-extended into 64bit register.
__ RV_li(t5, 0xFFFFFFFF81234000);
__ bne(a1, t5, &error);
// Everything was correctly executed.
// Load the expected result.
__ RV_li(a0, expected_res);
__ j(&exit);
__ bind(&error);
// Got an error. Return a wrong result.
__ RV_li(a0, 666);
__ bind(&exit);
};
auto res = GenAndRunTest(fn);
CHECK_EQ(expected_res, res);
}
TEST(RISCV3) {
// Test floating point instructions.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
struct T {
double a;
double b;
double c;
double d;
double e;
double f;
double g;
double h;
double i;
float fa;
float fb;
float fc;
float fd;
float fe;
float ff;
float fg;
} t;
// Create a function that accepts &t and loads, manipulates, and stores
// the doubles t.a ... t.f.
// Double precision floating point instructions.
auto fn = [](MacroAssembler& assm) {
__ fld(ft0, a0, offsetof(T, a));
__ fld(ft1, a0, offsetof(T, b));
__ fadd_d(ft2, ft0, ft1);
__ fsd(ft2, a0, offsetof(T, c)); // c = a + b.
__ fmv_d(ft3, ft2); // c
__ fneg_d(fa0, ft1); // -b
__ fsub_d(ft3, ft3, fa0);
__ fsd(ft3, a0, offsetof(T, d)); // d = c - (-b).
__ fsd(ft0, a0, offsetof(T, b)); // b = a.
__ RV_li(a4, 120);
__ fcvt_d_w(ft5, a4);
__ fmul_d(ft3, ft3, ft5);
__ fsd(ft3, a0, offsetof(T, e)); // e = d * 120 = 1.8066e16.
__ fdiv_d(ft4, ft3, ft0);
__ fsd(ft4, a0, offsetof(T, f)); // f = e / a = 120.44.
__ fsqrt_d(ft5, ft4);
__ fsd(ft5, a0, offsetof(T, g));
// g = sqrt(f) = 10.97451593465515908537
__ fld(ft0, a0, offsetof(T, h));
__ fld(ft1, a0, offsetof(T, i));
__ fmadd_d(ft5, ft1, ft0, ft1);
__ fsd(ft5, a0, offsetof(T, h));
// // Single precision floating point instructions.
__ flw(ft0, a0, offsetof(T, fa));
__ flw(ft1, a0, offsetof(T, fb));
__ fadd_s(ft2, ft0, ft1);
__ fsw(ft2, a0, offsetof(T, fc)); // fc = fa + fb.
__ fneg_s(ft3, ft1); // -fb
__ fsub_s(ft3, ft2, ft3);
__ fsw(ft3, a0, offsetof(T, fd)); // fd = fc - (-fb).
__ fsw(ft0, a0, offsetof(T, fb)); // fb = fa.
__ RV_li(t0, 120);
__ fcvt_s_w(ft5, t0); // ft5 = 120.0.
__ fmul_s(ft3, ft3, ft5);
__ fsw(ft3, a0, offsetof(T, fe)); // fe = fd * 120
__ fdiv_s(ft4, ft3, ft0);
__ fsw(ft4, a0, offsetof(T, ff)); // ff = fe / fa
__ fsqrt_s(ft5, ft4);
__ fsw(ft5, a0, offsetof(T, fg));
};
auto f = AssembleCode<F3>(fn);
// Double test values.
t.a = 1.5e14;
t.b = 2.75e11;
t.c = 0.0;
t.d = 0.0;
t.e = 0.0;
t.f = 0.0;
t.h = 1.5;
t.i = 2.75;
// Single test values.
t.fa = 1.5e6;
t.fb = 2.75e4;
t.fc = 0.0;
t.fd = 0.0;
t.fe = 0.0;
t.ff = 0.0;
f.Call(&t, 0, 0, 0, 0);
// Expected double results.
CHECK_EQ(1.5e14, t.a);
CHECK_EQ(1.5e14, t.b);
CHECK_EQ(1.50275e14, t.c);
CHECK_EQ(1.50550e14, t.d);
CHECK_EQ(1.8066e16, t.e);
CHECK_EQ(120.44, t.f);
CHECK_EQ(10.97451593465515908537, t.g);
CHECK_EQ(6.875, t.h);
// Expected single results.
CHECK_EQ(1.5e6, t.fa);
CHECK_EQ(1.5e6, t.fb);
CHECK_EQ(1.5275e06, t.fc);
CHECK_EQ(1.5550e06, t.fd);
CHECK_EQ(1.866e08, t.fe);
CHECK_EQ(124.40000152587890625, t.ff);
CHECK_EQ(11.1534748077392578125, t.fg);
}
TEST(RISCV4) {
// Test moves between floating point and
// integer registers.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
struct T {
double a;
double b;
double c;
float d;
int64_t e;
} t;
auto fn = [](MacroAssembler& assm) {
__ fld(ft0, a0, offsetof(T, a));
__ fld(fa1, a0, offsetof(T, b));
// Swap ft0 and fa1, by using 2 integer registers, a4-a5,
__ fmv_x_d(a4, ft0);
__ fmv_x_d(a5, fa1);
__ fmv_d_x(fa1, a4);
__ fmv_d_x(ft0, a5);
// Store the swapped ft0 and fa1 back to memory.
__ fsd(ft0, a0, offsetof(T, a));
__ fsd(fa1, a0, offsetof(T, c));
// Test sign extension of move operations from coprocessor.
__ flw(ft0, a0, offsetof(T, d));
__ fmv_x_w(a4, ft0);
__ sd(a4, a0, offsetof(T, e));
};
auto f = AssembleCode<F3>(fn);
t.a = 1.5e22;
t.b = 2.75e11;
t.c = 17.17;
t.d = -2.75e11;
f.Call(&t, 0, 0, 0, 0);
CHECK_EQ(2.75e11, t.a);
CHECK_EQ(2.75e11, t.b);
CHECK_EQ(1.5e22, t.c);
CHECK_EQ(static_cast<int64_t>(0xFFFFFFFFD2800E8EL), t.e);
}
TEST(RISCV5) {
// Test conversions between doubles and
// integers.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
struct T {
double a;
double b;
int i;
int j;
} t;
auto fn = [](MacroAssembler& assm) {
// Load all structure elements to registers.
__ fld(ft0, a0, offsetof(T, a));
__ fld(ft1, a0, offsetof(T, b));
__ lw(a4, a0, offsetof(T, i));
__ lw(a5, a0, offsetof(T, j));
// Convert double in ft0 to int in element i.
__ fcvt_l_d(a6, ft0);
__ sw(a6, a0, offsetof(T, i));
// Convert double in ft1 to int in element j.
__ fcvt_l_d(a7, ft1);
__ sw(a7, a0, offsetof(T, j));
// Convert int in original i (a4) to double in a.
__ fcvt_d_l(fa0, a4);
__ fsd(fa0, a0, offsetof(T, a));
// Convert int in original j (a5) to double in b.
__ fcvt_d_l(fa1, a5);
__ fsd(fa1, a0, offsetof(T, b));
};
auto f = AssembleCode<F3>(fn);
t.a = 1.5e4;
t.b = 2.75e8;
t.i = 12345678;
t.j = -100000;
f.Call(&t, 0, 0, 0, 0);
CHECK_EQ(12345678.0, t.a);
CHECK_EQ(-100000.0, t.b);
CHECK_EQ(15000, t.i);
CHECK_EQ(275000000, t.j);
}
TEST(RISCV6) {
// Test simple memory loads and stores.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
struct T {
uint32_t ui;
int32_t si;
int32_t r1;
int32_t r2;
int32_t r3;
int32_t r4;
int32_t r5;
int32_t r6;
} t;
auto fn = [](MacroAssembler& assm) {
// Basic word load/store.
__ lw(a4, a0, offsetof(T, ui));
__ sw(a4, a0, offsetof(T, r1));
// lh with positive data.
__ lh(a5, a0, offsetof(T, ui));
__ sw(a5, a0, offsetof(T, r2));
// lh with negative data.
__ lh(a6, a0, offsetof(T, si));
__ sw(a6, a0, offsetof(T, r3));
// lhu with negative data.
__ lhu(a7, a0, offsetof(T, si));
__ sw(a7, a0, offsetof(T, r4));
// Lb with negative data.
__ lb(t0, a0, offsetof(T, si));
__ sw(t0, a0, offsetof(T, r5));
// sh writes only 1/2 of word.
__ RV_li(t1, 0x33333333);
__ sw(t1, a0, offsetof(T, r6));
__ lhu(t1, a0, offsetof(T, si));
__ sh(t1, a0, offsetof(T, r6));
};
auto f = AssembleCode<F3>(fn);
t.ui = 0x11223344;
t.si = 0x99AABBCC;
f.Call(&t, 0, 0, 0, 0);
CHECK_EQ(static_cast<int32_t>(0x11223344), t.r1);
if (kArchEndian == kLittle) {
CHECK_EQ(static_cast<int32_t>(0x3344), t.r2);
CHECK_EQ(static_cast<int32_t>(0xFFFFBBCC), t.r3);
CHECK_EQ(static_cast<int32_t>(0x0000BBCC), t.r4);
CHECK_EQ(static_cast<int32_t>(0xFFFFFFCC), t.r5);
CHECK_EQ(static_cast<int32_t>(0x3333BBCC), t.r6);
} else {
CHECK_EQ(static_cast<int32_t>(0x1122), t.r2);
CHECK_EQ(static_cast<int32_t>(0xFFFF99AA), t.r3);
CHECK_EQ(static_cast<int32_t>(0x000099AA), t.r4);
CHECK_EQ(static_cast<int32_t>(0xFFFFFF99), t.r5);
CHECK_EQ(static_cast<int32_t>(0x99AA3333), t.r6);
}
}
// pair.first is the F_TYPE input to test, pair.second is I_TYPE expected result
template <typename T>
static const std::vector<std::pair<T, uint64_t>> fclass_test_values() {
static const std::pair<T, uint64_t> kValues[] = {
std::make_pair(-std::numeric_limits<T>::infinity(), kNegativeInfinity),
std::make_pair(-10240.56, kNegativeNormalNumber),
std::make_pair(-(std::numeric_limits<T>::min() / 2),
kNegativeSubnormalNumber),
std::make_pair(-0.0, kNegativeZero),
std::make_pair(+0.0, kPositiveZero),
std::make_pair((std::numeric_limits<T>::min() / 2),
kPositiveSubnormalNumber),
std::make_pair(10240.56, kPositiveNormalNumber),
std::make_pair(std::numeric_limits<T>::infinity(), kPositiveInfinity),
std::make_pair(std::numeric_limits<T>::signaling_NaN(), kSignalingNaN),
std::make_pair(std::numeric_limits<T>::quiet_NaN(), kQuietNaN)};
return std::vector<std::pair<T, uint64_t>>(&kValues[0],
&kValues[arraysize(kValues)]);
}
TEST(FCLASS) {
CcTest::InitializeVM();
{
auto i_vec = fclass_test_values<float>();
for (auto i = i_vec.begin(); i != i_vec.end(); ++i) {
auto input = *i;
auto fn = [](MacroAssembler& assm) { __ fclass_s(a0, fa0); };
auto res = GenAndRunTest<uint32_t>(input.first, fn);
CHECK_EQ(input.second, res);
}
}
{
auto i_vec = fclass_test_values<double>();
for (auto i = i_vec.begin(); i != i_vec.end(); ++i) {
auto input = *i;
auto fn = [](MacroAssembler& assm) { __ fclass_d(a0, fa0); };
auto res = GenAndRunTest<uint32_t>(input.first, fn);
CHECK_EQ(input.second, res);
}
}
}
TEST(RISCV7) {
// Test floating point compare and
// branch instructions.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
struct T {
double a;
double b;
double c;
double d;
double e;
double f;
int32_t result;
} t;
// Create a function that accepts &t,
// and loads, manipulates, and stores
// the doubles t.a ... t.f.
Label neither_is_nan, less_than, outa_here;
auto fn = [&neither_is_nan, &less_than, &outa_here](MacroAssembler& assm) {
__ fld(ft0, a0, offsetof(T, a));
__ fld(ft1, a0, offsetof(T, b));
__ fclass_d(t5, ft0);
__ fclass_d(t6, ft1);
__ or_(t5, t5, t6);
__ andi(t5, t5, kSignalingNaN | kQuietNaN);
__ beq(t5, zero_reg, &neither_is_nan);
__ sw(zero_reg, a0, offsetof(T, result));
__ j(&outa_here);
__ bind(&neither_is_nan);
__ flt_d(t5, ft1, ft0);
__ bne(t5, zero_reg, &less_than);
__ sw(zero_reg, a0, offsetof(T, result));
__ j(&outa_here);
__ bind(&less_than);
__ RV_li(a4, 1);
__ sw(a4, a0, offsetof(T, result)); // Set true.
// This test-case should have additional
// tests.
__ bind(&outa_here);
};
auto f = AssembleCode<F3>(fn);
t.a = 1.5e14;
t.b = 2.75e11;
t.c = 2.0;
t.d = -4.0;
t.e = 0.0;
t.f = 0.0;
t.result = 0;
f.Call(&t, 0, 0, 0, 0);
CHECK_EQ(1.5e14, t.a);
CHECK_EQ(2.75e11, t.b);
CHECK_EQ(1, t.result);
}
TEST(RISCV9) {
// Test BRANCH improvements.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes);
Label exit, exit2, exit3;
__ Branch(&exit, ge, a0, Operand(zero_reg));
__ Branch(&exit2, ge, a0, Operand(0x00001FFF));
__ Branch(&exit3, ge, a0, Operand(0x0001FFFF));
__ bind(&exit);
__ bind(&exit2);
__ bind(&exit3);
__ jr(ra);
CodeDesc desc;
assm.GetCode(isolate, &desc);
Handle<Code> code =
Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build();
USE(code);
}
TEST(NAN_BOX) {
// Test float NaN-boxing.
CcTest::InitializeVM();
// Test NaN boxing in FMV.X.D
{
auto fn = [](MacroAssembler& assm) { __ fmv_x_d(a0, fa0); };
auto res = GenAndRunTest<uint64_t>(1234.56f, fn);
CHECK_EQ(0xFFFFFFFF00000000 | bit_cast<uint32_t>(1234.56f), res);
}
// Test NaN boxing in FMV.X.W
{
auto fn = [](MacroAssembler& assm) { __ fmv_x_w(a0, fa0); };
auto res = GenAndRunTest<uint64_t>(1234.56f, fn);
CHECK_EQ((uint64_t)bit_cast<uint32_t>(1234.56f), res);
}
// Test FLW and FSW
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
struct T {
float a;
uint64_t box;
uint64_t res;
} t;
auto fn = [](MacroAssembler& assm) {
// Load all structure elements to registers.
__ flw(fa0, a0, offsetof(T, a));
// Check boxing when flw
__ fsd(fa0, a0, offsetof(T, box));
// Check only transfer low 32bits when fsw
__ fsw(fa0, a0, offsetof(T, res));
};
auto f = AssembleCode<F3>(fn);
t.a = -123.45;
t.box = 0;
t.res = 0;
f.Call(&t, 0, 0, 0, 0);
CHECK_EQ(0xFFFFFFFF00000000 | bit_cast<int32_t>(t.a), t.box);
CHECK_EQ((uint64_t)bit_cast<uint32_t>(t.a), t.res);
}
TEST(RVC_CI) {
// Test RV64C extension CI type instructions.
i::FLAG_riscv_c_extension = true;
CcTest::InitializeVM();
// Test c.addi
{
auto fn = [](MacroAssembler& assm) { __ c_addi(a0, -15); };
auto res = GenAndRunTest<int64_t>(LARGE_INT_EXCEED_32_BIT, fn);
CHECK_EQ(LARGE_INT_EXCEED_32_BIT - 15, res);
}
// Test c.addiw
{
auto fn = [](MacroAssembler& assm) { __ c_addiw(a0, -20); };
auto res = GenAndRunTest<int32_t>(LARGE_INT_UNDER_32_BIT, fn);
CHECK_EQ(LARGE_INT_UNDER_32_BIT - 20, res);
}
// Test c.addi16sp
{
auto fn = [](MacroAssembler& assm) {
__ mv(t1, sp);
__ mv(sp, a0);
__ c_addi16sp(-432);
__ mv(a0, sp);
__ mv(sp, t1);
};
auto res = GenAndRunTest<int64_t>(66666, fn);
CHECK_EQ(66666 - 432, res);
}
// Test c.li
{
auto fn = [](MacroAssembler& assm) { __ c_li(a0, -15); };
auto res = GenAndRunTest<int64_t>(1234543, fn);
CHECK_EQ(-15, res);
}
// Test c.lui
{
auto fn = [](MacroAssembler& assm) { __ c_lui(a0, -20); };
auto res = GenAndRunTest<int64_t>(0x1234567, fn);
CHECK_EQ(0xfffffffffffec000, (uint64_t)res);
}
// Test c.slli
{
auto fn = [](MacroAssembler& assm) { __ c_slli(a0, 13); };
auto res = GenAndRunTest<int64_t>(0x1234'5678ULL, fn);
CHECK_EQ(0x1234'5678ULL << 13, res);
}
}
TEST(RVC_CIW) {
i::FLAG_riscv_c_extension = true;
CcTest::InitializeVM();
// Test c.addi4spn
{
auto fn = [](MacroAssembler& assm) {
__ mv(t1, sp);
__ mv(sp, a0);
__ c_addi4spn(a0, 924);
__ mv(sp, t1);
};
auto res = GenAndRunTest<int64_t>(66666, fn);
CHECK_EQ(66666 + 924, res);
}
}
TEST(RVC_CR) {
// Test RV64C extension CR type instructions.
i::FLAG_riscv_c_extension = true;
CcTest::InitializeVM();
// Test c.add
{
auto fn = [](MacroAssembler& assm) {
__ RV_li(a1, MIN_VAL_IMM12);
__ c_add(a0, a1);
};
auto res = GenAndRunTest<int64_t>(LARGE_INT_EXCEED_32_BIT, fn);
CHECK_EQ(LARGE_INT_EXCEED_32_BIT + MIN_VAL_IMM12, res);
}
}
TEST(RVC_CA) {
// Test RV64C extension CA type instructions.
i::FLAG_riscv_c_extension = true;
CcTest::InitializeVM();
// Test c.sub
{
auto fn = [](MacroAssembler& assm) {
__ RV_li(a1, MIN_VAL_IMM12);
__ c_sub(a0, a1);
};
auto res = GenAndRunTest<int64_t>(LARGE_INT_UNDER_32_BIT, fn);
CHECK_EQ(LARGE_INT_UNDER_32_BIT - MIN_VAL_IMM12, res);
}
// Test c.xor
{
auto fn = [](MacroAssembler& assm) {
__ RV_li(a1, MIN_VAL_IMM12);
__ c_xor(a0, a1);
};
auto res = GenAndRunTest<int64_t>(LARGE_INT_UNDER_32_BIT, fn);
CHECK_EQ(LARGE_INT_UNDER_32_BIT ^ MIN_VAL_IMM12, res);
}
// Test c.or
{
auto fn = [](MacroAssembler& assm) {
__ RV_li(a1, MIN_VAL_IMM12);
__ c_or(a0, a1);
};
auto res = GenAndRunTest<int64_t>(LARGE_INT_UNDER_32_BIT, fn);
CHECK_EQ(LARGE_INT_UNDER_32_BIT | MIN_VAL_IMM12, res);
}
// Test c.and
{
auto fn = [](MacroAssembler& assm) {
__ RV_li(a1, MIN_VAL_IMM12);
__ c_and(a0, a1);
};
auto res = GenAndRunTest<int64_t>(LARGE_INT_UNDER_32_BIT, fn);
CHECK_EQ(LARGE_INT_UNDER_32_BIT & MIN_VAL_IMM12, res);
}
// Test c.subw
{
auto fn = [](MacroAssembler& assm) {
__ RV_li(a1, MIN_VAL_IMM12);
__ c_subw(a0, a1);
};
auto res = GenAndRunTest<int64_t>(LARGE_INT_UNDER_32_BIT, fn);
CHECK_EQ(LARGE_INT_UNDER_32_BIT - MIN_VAL_IMM12, res);
}
// Test c.addw
{
auto fn = [](MacroAssembler& assm) {
__ RV_li(a1, MIN_VAL_IMM12);
__ c_addw(a0, a1);
};
auto res = GenAndRunTest<int64_t>(LARGE_INT_UNDER_32_BIT, fn);
CHECK_EQ(LARGE_INT_UNDER_32_BIT + MIN_VAL_IMM12, res);
}
}
TEST(RVC_LOAD_STORE_SP) {
// Test RV64C extension fldsp/fsdsp, lwsp/swsp, ldsp/sdsp.
i::FLAG_riscv_c_extension = true;
CcTest::InitializeVM();
{
auto fn = [](MacroAssembler& assm) {
__ c_fsdsp(fa0, 80);
__ c_fldsp(fa0, 80);
};
auto res = GenAndRunTest<double>(-3456.678, fn);
CHECK_EQ(-3456.678, res);
}
{
auto fn = [](MacroAssembler& assm) {
__ c_swsp(a0, 40);
__ c_lwsp(a0, 40);
};
auto res = GenAndRunTest<int32_t>(0x456AF894, fn);
CHECK_EQ(0x456AF894, res);
}
{
auto fn = [](MacroAssembler& assm) {
__ c_sdsp(a0, 160);
__ c_ldsp(a0, 160);
};
auto res = GenAndRunTest<uint64_t>(0xFBB10A9C12345678, fn);
CHECK_EQ(0xFBB10A9C12345678, res);
}
}
TEST(RVC_LOAD_STORE_COMPRESSED) {
// Test RV64C extension fld, lw, ld.
i::FLAG_riscv_c_extension = true;
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
struct T {
double a;
double b;
double c;
} t;
// c.fld
{
auto fn = [](MacroAssembler& assm) {
__ c_fld(fa0, a0, offsetof(T, a));
__ c_fld(fa1, a0, offsetof(T, b));
__ fadd_d(fa2, fa1, fa0);
__ c_fsd(fa2, a0, offsetof(T, c)); // c = a + b.
};
auto f = AssembleCode<F3>(fn);
t.a = 1.5e14;
t.b = 1.5e14;
t.c = 3.0e14;
f.Call(&t, 0, 0, 0, 0);
// Expected double results.
CHECK_EQ(1.5e14, t.a);
CHECK_EQ(1.5e14, t.b);
CHECK_EQ(3.0e14, t.c);
}
struct S {
int32_t a;
int32_t b;
int32_t c;
} s;
// c.lw
{
auto fn = [](MacroAssembler& assm) {
__ c_lw(a1, a0, offsetof(S, a));
__ c_lw(a2, a0, offsetof(S, b));
__ add(a3, a1, a2);
__ c_sw(a3, a0, offsetof(S, c)); // c = a + b.
};
auto f = AssembleCode<F3>(fn);
s.a = 1;
s.b = 2;
s.c = 3;
f.Call(&s, 0, 0, 0, 0);
CHECK_EQ(1, s.a);
CHECK_EQ(2, s.b);
CHECK_EQ(3, s.c);
}
struct U {
int64_t a;
int64_t b;
int64_t c;
} u;
// c.ld
{
auto fn = [](MacroAssembler& assm) {
__ c_ld(a1, a0, offsetof(U, a));
__ c_ld(a2, a0, offsetof(U, b));
__ add(a3, a1, a2);
__ c_sd(a3, a0, offsetof(U, c)); // c = a + b.
};
auto f = AssembleCode<F3>(fn);
u.a = 1;
u.b = 2;
u.c = 3;
f.Call(&u, 0, 0, 0, 0);
CHECK_EQ(1, u.a);
CHECK_EQ(2, u.b);
CHECK_EQ(3, u.c);
}
}
TEST(RVC_JUMP) {
i::FLAG_riscv_c_extension = true;
CcTest::InitializeVM();
Label L, C;
auto fn = [&L, &C](MacroAssembler& assm) {
__ mv(a1, a0);
__ RV_li(a0, 0l);
__ c_j(&C);
__ bind(&L);
__ add(a0, a0, a1);
__ addi(a1, a1, -1);
__ bind(&C);
__ xori(a2, a1, 0);
__ bnez(a2, &L);
};
int64_t input = 50;
int64_t expected_res = 1275L;
auto res = GenAndRunTest<int64_t>(input, fn);
CHECK_EQ(expected_res, res);
}
TEST(RVC_CB) {
// Test RV64C extension CI type instructions.
FLAG_riscv_c_extension = true;
CcTest::InitializeVM();
// Test c.srai
{
auto fn = [](MacroAssembler& assm) { __ c_srai(a0, 13); };
auto res = GenAndRunTest<int64_t>(0x1234'5678ULL, fn);
CHECK_EQ(0x1234'5678ULL >> 13, res);
}
// Test c.srli
{
auto fn = [](MacroAssembler& assm) { __ c_srli(a0, 13); };
auto res = GenAndRunTest<int64_t>(0x1234'5678ULL, fn);
CHECK_EQ(0x1234'5678ULL >> 13, res);
}
// Test c.andi
{
auto fn = [](MacroAssembler& assm) { __ c_andi(a0, 13); };
auto res = GenAndRunTest<int64_t>(LARGE_INT_EXCEED_32_BIT, fn);
CHECK_EQ(LARGE_INT_EXCEED_32_BIT & 13, res);
}
}
TEST(RVC_CB_BRANCH) {
FLAG_riscv_c_extension = true;
// Test floating point compare and
// branch instructions.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
struct T {
double a;
double b;
double c;
double d;
double e;
double f;
int32_t result;
} t;
// Create a function that accepts &t,
// and loads, manipulates, and stores
// the doubles t.a ... t.f.
Label neither_is_nan, less_than, outa_here;
auto fn = [&neither_is_nan, &less_than, &outa_here](MacroAssembler& assm) {
__ fld(ft0, a0, offsetof(T, a));
__ fld(ft1, a0, offsetof(T, b));
__ fclass_d(t5, ft0);
__ fclass_d(t6, ft1);
__ or_(a1, t5, t6);
__ andi(a1, a1, kSignalingNaN | kQuietNaN);
__ c_beqz(a1, &neither_is_nan);
__ sw(zero_reg, a0, offsetof(T, result));
__ j(&outa_here);
__ bind(&neither_is_nan);
__ flt_d(a1, ft1, ft0);
__ c_bnez(a1, &less_than);
__ sw(zero_reg, a0, offsetof(T, result));
__ j(&outa_here);
__ bind(&less_than);
__ RV_li(a4, 1);
__ sw(a4, a0, offsetof(T, result)); // Set true.
// This test-case should have additional
// tests.
__ bind(&outa_here);
};
auto f = AssembleCode<F3>(fn);
t.a = 1.5e14;
t.b = 2.75e11;
t.c = 2.0;
t.d = -4.0;
t.e = 0.0;
t.f = 0.0;
t.result = 0;
f.Call(&t, 0, 0, 0, 0);
CHECK_EQ(1.5e14, t.a);
CHECK_EQ(2.75e11, t.b);
CHECK_EQ(1, t.result);
}
TEST(TARGET_ADDR) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
// This is the series of instructions to load 48 bit address 0x0123456789ab
uint32_t buffer[6] = {0x091ab37, 0x2b330213, 0x00b21213,
0x62626213, 0x00621213, 0x02b26213};
MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes);
uintptr_t addr = reinterpret_cast<uintptr_t>(&buffer[0]);
Address res = __ target_address_at(static_cast<Address>(addr));
CHECK_EQ(0x0123456789abL, res);
}
TEST(SET_TARGET_ADDR) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
// This is the series of instructions to load 48 bit address 0xba9876543210
uint32_t buffer[6] = {0x091ab37, 0x2b330213, 0x00b21213,
0x62626213, 0x00621213, 0x02b26213};
MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes);
uintptr_t addr = reinterpret_cast<uintptr_t>(&buffer[0]);
__ set_target_value_at(static_cast<Address>(addr), 0xba9876543210L,
FLUSH_ICACHE_IF_NEEDED);
Address res = __ target_address_at(static_cast<Address>(addr));
CHECK_EQ(0xba9876543210L, res);
}
// pair.first is the F_TYPE input to test, pair.second is I_TYPE expected
// result
template <typename F_TYPE, typename I_TYPE>
static const std::vector<std::pair<F_TYPE, I_TYPE>> out_of_range_test_values() {
static const std::pair<F_TYPE, I_TYPE> kValues[] = {
std::make_pair(std::numeric_limits<F_TYPE>::quiet_NaN(),
std::numeric_limits<I_TYPE>::max()),
std::make_pair(std::numeric_limits<F_TYPE>::signaling_NaN(),
std::numeric_limits<I_TYPE>::max()),
std::make_pair(std::numeric_limits<F_TYPE>::infinity(),
std::numeric_limits<I_TYPE>::max()),
std::make_pair(-std::numeric_limits<F_TYPE>::infinity(),
std::numeric_limits<I_TYPE>::min()),
std::make_pair(
static_cast<F_TYPE>(std::numeric_limits<I_TYPE>::max()) + 1024,
std::numeric_limits<I_TYPE>::max()),
std::make_pair(
static_cast<F_TYPE>(std::numeric_limits<I_TYPE>::min()) - 1024,
std::numeric_limits<I_TYPE>::min()),
};
return std::vector<std::pair<F_TYPE, I_TYPE>>(&kValues[0],
&kValues[arraysize(kValues)]);
}
// Test conversion from wider to narrower types w/ out-of-range values or from
// nan, inf, -inf
TEST(OUT_OF_RANGE_CVT) {
CcTest::InitializeVM();
{ // test fvt_w_d
auto i_vec = out_of_range_test_values<double, int32_t>();
for (auto i = i_vec.begin(); i != i_vec.end(); ++i) {
auto input = *i;
auto fn = [](MacroAssembler& assm) { __ fcvt_w_d(a0, fa0); };
auto res = GenAndRunTest<int32_t>(input.first, fn);
CHECK_EQ(input.second, res);
}
}
{ // test fvt_w_s
auto i_vec = out_of_range_test_values<float, int32_t>();
for (auto i = i_vec.begin(); i != i_vec.end(); ++i) {
auto input = *i;
auto fn = [](MacroAssembler& assm) { __ fcvt_w_s(a0, fa0); };
auto res = GenAndRunTest<int32_t>(input.first, fn);
CHECK_EQ(input.second, res);
}
}
{ // test fvt_wu_d
auto i_vec = out_of_range_test_values<double, uint32_t>();
for (auto i = i_vec.begin(); i != i_vec.end(); ++i) {
auto input = *i;
auto fn = [](MacroAssembler& assm) { __ fcvt_wu_d(a0, fa0); };
auto res = GenAndRunTest<uint32_t>(input.first, fn);
CHECK_EQ(input.second, res);
}
}
{ // test fvt_wu_s
auto i_vec = out_of_range_test_values<float, uint32_t>();
for (auto i = i_vec.begin(); i != i_vec.end(); ++i) {
auto input = *i;
auto fn = [](MacroAssembler& assm) { __ fcvt_wu_s(a0, fa0); };
auto res = GenAndRunTest<uint32_t>(input.first, fn);
CHECK_EQ(input.second, res);
}
}
{ // test fvt_l_d
auto i_vec = out_of_range_test_values<double, int64_t>();
for (auto i = i_vec.begin(); i != i_vec.end(); ++i) {
auto input = *i;
auto fn = [](MacroAssembler& assm) { __ fcvt_l_d(a0, fa0); };
auto res = GenAndRunTest<int64_t>(input.first, fn);
CHECK_EQ(input.second, res);
}
}
{ // test fvt_l_s
auto i_vec = out_of_range_test_values<float, int64_t>();
for (auto i = i_vec.begin(); i != i_vec.end(); ++i) {
auto input = *i;
auto fn = [](MacroAssembler& assm) { __ fcvt_l_s(a0, fa0); };
auto res = GenAndRunTest<int64_t>(input.first, fn);
CHECK_EQ(input.second, res);
}
}
{ // test fvt_lu_d
auto i_vec = out_of_range_test_values<double, uint64_t>();
for (auto i = i_vec.begin(); i != i_vec.end(); ++i) {
auto input = *i;
auto fn = [](MacroAssembler& assm) { __ fcvt_lu_d(a0, fa0); };
auto res = GenAndRunTest<uint64_t>(input.first, fn);
CHECK_EQ(input.second, res);
}
}
{ // test fvt_lu_s
auto i_vec = out_of_range_test_values<float, uint64_t>();
for (auto i = i_vec.begin(); i != i_vec.end(); ++i) {
auto input = *i;
auto fn = [](MacroAssembler& assm) { __ fcvt_lu_s(a0, fa0); };
auto res = GenAndRunTest<uint64_t>(input.first, fn);
CHECK_EQ(input.second, res);
}
}
}
#define FCMP_TEST_HELPER(F, fn, op) \
{ \
auto res1 = GenAndRunTest<int32_t>(std::numeric_limits<F>::quiet_NaN(), \
static_cast<F>(1.0), fn); \
CHECK_EQ(false, res1); \
auto res2 = \
GenAndRunTest<int32_t>(std::numeric_limits<F>::quiet_NaN(), \
std::numeric_limits<F>::quiet_NaN(), fn); \
CHECK_EQ(false, res2); \
auto res3 = \
GenAndRunTest<int32_t>(std::numeric_limits<F>::signaling_NaN(), \
std::numeric_limits<F>::quiet_NaN(), fn); \
CHECK_EQ(false, res3); \
auto res4 = \
GenAndRunTest<int32_t>(std::numeric_limits<F>::quiet_NaN(), \
std::numeric_limits<F>::infinity(), fn); \
CHECK_EQ(false, res4); \
auto res5 = \
GenAndRunTest<int32_t>(std::numeric_limits<F>::infinity(), \
std::numeric_limits<F>::infinity(), fn); \
CHECK_EQ((std::numeric_limits<F>::infinity() \
op std::numeric_limits<F>::infinity()), \
res5); \
auto res6 = \
GenAndRunTest<int32_t>(-std::numeric_limits<F>::infinity(), \
std::numeric_limits<F>::infinity(), fn); \
CHECK_EQ((-std::numeric_limits<F>::infinity() \
op std::numeric_limits<F>::infinity()), \
res6); \
}
TEST(F_NAN) {
// test floating-point compare w/ NaN, +/-Inf
CcTest::InitializeVM();
// floating compare
auto fn1 = [](MacroAssembler& assm) { __ feq_s(a0, fa0, fa1); };
FCMP_TEST_HELPER(float, fn1, ==);
auto fn2 = [](MacroAssembler& assm) { __ flt_s(a0, fa0, fa1); };
FCMP_TEST_HELPER(float, fn2, <);
auto fn3 = [](MacroAssembler& assm) { __ fle_s(a0, fa0, fa1); };
FCMP_TEST_HELPER(float, fn3, <=);
// double compare
auto fn4 = [](MacroAssembler& assm) { __ feq_d(a0, fa0, fa1); };
FCMP_TEST_HELPER(double, fn4, ==);
auto fn5 = [](MacroAssembler& assm) { __ flt_d(a0, fa0, fa1); };
FCMP_TEST_HELPER(double, fn5, <);
auto fn6 = [](MacroAssembler& assm) { __ fle_d(a0, fa0, fa1); };
FCMP_TEST_HELPER(double, fn6, <=);
}
TEST(jump_tables1) {
// Test jump tables with forward jumps.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
const int kNumCases = 128;
int values[kNumCases];
isolate->random_number_generator()->NextBytes(values, sizeof(values));
Label labels[kNumCases], done;
auto fn = [&labels, &done, values](MacroAssembler& assm) {
__ addi(sp, sp, -8);
__ Sd(ra, MemOperand(sp));
__ Align(8);
{
__ BlockTrampolinePoolFor(kNumCases * 2 + 6);
__ auipc(ra, 0);
__ slli(t3, a0, 3);
__ add(t3, t3, ra);
__ Ld(t3, MemOperand(t3, 6 * kInstrSize));
__ jr(t3);
__ nop(); // For 16-byte alignment
for (int i = 0; i < kNumCases; ++i) {
__ dd(&labels[i]);
}
}
for (int i = 0; i < kNumCases; ++i) {
__ bind(&labels[i]);
__ RV_li(a0, values[i]);
__ j(&done);
}
__ bind(&done);
__ Ld(ra, MemOperand(sp));
__ addi(sp, sp, 8);
CHECK_EQ(0, assm.UnboundLabelsCount());
};
auto f = AssembleCode<F1>(fn);
for (int i = 0; i < kNumCases; ++i) {
int64_t res = reinterpret_cast<int64_t>(f.Call(i, 0, 0, 0, 0));
CHECK_EQ(values[i], static_cast<int>(res));
}
}
TEST(jump_tables2) {
// Test jump tables with backward jumps.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
const int kNumCases = 128;
int values[kNumCases];
isolate->random_number_generator()->NextBytes(values, sizeof(values));
Label labels[kNumCases], done, dispatch;
auto fn = [&labels, &done, &dispatch, values](MacroAssembler& assm) {
__ addi(sp, sp, -8);
__ Sd(ra, MemOperand(sp));
__ j(&dispatch);
for (int i = 0; i < kNumCases; ++i) {
__ bind(&labels[i]);
__ RV_li(a0, values[i]);
__ j(&done);
}
__ Align(8);
__ bind(&dispatch);
{
__ BlockTrampolinePoolFor(kNumCases * 2 + 6);
__ auipc(ra, 0);
__ slli(t3, a0, 3);
__ add(t3, t3, ra);
__ Ld(t3, MemOperand(t3, 6 * kInstrSize));
__ jr(t3);
__ nop(); // For 16-byte alignment
for (int i = 0; i < kNumCases; ++i) {
__ dd(&labels[i]);
}
}
__ bind(&done);
__ Ld(ra, MemOperand(sp));
__ addi(sp, sp, 8);
};
auto f = AssembleCode<F1>(fn);
for (int i = 0; i < kNumCases; ++i) {
int64_t res = reinterpret_cast<int64_t>(f.Call(i, 0, 0, 0, 0));
CHECK_EQ(values[i], res);
}
}
TEST(jump_tables3) {
// Test jump tables with backward jumps and embedded heap objects.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
const int kNumCases = 128;
Handle<Object> values[kNumCases];
for (int i = 0; i < kNumCases; ++i) {
double value = isolate->random_number_generator()->NextDouble();
values[i] = isolate->factory()->NewHeapNumber<AllocationType::kOld>(value);
}
Label labels[kNumCases], done, dispatch;
Object obj;
int64_t imm64;
auto fn = [&labels, &done, &dispatch, values, &obj,
&imm64](MacroAssembler& assm) {
__ addi(sp, sp, -8);
__ Sd(ra, MemOperand(sp));
__ j(&dispatch);
for (int i = 0; i < kNumCases; ++i) {
__ bind(&labels[i]);
obj = *values[i];
imm64 = obj.ptr();
__ nop(); // For 8 byte alignment
__ RV_li(a0, imm64);
__ nop(); // For 8 byte alignment
__ j(&done);
}
__ bind(&dispatch);
{
__ BlockTrampolinePoolFor(kNumCases * 2 + 6);
__ Align(8);
__ auipc(ra, 0);
__ slli(t3, a0, 3);
__ add(t3, t3, ra);
__ Ld(t3, MemOperand(t3, 6 * kInstrSize));
__ jr(t3);
__ nop(); // For 16-byte alignment
for (int i = 0; i < kNumCases; ++i) {
__ dd(&labels[i]);
}
}
__ bind(&done);
__ Ld(ra, MemOperand(sp));
__ addi(sp, sp, 8);
};
auto f = AssembleCode<F1>(fn);
for (int i = 0; i < kNumCases; ++i) {
Handle<Object> result(
Object(reinterpret_cast<Address>(f.Call(i, 0, 0, 0, 0))), isolate);
#ifdef OBJECT_PRINT
::printf("f(%d) = ", i);
result->Print(std::cout);
::printf("\n");
#endif
CHECK(values[i].is_identical_to(result));
}
}
TEST(li_estimate) {
std::vector<int64_t> immediates = {
-256, -255, 0, 255, 8192, 0x7FFFFFFF,
INT32_MIN, INT32_MAX / 2, INT32_MAX, UINT32_MAX, INT64_MAX, INT64_MAX / 2,
INT64_MIN};
// Test jump tables with backward jumps and embedded heap objects.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes);
for (auto p : immediates) {
Label a;
assm.bind(&a);
assm.RV_li(t0, p);
int expected_count = assm.li_estimate(p, true);
int count = assm.InstructionsGeneratedSince(&a);
CHECK_EQ(count, expected_count);
}
}
#define UTEST_LOAD_STORE_RVV(ldname, stname, SEW, arg...) \
TEST(RISCV_UTEST_##stname##ldname##SEW) { \
CcTest::InitializeVM(); \
Isolate* isolate = CcTest::i_isolate(); \
HandleScope scope(isolate); \
int8_t src[16] = {arg}; \
int8_t dst[16]; \
auto fn = [](MacroAssembler& assm) { \
__ VU.set(t0, SEW, Vlmul::m1); \
__ vl(v2, a0, 0, VSew::E8); \
__ vs(v2, a1, 0, VSew::E8); \
}; \
GenAndRunTest<int32_t, int64_t>((int64_t)src, (int64_t)dst, fn); \
CHECK(!memcmp(src, dst, sizeof(src))); \
}
#ifdef CAN_USE_RVV_INSTRUCTIONS
UTEST_LOAD_STORE_RVV(vl, vs, E8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16)
// UTEST_LOAD_STORE_RVV(vl, vs, E8, 127, 127, 127, 127, 127, 127, 127)
TEST(RVV_VSETIVLI) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
auto fn = [](MacroAssembler& assm) {
__ VU.set(t0, VSew::E8, Vlmul::m1);
__ vsetivli(t0, 16, VSew::E128, Vlmul::m1);
};
GenAndRunTest(fn);
}
TEST(RVV_VFMV) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
for (float a : compiler::ValueHelper::GetVector<float>()) {
float src = a;
float dst[8] = {0};
float ref[8] = {a, a, a, a, a, a, a, a};
auto fn = [](MacroAssembler& assm) {
__ VU.set(t0, VSew::E32, Vlmul::m2);
__ flw(fa1, a0, 0);
__ vfmv_vf(v2, fa1);
__ vs(v2, a1, 0, VSew::E32);
};
GenAndRunTest<int32_t, int64_t>((int64_t)&src, (int64_t)dst, fn);
CHECK(!memcmp(ref, dst, sizeof(ref)));
}
}
inline int32_t ToImm5(int32_t v) {
int32_t smax = (int32_t)(INT64_MAX >> (64 - 5));
int32_t smin = (int32_t)(INT64_MIN >> (64 - 5));
return (v > smax) ? smax : ((v < smin) ? smin : v);
}
// Tests for vector integer arithmetic instructions between vector and vector
#define UTEST_RVV_VI_VV_FORM_WITH_RES(instr_name, width, array, expect_res) \
TEST(RISCV_UTEST_##instr_name##_##width) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { \
__ VU.set(t0, VSew::E##width, Vlmul::m1); \
__ vmv_vx(v0, a0); \
__ vmv_vx(v1, a1); \
__ instr_name(v0, v0, v1); \
__ vmv_xs(a0, v0); \
}; \
for (int##width##_t rs1_val : array) { \
for (int##width##_t rs2_val : array) { \
auto res = GenAndRunTest<int32_t, int32_t>(rs1_val, rs2_val, fn); \
CHECK_EQ(static_cast<int##width##_t>(expect_res), res); \
} \
} \
}
// Tests for vector integer arithmetic instructions between vector and scalar
#define UTEST_RVV_VI_VX_FORM_WITH_RES(instr_name, width, array, expect_res) \
TEST(RISCV_UTEST_##instr_name##_##width) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { \
__ VU.set(t0, VSew::E##width, Vlmul::m1); \
__ vmv_vx(v0, a0); \
__ instr_name(v0, v0, a1); \
__ vmv_xs(a0, v0); \
}; \
for (int##width##_t rs1_val : array) { \
for (int##width##_t rs2_val : array) { \
auto res = GenAndRunTest<int32_t, int32_t>(rs1_val, rs2_val, fn); \
CHECK_EQ(static_cast<int##width##_t>(expect_res), res); \
} \
} \
}
// Tests for vector integer arithmetic instructions between vector and 5-bit
// immediate
#define UTEST_RVV_VI_VI_FORM_WITH_RES(instr_name, width, array, expect_res) \
TEST(RISCV_UTEST_##instr_name##_##width) { \
CcTest::InitializeVM(); \
for (int##width##_t rs1_val : array) { \
for (int##width##_t rs2_val : array) { \
auto fn = [rs2_val](MacroAssembler& assm) { \
__ VU.set(t0, VSew::E##width, Vlmul::m1); \
__ vmv_vx(v0, a0); \
__ instr_name(v0, v0, ToImm5(rs2_val)); \
__ vmv_xs(a0, v0); \
}; \
auto res = GenAndRunTest<int32_t, int32_t>(rs1_val, fn); \
CHECK_EQ(static_cast<int##width##_t>(expect_res), res); \
} \
} \
}
#define UTEST_RVV_VI_VV_FORM_WITH_OP(instr_name, width, array, tested_op) \
UTEST_RVV_VI_VV_FORM_WITH_RES(instr_name, width, array, \
(int##width##_t)((rs1_val)tested_op(rs2_val)))
#define UTEST_RVV_VI_VX_FORM_WITH_OP(instr_name, width, array, tested_op) \
UTEST_RVV_VI_VX_FORM_WITH_RES(instr_name, width, array, \
(int##width##_t)((rs1_val)tested_op(rs2_val)))
#define UTEST_RVV_VI_VI_FORM_WITH_OP(instr_name, width, array, tested_op) \
UTEST_RVV_VI_VI_FORM_WITH_RES( \
instr_name, width, array, \
(int##width##_t)((rs1_val)tested_op(ToImm5(rs2_val))))
#define UTEST_RVV_VI_VV_FORM_WITH_FN(instr_name, width, array, tested_fn) \
UTEST_RVV_VI_VV_FORM_WITH_RES(instr_name, width, array, \
tested_fn(rs1_val, rs2_val))
#define UTEST_RVV_VI_VX_FORM_WITH_FN(instr_name, width, array, tested_fn) \
UTEST_RVV_VI_VX_FORM_WITH_RES(instr_name, width, array, \
tested_fn(rs1_val, rs2_val))
#define ARRAY_INT32 compiler::ValueHelper::GetVector<int32_t>()
#define VV(instr_name, array, tested_op) \
UTEST_RVV_VI_VV_FORM_WITH_OP(instr_name, 8, array, tested_op) \
UTEST_RVV_VI_VV_FORM_WITH_OP(instr_name, 16, array, tested_op) \
UTEST_RVV_VI_VV_FORM_WITH_OP(instr_name, 32, array, tested_op)
#define VX(instr_name, array, tested_op) \
UTEST_RVV_VI_VX_FORM_WITH_OP(instr_name, 8, array, tested_op) \
UTEST_RVV_VI_VX_FORM_WITH_OP(instr_name, 16, array, tested_op) \
UTEST_RVV_VI_VX_FORM_WITH_OP(instr_name, 32, array, tested_op)
#define VI(instr_name, array, tested_op) \
UTEST_RVV_VI_VI_FORM_WITH_OP(instr_name, 8, array, tested_op) \
UTEST_RVV_VI_VI_FORM_WITH_OP(instr_name, 16, array, tested_op) \
UTEST_RVV_VI_VI_FORM_WITH_OP(instr_name, 32, array, tested_op)
VV(vadd_vv, ARRAY_INT32, +)
VX(vadd_vx, ARRAY_INT32, +)
VI(vadd_vi, ARRAY_INT32, +)
VV(vsub_vv, ARRAY_INT32, -)
VX(vsub_vx, ARRAY_INT32, -)
VV(vand_vv, ARRAY_INT32, &)
VX(vand_vx, ARRAY_INT32, &)
VI(vand_vi, ARRAY_INT32, &)
VV(vor_vv, ARRAY_INT32, |)
VX(vor_vx, ARRAY_INT32, |)
VI(vor_vi, ARRAY_INT32, |)
VV(vxor_vv, ARRAY_INT32, ^)
VX(vxor_vx, ARRAY_INT32, ^)
VI(vxor_vi, ARRAY_INT32, ^)
UTEST_RVV_VI_VV_FORM_WITH_FN(vmax_vv, 8, ARRAY_INT32, std::max<int8_t>)
UTEST_RVV_VI_VX_FORM_WITH_FN(vmax_vx, 8, ARRAY_INT32, std::max<int8_t>)
UTEST_RVV_VI_VV_FORM_WITH_FN(vmax_vv, 16, ARRAY_INT32, std::max<int16_t>)
UTEST_RVV_VI_VX_FORM_WITH_FN(vmax_vx, 16, ARRAY_INT32, std::max<int16_t>)
UTEST_RVV_VI_VV_FORM_WITH_FN(vmax_vv, 32, ARRAY_INT32, std::max<int32_t>)
UTEST_RVV_VI_VX_FORM_WITH_FN(vmax_vx, 32, ARRAY_INT32, std::max<int32_t>)
UTEST_RVV_VI_VV_FORM_WITH_FN(vmin_vv, 8, ARRAY_INT32, std::min<int8_t>)
UTEST_RVV_VI_VX_FORM_WITH_FN(vmin_vx, 8, ARRAY_INT32, std::min<int8_t>)
UTEST_RVV_VI_VV_FORM_WITH_FN(vmin_vv, 16, ARRAY_INT32, std::min<int16_t>)
UTEST_RVV_VI_VX_FORM_WITH_FN(vmin_vx, 16, ARRAY_INT32, std::min<int16_t>)
UTEST_RVV_VI_VV_FORM_WITH_FN(vmin_vv, 32, ARRAY_INT32, std::min<int32_t>)
UTEST_RVV_VI_VX_FORM_WITH_FN(vmin_vx, 32, ARRAY_INT32, std::min<int32_t>)
UTEST_RVV_VI_VV_FORM_WITH_FN(vmaxu_vv, 8, ARRAY_INT32, std::max<uint8_t>)
UTEST_RVV_VI_VX_FORM_WITH_FN(vmaxu_vx, 8, ARRAY_INT32, std::max<uint8_t>)
UTEST_RVV_VI_VV_FORM_WITH_FN(vmaxu_vv, 16, ARRAY_INT32, std::max<uint16_t>)
UTEST_RVV_VI_VX_FORM_WITH_FN(vmaxu_vx, 16, ARRAY_INT32, std::max<uint16_t>)
UTEST_RVV_VI_VV_FORM_WITH_FN(vmaxu_vv, 32, ARRAY_INT32, std::max<uint32_t>)
UTEST_RVV_VI_VX_FORM_WITH_FN(vmaxu_vx, 32, ARRAY_INT32, std::max<uint32_t>)
UTEST_RVV_VI_VV_FORM_WITH_FN(vminu_vv, 8, ARRAY_INT32, std::min<uint8_t>)
UTEST_RVV_VI_VX_FORM_WITH_FN(vminu_vx, 8, ARRAY_INT32, std::min<uint8_t>)
UTEST_RVV_VI_VV_FORM_WITH_FN(vminu_vv, 16, ARRAY_INT32, std::min<uint16_t>)
UTEST_RVV_VI_VX_FORM_WITH_FN(vminu_vx, 16, ARRAY_INT32, std::min<uint16_t>)
UTEST_RVV_VI_VV_FORM_WITH_FN(vminu_vv, 32, ARRAY_INT32, std::min<uint32_t>)
UTEST_RVV_VI_VX_FORM_WITH_FN(vminu_vx, 32, ARRAY_INT32, std::min<uint32_t>)
#undef ARRAY_INT32
#undef VV
#undef VX
#undef VI
#undef UTEST_RVV_VI_VV_FORM_WITH_FN
#undef UTEST_RVV_VI_VX_FORM_WITH_FN
#undef UTEST_RVV_VI_VI_FORM_WITH_OP
#undef UTEST_RVV_VI_VX_FORM_WITH_OP
#undef UTEST_RVV_VI_VV_FORM_WITH_OP
#undef UTEST_RVV_VI_VI_FORM
#undef UTEST_RVV_VI_VX_FORM
#undef UTEST_RVV_VI_VV_FORM
// Tests for vector single-width floating-point arithmetic instructions between
// vector and vector
#define UTEST_RVV_VF_VV_FORM_WITH_RES(instr_name, array, expect_res) \
TEST(RISCV_UTEST_##instr_name) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { \
__ VU.set(t0, VSew::E32, Vlmul::m1); \
__ vfmv_vf(v0, fa0); \
__ vfmv_vf(v1, fa1); \
__ instr_name(v0, v0, v1); \
__ vfmv_fs(fa0, v0); \
}; \
for (float rs1_fval : array) { \
for (float rs2_fval : array) { \
auto res = GenAndRunTest<float, float>(rs1_fval, rs2_fval, fn); \
CHECK_FLOAT_EQ(UseCanonicalNan<float>(expect_res), res); \
} \
} \
}
// Tests for vector single-width floating-point arithmetic instructions between
// vector and scalar
#define UTEST_RVV_VF_VF_FORM_WITH_RES(instr_name, array, expect_res) \
TEST(RISCV_UTEST_##instr_name) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { \
__ VU.set(t0, VSew::E32, Vlmul::m1); \
__ vfmv_vf(v0, fa0); \
__ instr_name(v0, v0, fa1); \
__ vfmv_fs(fa0, v0); \
}; \
for (float rs1_fval : array) { \
for (float rs2_fval : array) { \
auto res = GenAndRunTest<float, float>(rs1_fval, rs2_fval, fn); \
CHECK_FLOAT_EQ(UseCanonicalNan<float>(expect_res), res); \
} \
} \
}
#define UTEST_RVV_VF_VV_FORM_WITH_OP(instr_name, array, tested_op) \
UTEST_RVV_VF_VV_FORM_WITH_RES(instr_name, array, \
((rs1_fval)tested_op(rs2_fval)))
#define UTEST_RVV_VF_VF_FORM_WITH_OP(instr_name, array, tested_op) \
UTEST_RVV_VF_VF_FORM_WITH_RES(instr_name, array, \
((rs1_fval)tested_op(rs2_fval)))
#define ARRAY_FLOAT compiler::ValueHelper::GetVector<float>()
UTEST_RVV_VF_VV_FORM_WITH_OP(vfadd_vv, ARRAY_FLOAT, +)
// UTEST_RVV_VF_VF_FORM_WITH_OP(vfadd_vf, ARRAY_FLOAT, +)
UTEST_RVV_VF_VV_FORM_WITH_OP(vfsub_vv, ARRAY_FLOAT, -)
// UTEST_RVV_VF_VF_FORM_WITH_OP(vfsub_vf, ARRAY_FLOAT, -)
UTEST_RVV_VF_VV_FORM_WITH_OP(vfmul_vv, ARRAY_FLOAT, *)
// UTEST_RVV_VF_VF_FORM_WITH_OP(vfmul_vf, ARRAY_FLOAT, *)
UTEST_RVV_VF_VV_FORM_WITH_OP(vfdiv_vv, ARRAY_FLOAT, /)
// UTEST_RVV_VF_VF_FORM_WITH_OP(vfdiv_vf, ARRAY_FLOAT, /)
#undef ARRAY_FLOAT
#undef UTEST_RVV_VF_VV_FORM_WITH_OP
#undef UTEST_RVV_VF_VF_FORM_WITH_OP
#undef UTEST_RVV_VF_VV_FORM
#undef UTEST_RVV_VF_VF_FORM
// Tests for vector single-width floating-point fused multiply-add Instructions
// between vectors
#define UTEST_RVV_FMA_VV_FORM_WITH_RES(instr_name, array, expect_res) \
TEST(RISCV_UTEST_##instr_name) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { \
__ VU.set(t0, VSew::E32, Vlmul::m1); \
__ vfmv_vf(v0, fa0); \
__ vfmv_vf(v1, fa1); \
__ vfmv_vf(v2, fa2); \
__ instr_name(v0, v1, v2); \
__ vfmv_fs(fa0, v0); \
}; \
for (float rs1_fval : array) { \
for (float rs2_fval : array) { \
for (float rs3_fval : array) { \
auto res = \
GenAndRunTest<float, float>(rs1_fval, rs2_fval, rs3_fval, fn); \
CHECK_FLOAT_EQ(expect_res, res); \
} \
} \
} \
}
// Tests for vector single-width floating-point fused multiply-add Instructions
// between vectors and scalar
#define UTEST_RVV_FMA_VF_FORM_WITH_RES(instr_name, array, expect_res) \
TEST(RISCV_UTEST_##instr_name) { \
CcTest::InitializeVM(); \
auto fn = [](MacroAssembler& assm) { \
__ VU.set(t0, VSew::E32, Vlmul::m1); \
__ vfmv_vf(v0, fa0); \
__ vfmv_vf(v2, fa2); \
__ instr_name(v0, fa1, v2); \
__ vfmv_fs(fa0, v0); \
}; \
for (float rs1_fval : array) { \
for (float rs2_fval : array) { \
for (float rs3_fval : array) { \
auto res = \
GenAndRunTest<float, float>(rs1_fval, rs2_fval, rs3_fval, fn); \
CHECK_FLOAT_EQ(expect_res, res); \
} \
} \
} \
}
#define ARRAY_FLOAT compiler::ValueHelper::GetVector<float>()
UTEST_RVV_FMA_VV_FORM_WITH_RES(vfmadd_vv, ARRAY_FLOAT,
std::fma(rs2_fval, rs1_fval, rs3_fval))
UTEST_RVV_FMA_VF_FORM_WITH_RES(vfmadd_vf, ARRAY_FLOAT,
std::fma(rs2_fval, rs1_fval, rs3_fval))
UTEST_RVV_FMA_VV_FORM_WITH_RES(vfnmadd_vv, ARRAY_FLOAT,
std::fma(rs2_fval, -rs1_fval, -rs3_fval))
UTEST_RVV_FMA_VF_FORM_WITH_RES(vfnmadd_vf, ARRAY_FLOAT,
std::fma(rs2_fval, -rs1_fval, -rs3_fval))
UTEST_RVV_FMA_VV_FORM_WITH_RES(vfmsub_vv, ARRAY_FLOAT,
std::fma(rs2_fval, rs1_fval, -rs3_fval))
UTEST_RVV_FMA_VF_FORM_WITH_RES(vfmsub_vf, ARRAY_FLOAT,
std::fma(rs2_fval, rs1_fval, -rs3_fval))
UTEST_RVV_FMA_VV_FORM_WITH_RES(vfnmsub_vv, ARRAY_FLOAT,
std::fma(rs2_fval, -rs1_fval, rs3_fval))
UTEST_RVV_FMA_VF_FORM_WITH_RES(vfnmsub_vf, ARRAY_FLOAT,
std::fma(rs2_fval, -rs1_fval, rs3_fval))
UTEST_RVV_FMA_VV_FORM_WITH_RES(vfmacc_vv, ARRAY_FLOAT,
std::fma(rs2_fval, rs3_fval, rs1_fval))
UTEST_RVV_FMA_VF_FORM_WITH_RES(vfmacc_vf, ARRAY_FLOAT,
std::fma(rs2_fval, rs3_fval, rs1_fval))
UTEST_RVV_FMA_VV_FORM_WITH_RES(vfnmacc_vv, ARRAY_FLOAT,
std::fma(rs2_fval, -rs3_fval, -rs1_fval))
UTEST_RVV_FMA_VF_FORM_WITH_RES(vfnmacc_vf, ARRAY_FLOAT,
std::fma(rs2_fval, -rs3_fval, -rs1_fval))
UTEST_RVV_FMA_VV_FORM_WITH_RES(vfmsac_vv, ARRAY_FLOAT,
std::fma(rs2_fval, rs3_fval, -rs1_fval))
UTEST_RVV_FMA_VF_FORM_WITH_RES(vfmsac_vf, ARRAY_FLOAT,
std::fma(rs2_fval, rs3_fval, -rs1_fval))
UTEST_RVV_FMA_VV_FORM_WITH_RES(vfnmsac_vv, ARRAY_FLOAT,
std::fma(rs2_fval, -rs3_fval, rs1_fval))
UTEST_RVV_FMA_VF_FORM_WITH_RES(vfnmsac_vf, ARRAY_FLOAT,
std::fma(rs2_fval, -rs3_fval, rs1_fval))
#undef ARRAY_FLOAT
#undef UTEST_RVV_FMA_VV_FORM
#undef UTEST_RVV_FMA_VF_FORM
// calculate the value of r used in rounding
static inline uint8_t get_round(int vxrm, uint64_t v, uint8_t shift) {
// uint8_t d = extract64(v, shift, 1);
uint8_t d = unsigned_bitextract_64(shift, shift, v);
uint8_t d1;
uint64_t D1, D2;
if (shift == 0 || shift > 64) {
return 0;
}
// d1 = extract64(v, shift - 1, 1);
d1 = unsigned_bitextract_64(shift - 1, shift - 1, v);
// D1 = extract64(v, 0, shift);
D1 = unsigned_bitextract_64(shift - 1, 0, v);
if (vxrm == 0) { /* round-to-nearest-up (add +0.5 LSB) */
return d1;
} else if (vxrm == 1) { /* round-to-nearest-even */
if (shift > 1) {
// D2 = extract64(v, 0, shift - 1);
D2 = unsigned_bitextract_64(shift - 2, 0, v);
return d1 & ((D2 != 0) | d);
} else {
return d1 & d;
}
} else if (vxrm == 3) { /* round-to-odd (OR bits into LSB, aka "jam") */
return !d & (D1 != 0);
}
return 0; /* round-down (truncate) */
}
#define UTEST_RVV_VNCLIP_E32M2_E16M1(instr_name, sign) \
TEST(RISCV_UTEST_##instr_name##_E32M2_E16M1) { \
constexpr RoundingMode vxrm = RNE; \
CcTest::InitializeVM(); \
Isolate* isolate = CcTest::i_isolate(); \
HandleScope scope(isolate); \
for (int32_t x : compiler::ValueHelper::GetVector<int>()) { \
for (uint8_t shift = 0; shift < 32; shift++) { \
auto fn = [shift](MacroAssembler& assm) { \
__ VU.set(vxrm); \
__ vsetvli(t0, zero_reg, VSew::E32, Vlmul::m2); \
__ vl(v2, a0, 0, VSew::E32); \
__ vsetvli(t0, zero_reg, VSew::E16, Vlmul::m1); \
__ instr_name(v4, v2, shift); \
__ vs(v4, a1, 0, VSew::E16); \
}; \
struct T { \
sign##int32_t src[8] = {0}; \
sign##int16_t dst[8] = {0}; \
sign##int16_t ref[8] = {0}; \
} t; \
for (auto src : t.src) src = static_cast<sign##int32_t>(x); \
for (auto ref : t.ref) \
ref = base::saturated_cast<sign##int16_t>( \
(static_cast<sign##int32_t>(x) >> shift) + \
get_round(vxrm, x, shift)); \
GenAndRunTest<int32_t, int64_t>((int64_t)t.src, (int64_t)t.dst, fn); \
CHECK(!memcmp(t.dst, t.ref, sizeof(t.ref))); \
} \
} \
}
UTEST_RVV_VNCLIP_E32M2_E16M1(vnclipu_vi, u)
UTEST_RVV_VNCLIP_E32M2_E16M1(vnclip_vi, )
#undef UTEST_RVV_VNCLIP_E32M2_E16M1
#endif
#undef __
} // namespace internal
} // namespace v8