// 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/float16.h"
#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/numbers/conversions-inl.h"
#include "src/utils/utils.h"
#include "test/common/value-helper.h"
#include "test/unittests/assembler/test-helper-riscv64.h"
#include "test/unittests/test-utils.h"

namespace v8 {
namespace internal {

using AssemblerRISCV64Test = TestWithIsolate;

// With 512-bit vectors, there are at most 64 elements.
static_assert(kMaxRvvVLEN <= 512, "Unsupported VLEN");
const int kMaxElements = 64;

// 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.

// Macro for concatenation
#define CAT_(a, b) a##b
#define CAT(a, b) CAT_(a, b)

#define UTEST_R2_FORM_WITH_RES(instr_name, type, rs1_val, rs2_val,     \
                               expected_res)                           \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {             \
    auto fn = [](MacroAssembler& assm) { __ instr_name(a0, a0, a1); }; \
    auto res = GenAndRunTest<type, type>(rs1_val, rs2_val, fn);        \
    CHECK_EQ(expected_res, res);                                       \
  }

// Generic macro for extension-specific tests with single feature
#define UTEST_R2_FORM_WITH_RES_EXT(instr_name, type, rs1_val, rs2_val,       \
                                   expected_res, ext)                        \
  TEST_F(AssemblerRISCV64Test, CAT(CAT(RISCV_UTEST_, instr_name), _##ext)) { \
    if (!CpuFeatures::IsSupported(ext)) {                                    \
      return;                                                                \
    }                                                                        \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {             \
    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_EXT(instr_name, in_type, out_type, rs1_val,   \
                                   expected_res, ext)                        \
  TEST_F(AssemblerRISCV64Test, CAT(CAT(RISCV_UTEST_, instr_name), _##ext)) { \
    if (!CpuFeatures::IsSupported(ext)) {                                    \
      return;                                                                \
    }                                                                        \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {               \
    if (!CpuFeatures::IsSupported(RVC)) {                                \
      return;                                                            \
    }                                                                    \
                                                                         \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {                    \
    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_I_FORM_WITH_RES_EXT(instr_name, type, rs1_val, imm12,          \
                                  expected_res, ext)                         \
  TEST_F(AssemblerRISCV64Test, CAT(CAT(RISCV_UTEST_, instr_name), _##ext)) { \
    if (!CpuFeatures::IsSupported(ext)) {                                    \
      return;                                                                \
    }                                                                        \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {                     \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##stname##ldname) { \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##stname##ldname) {      \
    DCHECK(std::is_floating_point_v<value_type>);                   \
                                                                    \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##stname##ldname) { \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {                  \
    DCHECK(std::is_floating_point_v<type>);                                 \
                                                                            \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {                \
    DCHECK(std::is_floating_point_v<type>);                               \
                                                                          \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {                     \
    DCHECK(std::is_floating_point_v<type>);                                    \
                                                                               \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {                   \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {                \
    DCHECK(std::is_integral_v<input_type> &&                              \
           std::is_floating_point_v<output_type>);                        \
                                                                          \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {           \
    DCHECK(std::is_floating_point_v<input_type> &&                   \
           std::is_integral_v<output_type>);                         \
                                                                     \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_dyn_##instr_name) {       \
    DCHECK(std::is_floating_point_v<input_type> &&                   \
           std::is_integral_v<output_type>);                         \
                                                                     \
    auto fn = [](MacroAssembler& assm) {                             \
      __ csrrwi(t0, csr_frm, rounding_mode);                         \
      __ instr_name(a0, fa0, DYN);                                   \
      __ csrw(csr_frm, t0);                                          \
    };                                                               \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {                \
    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_F(AssemblerRISCV64Test, RISCV_UTEST_CSRI_##csr_reg) {                \
    CHECK_EQ(is_uint5(csr_write_val) && is_uint5(csr_set_clear_val), true); \
                                                                            \
    int64_t expected_res = 111;                                             \
    Label exit, error;                                                      \
    auto fn = [&exit, &error, expected_res](MacroAssembler& assm) {         \
      __ csrr(t1, csr_reg);                                                 \
      /* 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);                                                       \
      __ csrw(csr_reg, t1);                                                 \
    };                                                                      \
    auto res = GenAndRunTest(fn);                                           \
    CHECK_EQ(expected_res, res);                                            \
  }

#define UTEST_CSR(csr_reg, csr_write_val, csr_set_clear_val)        \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_CSR_##csr_reg) {         \
    Label exit, error;                                              \
    int64_t expected_res = 111;                                     \
    auto fn = [&exit, &error, expected_res](MacroAssembler& assm) { \
      __ csrr(t1, csr_reg);                                         \
      /* 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);                                               \
      __ csrw(csr_reg, t1);                                         \
    };                                                              \
                                                                    \
    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 | kFPUOverflow, kUnderflow)
UTEST_CSR(csr_frm, DYN, RUP)
UTEST_CSR(csr_fflags, kInexact | kInvalidOperation, kInvalidOperation)
UTEST_CSR(csr_fcsr, kDivideByZero | kFPUOverflow | (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))
// RV64B
UTEST_R2_FORM_WITH_RES_EXT(
    sh1add, int64_t, LARGE_INT_EXCEED_32_BIT, LARGE_UINT_EXCEED_32_BIT,
    ((LARGE_UINT_EXCEED_32_BIT) + (LARGE_INT_EXCEED_32_BIT << 1)), ZBA)
UTEST_R2_FORM_WITH_RES_EXT(
    sh2add, int64_t, LARGE_INT_EXCEED_32_BIT, LARGE_UINT_EXCEED_32_BIT,
    ((LARGE_UINT_EXCEED_32_BIT) + (LARGE_INT_EXCEED_32_BIT << 2)), ZBA)
UTEST_R2_FORM_WITH_RES_EXT(
    sh3add, int64_t, LARGE_INT_EXCEED_32_BIT, LARGE_UINT_EXCEED_32_BIT,
    ((LARGE_UINT_EXCEED_32_BIT) + (LARGE_INT_EXCEED_32_BIT << 3)), ZBA)

UTEST_R2_FORM_WITH_RES_EXT(sh1adduw, int64_t, 0x13f42, 1,
                           ((1) + (uint32_t(0x13f42) << 1)), ZBA)

UTEST_R2_FORM_WITH_RES_EXT(sh2adduw, int64_t, 0x13f42, LARGE_UINT_EXCEED_32_BIT,
                           int64_t((LARGE_UINT_EXCEED_32_BIT) +
                                   (uint32_t(0x13f42) << 2)),
                           ZBA)

UTEST_R2_FORM_WITH_RES_EXT(sh3adduw, int64_t, LARGE_INT_EXCEED_32_BIT,
                           LARGE_UINT_EXCEED_32_BIT,
                           int64_t((LARGE_UINT_EXCEED_32_BIT) +
                                   (uint32_t(LARGE_INT_EXCEED_32_BIT) << 3)),
                           ZBA)
UTEST_R2_FORM_WITH_RES_EXT(adduw, int64_t, LARGE_INT_EXCEED_32_BIT,
                           LARGE_UINT_EXCEED_32_BIT,
                           int64_t((LARGE_UINT_EXCEED_32_BIT) +
                                   (uint32_t(LARGE_INT_EXCEED_32_BIT))),
                           ZBA)

UTEST_I_FORM_WITH_RES_EXT(slliuw, int64_t, LARGE_INT_EXCEED_32_BIT, 10,
                          (int64_t(uint32_t(LARGE_INT_EXCEED_32_BIT))) << 10,
                          ZBA)

UTEST_R2_FORM_WITH_RES_EXT(
    andn, int64_t, LARGE_INT_EXCEED_32_BIT, LARGE_UINT_EXCEED_32_BIT,
    ((LARGE_INT_EXCEED_32_BIT) & (~LARGE_UINT_EXCEED_32_BIT)), ZBB)

UTEST_R2_FORM_WITH_RES_EXT(
    orn, int64_t, LARGE_INT_EXCEED_32_BIT, LARGE_UINT_EXCEED_32_BIT,
    ((LARGE_INT_EXCEED_32_BIT) | (~LARGE_UINT_EXCEED_32_BIT)), ZBB)

UTEST_R2_FORM_WITH_RES_EXT(
    xnor, int64_t, LARGE_INT_EXCEED_32_BIT, LARGE_UINT_EXCEED_32_BIT,
    int64_t(~(LARGE_INT_EXCEED_32_BIT ^ LARGE_UINT_EXCEED_32_BIT)), ZBB)

UTEST_R1_FORM_WITH_RES_EXT(clz, int64_t, int64_t, 0b000011000100000000000, 47,
                           ZBB)
UTEST_R1_FORM_WITH_RES_EXT(ctz, int64_t, int64_t, 0b000011000100000000000, 11,
                           ZBB)

UTEST_R1_FORM_WITH_RES_EXT(clzw, int64_t, int64_t, 0b000011000100000000000, 15,
                           ZBB)
UTEST_R1_FORM_WITH_RES_EXT(ctzw, int64_t, int64_t, 0b000011000100000000000, 11,
                           ZBB)

UTEST_R1_FORM_WITH_RES_EXT(cpop, int64_t, int64_t, 0b000011000100000000000, 3,
                           ZBB)
UTEST_R1_FORM_WITH_RES_EXT(cpopw, int64_t, int64_t, 0b000011000100000000011, 5,
                           ZBB)

UTEST_R2_FORM_WITH_RES_EXT(max, int64_t, -1012, 3456, 3456, ZBB)
UTEST_R2_FORM_WITH_RES_EXT(min, int64_t, -1012, 3456, -1012, ZBB)
UTEST_R2_FORM_WITH_RES_EXT(maxu, uint64_t, -1012, 3456, uint64_t(-1012), ZBB)
UTEST_R2_FORM_WITH_RES_EXT(minu, uint64_t, -1012, 3456, 3456, ZBB)

UTEST_R1_FORM_WITH_RES_EXT(sextb, int64_t, int64_t, 0xB080,
                           int64_t(0xffffffffffffff80), ZBB)
UTEST_R1_FORM_WITH_RES_EXT(sexth, int64_t, int64_t, 0xB080,
                           int64_t(0xffffffffffffb080), ZBB)
UTEST_R1_FORM_WITH_RES_EXT(zexth, int64_t, int64_t, 0xB080, 0xB080, ZBB)

UTEST_R2_FORM_WITH_RES_EXT(rol, uint64_t, 16, 2, 64, ZBB)
UTEST_R2_FORM_WITH_RES_EXT(rolw, uint32_t, 16, 2, 64, ZBB)
UTEST_R2_FORM_WITH_RES_EXT(ror, uint64_t, 16, 2, 4, ZBB)
UTEST_R2_FORM_WITH_RES_EXT(rorw, uint32_t, 16, 2, 4, ZBB)
UTEST_I_FORM_WITH_RES_EXT(rori, int64_t, 16, 2, 4, ZBB)
UTEST_I_FORM_WITH_RES_EXT(roriw, int32_t, 16, 2, 4, ZBB)
UTEST_R1_FORM_WITH_RES_EXT(orcb, int64_t, uint64_t, 0xFF00011010010011,
                           0xFF00FFFFFFFF00FF, ZBB)
UTEST_R1_FORM_WITH_RES_EXT(rev8, uint64_t, uint64_t, 0x1234567890ABCDEF,
                           0xEFCDAB9078563412, ZBB)

UTEST_R2_FORM_WITH_RES_EXT(bclr, int64_t, 0x13f62, 5, 0x13f42, ZBS)
UTEST_I_FORM_WITH_RES_EXT(bclri, int64_t, 0x2013F6213F62, 45, 0x13f6213f62, ZBS)
UTEST_R2_FORM_WITH_RES_EXT(bext, int64_t, 0x13f62, 5, 1, ZBS)
UTEST_I_FORM_WITH_RES_EXT(bexti, int64_t, 0x2013F6213F62, 45, 1, ZBS)
UTEST_R2_FORM_WITH_RES_EXT(binv, int64_t, 0x13f62, 5, 0x13f42, ZBS)
UTEST_I_FORM_WITH_RES_EXT(binvi, int64_t, 0x13f6213f62, 45, 0x2013F6213F62, ZBS)
UTEST_R2_FORM_WITH_RES_EXT(bset, int64_t, 0x13f42, 5, 0x13f62, ZBS)
UTEST_I_FORM_WITH_RES_EXT(bseti, int64_t, 0x13f6213f62, 45, 0x2013F6213F62, ZBS)

// -- 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()))

// -- RVZFA Standard Extension --
TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fli_s) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  struct FliSTestCase {
    uint8_t imm5;
    float expected;
  };
  FliSTestCase test_cases[] = {
      {0, -1.0f},
      {1, 1.17549435e-38f},
      {2, 1.52587890625e-5f},
      {8, 0.25f},
      {12, 0.5f},
      {16, 1.0f},
      {20, 2.0f},
      {24, 8.0f},
      {30, std::numeric_limits<float>::infinity()},
  };
  for (auto& tc : test_cases) {
    auto fn = [imm5 = tc.imm5](MacroAssembler& assm) {
      __ fli_s(fa0, imm5);
      __ fmv_x_w(a0, fa0);
    };
    auto res = GenAndRunTest(fn);
    float fres = base::bit_cast<float>(static_cast<int32_t>(res));
    if (tc.imm5 == 30) {
      CHECK(std::isinf(fres) && fres > 0);
    } else {
      CHECK_EQ(tc.expected, fres);
    }
  }
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fli_d) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  struct FliDTestCase {
    uint8_t imm5;
    double expected;
  };
  FliDTestCase test_cases[] = {
      {0, -1.0},
      {1, 2.2250738585072014e-308},
      {2, 1.52587890625e-5},
      {8, 0.25},
      {12, 0.5},
      {16, 1.0},
      {20, 2.0},
      {24, 8.0},
      {30, std::numeric_limits<double>::infinity()},
  };
  for (auto& tc : test_cases) {
    auto fn = [imm5 = tc.imm5](MacroAssembler& assm) {
      __ fli_d(fa0, imm5);
      __ fmv_x_d(a0, fa0);
    };
    auto res = GenAndRunTest(fn);
    double dres = base::bit_cast<double>(res);
    if (tc.imm5 == 30) {
      CHECK(std::isinf(dres) && dres > 0);
    } else {
      CHECK_EQ(tc.expected, dres);
    }
  }
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fround_s) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  struct FroundSTestCase {
    float input;
    float expected;
    FPURoundingMode rm;
  };
  FroundSTestCase test_cases[] = {
      {1.5f, 2.0f, RNE},   {2.5f, 2.0f, RNE},   {-1.5f, -2.0f, RNE},
      {1.5f, 1.0f, RTZ},   {-1.5f, -1.0f, RTZ}, {1.5f, 1.0f, RDN},
      {-1.5f, -2.0f, RDN}, {1.5f, 2.0f, RUP},   {-1.5f, -1.0f, RUP},
      {2.5f, 3.0f, RMM},   {-2.5f, -3.0f, RMM},
  };
  for (auto& tc : test_cases) {
    auto fn = [rm = tc.rm](MacroAssembler& assm) { __ fround_s(fa0, fa0, rm); };
    auto res = GenAndRunTest<float, float>(tc.input, fn);
    CHECK_EQ(tc.expected, res);
  }
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fround_d) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  struct FroundDTestCase {
    double input;
    double expected;
    FPURoundingMode rm;
  };
  FroundDTestCase test_cases[] = {
      {1.5, 2.0, RNE},   {2.5, 2.0, RNE}, {-1.5, -2.0, RNE}, {1.5, 1.0, RTZ},
      {-1.5, -1.0, RTZ}, {1.5, 1.0, RDN}, {-1.5, -2.0, RDN}, {1.5, 2.0, RUP},
      {-1.5, -1.0, RUP}, {2.5, 3.0, RMM}, {-2.5, -3.0, RMM},
  };
  for (auto& tc : test_cases) {
    auto fn = [rm = tc.rm](MacroAssembler& assm) { __ fround_d(fa0, fa0, rm); };
    auto res = GenAndRunTest<double, double>(tc.input, fn);
    CHECK_EQ(tc.expected, res);
  }
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvtmod_w_d) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  auto FcvtmodHelper = [](uint64_t value_bits, int64_t expected) {
    auto fn = [](MacroAssembler& assm) { __ fcvtmod_w_d(a0, fa0); };
    auto res =
        GenAndRunTest<int64_t, double>(base::bit_cast<double>(value_bits), fn);
    CHECK_EQ(expected, res);
  };

  // Simple values.
  FcvtmodHelper(0x0000000000000000ULL, 0);   // 0.0
  FcvtmodHelper(0x3ff0000000000000ULL, 1);   // 1.0
  FcvtmodHelper(0x3ff8000000000000ULL, 1);   // 1.5 (rounds towards zero)
  FcvtmodHelper(0x4000000000000000ULL, 2);   // 2.0
  FcvtmodHelper(0x4024000000000000ULL, 10);  // 10.0

  // Negative values.
  FcvtmodHelper(0x8000000000000000ULL, 0);    // -0.0
  FcvtmodHelper(0xbff0000000000000ULL, -1);   // -1.0
  FcvtmodHelper(0xbff8000000000000ULL, -1);   // -1.5 (rounds towards zero)
  FcvtmodHelper(0xc000000000000000ULL, -2);   // -2.0
  FcvtmodHelper(0xc024000000000000ULL, -10);  // -10.0

  // INT32_MAX and INT32_MIN.
  FcvtmodHelper(0x41dfffffffc00000ULL, 2147483647);  // 2147483647.0 (INT32_MAX)
  FcvtmodHelper(0xc1e0000000000000ULL,
                -2147483648LL);  // -2147483648.0 (INT32_MIN)

  // Values outside 32-bit range (mod 2^32).
  FcvtmodHelper(0x41e0000000000000ULL,
                -2147483648LL);              // 2147483648.0 -> 0x80000000
  FcvtmodHelper(0x41f0000000000000ULL, 0);   // 4294967296.0 (2^32) -> 0
  FcvtmodHelper(0x41F0000000100000ULL, 1);   // 4294967297.0 -> 1
  FcvtmodHelper(0xc1f0000000000000ULL, 0);   // -4294967296.0 -> 0
  FcvtmodHelper(0xC1F0000000100000ULL, -1);  // -4294967297.0 -> -1

  // Largest finite value (overflows to 0 mod 2^32).
  FcvtmodHelper(0x7fefffffffffffffULL, 0);
  FcvtmodHelper(0xffefffffffffffffULL, 0);

  // Infinity.
  FcvtmodHelper(0x7ff0000000000000ULL, 0);  // +Inf
  FcvtmodHelper(0xfff0000000000000ULL, 0);  // -Inf

  // NaNs.
  FcvtmodHelper(0x7ff8000000000000ULL, 0);  // Canonical quiet NaN
  FcvtmodHelper(0x7ff923456789abcdULL, 0);  // Quiet NaN
  FcvtmodHelper(0x7ff0000000000001ULL, 0);  // Signalling NaN
  FcvtmodHelper(0x7ff123456789abcdULL, 0);  // Signalling NaN
  FcvtmodHelper(0xfff8000000000000ULL, 0);  // Negative quiet NaN
  FcvtmodHelper(0xfff923456789abcdULL, 0);  // Negative quiet NaN

  // Subnormals.
  FcvtmodHelper(0x0000000000000001ULL, 0);  // Smallest subnormal
  FcvtmodHelper(0x000fffffffffffffULL, 0);  // Largest subnormal
  FcvtmodHelper(0x000123456789abcdULL, 0);  // Subnormal
  FcvtmodHelper(0x8000000000000001ULL, 0);  // Negative smallest subnormal
  FcvtmodHelper(0x800fffffffffffffULL, 0);  // Negative largest subnormal

  // Small normal values.
  FcvtmodHelper(0x0010000000000000ULL, 0);  // Smallest positive normal
  FcvtmodHelper(0x8010000000000000ULL, 0);  // Negative smallest normal

  // Values near 0.5.
  FcvtmodHelper(0x3fe0000000000000ULL, 0);  // 0.5 -> rounds to 0
  FcvtmodHelper(0x3fdfffffffffffffULL, 0);  // Just below 0.5
  FcvtmodHelper(0x3fe0000000000001ULL, 0);  // Just above 0.5
  FcvtmodHelper(0xbfe0000000000000ULL, 0);  // -0.5 -> rounds to 0
  FcvtmodHelper(0xbfdfffffffffffffULL, 0);  // Just below -0.5
  FcvtmodHelper(0xbfe0000000000001ULL, 0);  // Just above -0.5

  // Values near 1.0.
  FcvtmodHelper(0x3fefffffffffffffULL, 0);   // Just below 1.0
  FcvtmodHelper(0x3ff0000000000001ULL, 1);   // Just above 1.0
  FcvtmodHelper(0xbfefffffffffffffULL, 0);   // Just below -1.0
  FcvtmodHelper(0xbff0000000000001ULL, -1);  // Just above -1.0
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fleq_s) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  auto FleqSHelper = [](float rs1, float rs2, int32_t expected) {
    auto fn = [](MacroAssembler& assm) { __ fleq_s(a0, fa0, fa1); };
    auto res = GenAndRunTest<int32_t, float>(rs1, rs2, fn);
    CHECK_EQ(expected, res);
  };
  // Normal comparisons
  FleqSHelper(1.0f, 2.0f, 1);       // 1.0 <= 2.0 -> true
  FleqSHelper(2.0f, 1.0f, 0);       // 2.0 <= 1.0 -> false
  FleqSHelper(1.0f, 1.0f, 1);       // 1.0 <= 1.0 -> true
  FleqSHelper(-1.0f, 1.0f, 1);      // -1.0 <= 1.0 -> true
  FleqSHelper(1.0f, -1.0f, 0);      // 1.0 <= -1.0 -> false
  FleqSHelper(3.14f, 3.15f, 1);     // 3.14 <= 3.15 -> true
  FleqSHelper(-100.0f, -50.0f, 1);  // -100.0 <= -50.0 -> true

  // Zero comparisons
  FleqSHelper(0.0f, 0.0f, 1);  // 0.0 <= 0.0 -> true
  FleqSHelper(base::bit_cast<float>(0x80000000U),
              base::bit_cast<float>(0x00000000U), 1);  // -0.0 <= +0.0
  FleqSHelper(base::bit_cast<float>(0x00000000U),
              base::bit_cast<float>(0x80000000U), 1);  // +0.0 <= -0.0
  FleqSHelper(base::bit_cast<float>(0x80000000U),
              base::bit_cast<float>(0x80000000U), 1);  // -0.0 <= -0.0
  FleqSHelper(0.0f, 1.0f, 1);                          // 0.0 <= 1.0 -> true
  FleqSHelper(-1.0f, 0.0f, 1);                         // -1.0 <= 0.0 -> true

  // NaN comparisons (should return 0, quiet - no exception)
  FleqSHelper(base::bit_cast<float>(0x7fc00000U),
              base::bit_cast<float>(0x3f800000U), 0);  // NaN <= 1.0
  FleqSHelper(base::bit_cast<float>(0x3f800000U),
              base::bit_cast<float>(0x7fc00000U), 0);  // 1.0 <= NaN
  FleqSHelper(base::bit_cast<float>(0x7fc00000U),
              base::bit_cast<float>(0x7fc00000U), 0);  // NaN <= NaN
  FleqSHelper(base::bit_cast<float>(0x7ff00000U),
              base::bit_cast<float>(0x3f800000U), 0);  // Signaling NaN <= 1.0
  FleqSHelper(base::bit_cast<float>(0xffc00000U),
              base::bit_cast<float>(0x3f800000U), 0);  // -NaN <= 1.0

  // Infinity comparisons
  FleqSHelper(base::bit_cast<float>(0x7f800000U),
              base::bit_cast<float>(0x3f800000U), 0);  // +Inf <= 1.0
  FleqSHelper(base::bit_cast<float>(0x3f800000U),
              base::bit_cast<float>(0x7f800000U), 1);  // 1.0 <= +Inf
  FleqSHelper(base::bit_cast<float>(0xff800000U),
              base::bit_cast<float>(0x3f800000U), 1);  // -Inf <= 1.0
  FleqSHelper(base::bit_cast<float>(0x7f800000U),
              base::bit_cast<float>(0x7f800000U), 1);  // +Inf <= +Inf
  FleqSHelper(base::bit_cast<float>(0xff800000U),
              base::bit_cast<float>(0xff800000U), 1);  // -Inf <= -Inf
  FleqSHelper(base::bit_cast<float>(0xff800000U),
              base::bit_cast<float>(0x7f800000U), 1);  // -Inf <= +Inf
  FleqSHelper(base::bit_cast<float>(0x7f800000U),
              base::bit_cast<float>(0xff800000U), 0);  // +Inf <= -Inf

  // Denormal numbers
  FleqSHelper(base::bit_cast<float>(0x00000001U),
              base::bit_cast<float>(0x3f800000U), 1);  // smallest denorm <= 1.0
  FleqSHelper(base::bit_cast<float>(0x3f800000U),
              base::bit_cast<float>(0x00000001U), 0);  // 1.0 <= smallest denorm
  FleqSHelper(base::bit_cast<float>(0x007fffffU),
              base::bit_cast<float>(0x00800000U),
              1);  // largest denorm <= smallest normal
  FleqSHelper(base::bit_cast<float>(0x00000001U),
              base::bit_cast<float>(0x00000002U), 1);  // denorm 1 <= denorm 2

  // Boundary values
  FleqSHelper(base::bit_cast<float>(0x7f7fffffU),
              base::bit_cast<float>(0x7f800000U), 1);  // FLT_MAX <= +Inf
  FleqSHelper(base::bit_cast<float>(0x7f800000U),
              base::bit_cast<float>(0x7f7fffffU), 0);  // +Inf <= FLT_MAX
  FleqSHelper(base::bit_cast<float>(0x00800000U),
              base::bit_cast<float>(0x00800001U), 1);  // FLT_MIN <= FLT_MIN+1
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fltq_s) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  auto FltqSHelper = [](float rs1, float rs2, int32_t expected) {
    auto fn = [](MacroAssembler& assm) { __ fltq_s(a0, fa0, fa1); };
    auto res = GenAndRunTest<int32_t, float>(rs1, rs2, fn);
    CHECK_EQ(expected, res);
  };
  // Normal comparisons
  FltqSHelper(1.0f, 2.0f, 1);       // 1.0 < 2.0 -> true
  FltqSHelper(2.0f, 1.0f, 0);       // 2.0 < 1.0 -> false
  FltqSHelper(1.0f, 1.0f, 0);       // 1.0 < 1.0 -> false
  FltqSHelper(-1.0f, 1.0f, 1);      // -1.0 < 1.0 -> true
  FltqSHelper(1.0f, -1.0f, 0);      // 1.0 < -1.0 -> false
  FltqSHelper(3.14f, 3.15f, 1);     // 3.14 < 3.15 -> true
  FltqSHelper(-100.0f, -50.0f, 1);  // -100.0 < -50.0 -> true

  // Zero comparisons
  FltqSHelper(0.0f, 0.0f, 0);  // 0.0 < 0.0 -> false
  FltqSHelper(base::bit_cast<float>(0x80000000U),
              base::bit_cast<float>(0x00000000U), 0);  // -0.0 < +0.0
  FltqSHelper(base::bit_cast<float>(0x00000000U),
              base::bit_cast<float>(0x80000000U), 0);  // +0.0 < -0.0
  FltqSHelper(base::bit_cast<float>(0x80000000U),
              base::bit_cast<float>(0x80000000U), 0);  // -0.0 < -0.0
  FltqSHelper(0.0f, 1.0f, 1);                          // 0.0 < 1.0 -> true
  FltqSHelper(-1.0f, 0.0f, 1);                         // -1.0 < 0.0 -> true

  // NaN comparisons (should return 0, quiet - no exception)
  FltqSHelper(base::bit_cast<float>(0x7fc00000U),
              base::bit_cast<float>(0x3f800000U), 0);  // NaN < 1.0
  FltqSHelper(base::bit_cast<float>(0x3f800000U),
              base::bit_cast<float>(0x7fc00000U), 0);  // 1.0 < NaN
  FltqSHelper(base::bit_cast<float>(0x7fc00000U),
              base::bit_cast<float>(0x7fc00000U), 0);  // NaN < NaN
  FltqSHelper(base::bit_cast<float>(0x7ff00000U),
              base::bit_cast<float>(0x3f800000U), 0);  // Signaling NaN < 1.0
  FltqSHelper(base::bit_cast<float>(0xffc00000U),
              base::bit_cast<float>(0x3f800000U), 0);  // -NaN < 1.0

  // Infinity comparisons
  FltqSHelper(base::bit_cast<float>(0x7f800000U),
              base::bit_cast<float>(0x3f800000U), 0);  // +Inf < 1.0
  FltqSHelper(base::bit_cast<float>(0x3f800000U),
              base::bit_cast<float>(0x7f800000U), 1);  // 1.0 < +Inf
  FltqSHelper(base::bit_cast<float>(0xff800000U),
              base::bit_cast<float>(0x3f800000U), 1);  // -Inf < 1.0
  FltqSHelper(base::bit_cast<float>(0x7f800000U),
              base::bit_cast<float>(0x7f800000U), 0);  // +Inf < +Inf
  FltqSHelper(base::bit_cast<float>(0xff800000U),
              base::bit_cast<float>(0xff800000U), 0);  // -Inf < -Inf
  FltqSHelper(base::bit_cast<float>(0xff800000U),
              base::bit_cast<float>(0x7f800000U), 1);  // -Inf < +Inf
  FltqSHelper(base::bit_cast<float>(0x7f800000U),
              base::bit_cast<float>(0xff800000U), 0);  // +Inf < -Inf

  // Denormal numbers
  FltqSHelper(base::bit_cast<float>(0x00000001U),
              base::bit_cast<float>(0x3f800000U), 1);  // smallest denorm < 1.0
  FltqSHelper(base::bit_cast<float>(0x3f800000U),
              base::bit_cast<float>(0x00000001U), 0);  // 1.0 < smallest denorm
  FltqSHelper(base::bit_cast<float>(0x007fffffU),
              base::bit_cast<float>(0x00800000U),
              1);  // largest denorm < smallest normal
  FltqSHelper(base::bit_cast<float>(0x00000001U),
              base::bit_cast<float>(0x00000002U), 1);  // denorm 1 < denorm 2

  // Boundary values
  FltqSHelper(base::bit_cast<float>(0x7f7fffffU),
              base::bit_cast<float>(0x7f800000U), 1);  // FLT_MAX < +Inf
  FltqSHelper(base::bit_cast<float>(0x7f800000U),
              base::bit_cast<float>(0x7f7fffffU), 0);  // +Inf < FLT_MAX
  FltqSHelper(base::bit_cast<float>(0x00800000U),
              base::bit_cast<float>(0x00800001U), 1);  // FLT_MIN < FLT_MIN+1
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fleq_d) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  auto FleqDHelper = [](double rs1, double rs2, int32_t expected) {
    auto fn = [](MacroAssembler& assm) { __ fleq_d(a0, fa0, fa1); };
    auto res = GenAndRunTest<int32_t, double>(rs1, rs2, fn);
    CHECK_EQ(expected, res);
  };
  // Normal comparisons
  FleqDHelper(1.0, 2.0, 1);   // 1.0 <= 2.0 -> true
  FleqDHelper(2.0, 1.0, 0);   // 2.0 <= 1.0 -> false
  FleqDHelper(1.0, 1.0, 1);   // 1.0 <= 1.0 -> true
  FleqDHelper(-1.0, 1.0, 1);  // -1.0 <= 1.0 -> true
  FleqDHelper(1.0, -1.0, 0);  // 1.0 <= -1.0 -> false
  FleqDHelper(3.14159265358979, 3.1415926535898002,
              1);                 // pi_small <= pi_large -> true
  FleqDHelper(-100.0, -50.0, 1);  // -100.0 <= -50.0 -> true

  // Zero comparisons
  FleqDHelper(0.0, 0.0, 1);  // 0.0 <= 0.0 -> true
  FleqDHelper(base::bit_cast<double>(0x8000000000000000ULL),
              base::bit_cast<double>(0x0000000000000000ULL),
              1);  // -0.0 <= +0.0
  FleqDHelper(base::bit_cast<double>(0x0000000000000000ULL),
              base::bit_cast<double>(0x8000000000000000ULL),
              1);  // +0.0 <= -0.0
  FleqDHelper(base::bit_cast<double>(0x8000000000000000ULL),
              base::bit_cast<double>(0x8000000000000000ULL),
              1);             // -0.0 <= -0.0
  FleqDHelper(0.0, 1.0, 1);   // 0.0 <= 1.0 -> true
  FleqDHelper(-1.0, 0.0, 1);  // -1.0 <= 0.0 -> true

  // NaN comparisons (should return 0, quiet - no exception)
  FleqDHelper(base::bit_cast<double>(0x7ff8000000000000ULL),
              base::bit_cast<double>(0x3ff0000000000000ULL), 0);  // NaN <= 1.0
  FleqDHelper(base::bit_cast<double>(0x3ff0000000000000ULL),
              base::bit_cast<double>(0x7ff8000000000000ULL), 0);  // 1.0 <= NaN
  FleqDHelper(base::bit_cast<double>(0x7ff8000000000000ULL),
              base::bit_cast<double>(0x7ff8000000000000ULL), 0);  // NaN <= NaN
  FleqDHelper(base::bit_cast<double>(0x7ff0000000000001ULL),
              base::bit_cast<double>(0x3ff0000000000000ULL),
              0);  // Signaling NaN <= 1.0
  FleqDHelper(base::bit_cast<double>(0xfff8000000000000ULL),
              base::bit_cast<double>(0x3ff0000000000000ULL), 0);  // -NaN <= 1.0

  // Infinity comparisons
  FleqDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
              base::bit_cast<double>(0x3ff0000000000000ULL), 0);  // +Inf <= 1.0
  FleqDHelper(base::bit_cast<double>(0x3ff0000000000000ULL),
              base::bit_cast<double>(0x7ff0000000000000ULL), 1);  // 1.0 <= +Inf
  FleqDHelper(base::bit_cast<double>(0xfff0000000000000ULL),
              base::bit_cast<double>(0x3ff0000000000000ULL), 1);  // -Inf <= 1.0
  FleqDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
              base::bit_cast<double>(0x7ff0000000000000ULL),
              1);  // +Inf <= +Inf
  FleqDHelper(base::bit_cast<double>(0xfff0000000000000ULL),
              base::bit_cast<double>(0xfff0000000000000ULL),
              1);  // -Inf <= -Inf
  FleqDHelper(base::bit_cast<double>(0xfff0000000000000ULL),
              base::bit_cast<double>(0x7ff0000000000000ULL),
              1);  // -Inf <= +Inf
  FleqDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
              base::bit_cast<double>(0xfff0000000000000ULL),
              0);  // +Inf <= -Inf

  // Denormal numbers
  FleqDHelper(base::bit_cast<double>(0x0000000000000001ULL),
              base::bit_cast<double>(0x3ff0000000000000ULL),
              1);  // smallest denorm <= 1.0
  FleqDHelper(base::bit_cast<double>(0x3ff0000000000000ULL),
              base::bit_cast<double>(0x0000000000000001ULL),
              0);  // 1.0 <= smallest denorm
  FleqDHelper(base::bit_cast<double>(0x000fffffffffffffULL),
              base::bit_cast<double>(0x0010000000000000ULL),
              1);  // largest denorm <= smallest normal
  FleqDHelper(base::bit_cast<double>(0x0000000000000001ULL),
              base::bit_cast<double>(0x0000000000000002ULL),
              1);  // denorm 1 <= denorm 2

  // Boundary values
  FleqDHelper(base::bit_cast<double>(0x7fefffffffffffffULL),
              base::bit_cast<double>(0x7ff0000000000000ULL),
              1);  // DBL_MAX <= +Inf
  FleqDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
              base::bit_cast<double>(0x7fefffffffffffffULL),
              0);  // +Inf <= DBL_MAX
  FleqDHelper(base::bit_cast<double>(0x0010000000000000ULL),
              base::bit_cast<double>(0x0010000000000001ULL),
              1);  // DBL_MIN <= DBL_MIN+1

  // Large integer range values
  FleqDHelper(2147483647.0, 2147483648.0, 1);  // INT32_MAX <= INT32_MAX+1
  FleqDHelper(-2147483648.0, 0.0, 1);          // INT32_MIN <= 0.0
  FleqDHelper(9007199254740992.0, 9007199254740992.0, 1);  // 2^53 <= 2^53
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fltq_d) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  auto FltqDHelper = [](double rs1, double rs2, int32_t expected) {
    auto fn = [](MacroAssembler& assm) { __ fltq_d(a0, fa0, fa1); };
    auto res = GenAndRunTest<int32_t, double>(rs1, rs2, fn);
    CHECK_EQ(expected, res);
  };
  // Normal comparisons
  FltqDHelper(1.0, 2.0, 1);   // 1.0 < 2.0 -> true
  FltqDHelper(2.0, 1.0, 0);   // 2.0 < 1.0 -> false
  FltqDHelper(1.0, 1.0, 0);   // 1.0 < 1.0 -> false
  FltqDHelper(-1.0, 1.0, 1);  // -1.0 < 1.0 -> true
  FltqDHelper(1.0, -1.0, 0);  // 1.0 < -1.0 -> false
  FltqDHelper(3.14159265358979, 3.1415926535898002,
              1);                 // pi_small < pi_large -> true
  FltqDHelper(-100.0, -50.0, 1);  // -100.0 < -50.0 -> true

  // Zero comparisons
  FltqDHelper(0.0, 0.0, 0);  // 0.0 < 0.0 -> false
  FltqDHelper(base::bit_cast<double>(0x8000000000000000ULL),
              base::bit_cast<double>(0x0000000000000000ULL), 0);  // -0.0 < +0.0
  FltqDHelper(base::bit_cast<double>(0x0000000000000000ULL),
              base::bit_cast<double>(0x8000000000000000ULL), 0);  // +0.0 < -0.0
  FltqDHelper(base::bit_cast<double>(0x8000000000000000ULL),
              base::bit_cast<double>(0x8000000000000000ULL), 0);  // -0.0 < -0.0
  FltqDHelper(0.0, 1.0, 1);   // 0.0 < 1.0 -> true
  FltqDHelper(-1.0, 0.0, 1);  // -1.0 < 0.0 -> true

  // NaN comparisons (should return 0, quiet - no exception)
  FltqDHelper(base::bit_cast<double>(0x7ff8000000000000ULL),
              base::bit_cast<double>(0x3ff0000000000000ULL), 0);  // NaN < 1.0
  FltqDHelper(base::bit_cast<double>(0x3ff0000000000000ULL),
              base::bit_cast<double>(0x7ff8000000000000ULL), 0);  // 1.0 < NaN
  FltqDHelper(base::bit_cast<double>(0x7ff8000000000000ULL),
              base::bit_cast<double>(0x7ff8000000000000ULL), 0);  // NaN < NaN
  FltqDHelper(base::bit_cast<double>(0x7ff0000000000001ULL),
              base::bit_cast<double>(0x3ff0000000000000ULL),
              0);  // Signaling NaN < 1.0
  FltqDHelper(base::bit_cast<double>(0xfff8000000000000ULL),
              base::bit_cast<double>(0x3ff0000000000000ULL), 0);  // -NaN < 1.0

  // Infinity comparisons
  FltqDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
              base::bit_cast<double>(0x3ff0000000000000ULL), 0);  // +Inf < 1.0
  FltqDHelper(base::bit_cast<double>(0x3ff0000000000000ULL),
              base::bit_cast<double>(0x7ff0000000000000ULL), 1);  // 1.0 < +Inf
  FltqDHelper(base::bit_cast<double>(0xfff0000000000000ULL),
              base::bit_cast<double>(0x3ff0000000000000ULL), 1);  // -Inf < 1.0
  FltqDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
              base::bit_cast<double>(0x7ff0000000000000ULL), 0);  // +Inf < +Inf
  FltqDHelper(base::bit_cast<double>(0xfff0000000000000ULL),
              base::bit_cast<double>(0xfff0000000000000ULL), 0);  // -Inf < -Inf
  FltqDHelper(base::bit_cast<double>(0xfff0000000000000ULL),
              base::bit_cast<double>(0x7ff0000000000000ULL), 1);  // -Inf < +Inf
  FltqDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
              base::bit_cast<double>(0xfff0000000000000ULL), 0);  // +Inf < -Inf

  // Denormal numbers
  FltqDHelper(base::bit_cast<double>(0x0000000000000001ULL),
              base::bit_cast<double>(0x3ff0000000000000ULL),
              1);  // smallest denorm < 1.0
  FltqDHelper(base::bit_cast<double>(0x3ff0000000000000ULL),
              base::bit_cast<double>(0x0000000000000001ULL),
              0);  // 1.0 < smallest denorm
  FltqDHelper(base::bit_cast<double>(0x000fffffffffffffULL),
              base::bit_cast<double>(0x0010000000000000ULL),
              1);  // largest denorm < smallest normal
  FltqDHelper(base::bit_cast<double>(0x0000000000000001ULL),
              base::bit_cast<double>(0x0000000000000002ULL),
              1);  // denorm 1 < denorm 2

  // Boundary values
  FltqDHelper(base::bit_cast<double>(0x7fefffffffffffffULL),
              base::bit_cast<double>(0x7ff0000000000000ULL),
              1);  // DBL_MAX < +Inf
  FltqDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
              base::bit_cast<double>(0x7fefffffffffffffULL),
              0);  // +Inf < DBL_MAX
  FltqDHelper(base::bit_cast<double>(0x0010000000000000ULL),
              base::bit_cast<double>(0x0010000000000001ULL),
              1);  // DBL_MIN < DBL_MIN+1

  // Large integer range values
  FltqDHelper(2147483647.0, 2147483648.0, 1);  // INT32_MAX < INT32_MAX+1
  FltqDHelper(-2147483648.0, 0.0, 1);          // INT32_MIN < 0.0
  FltqDHelper(9007199254740992.0, 9007199254740992.0, 0);  // 2^53 < 2^53
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fminm_s) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  auto FminmSHelper = [](float rs1, float rs2, uint32_t expected_bits) {
    auto fn = [](MacroAssembler& assm) { __ fminm_s(fa0, fa0, fa1); };
    auto res = GenAndRunTest<float, float>(rs1, rs2, fn);
    CHECK_EQ(expected_bits, base::bit_cast<uint32_t>(res));
  };
  // Normal comparisons
  FminmSHelper(1.0f, 2.0f, 0x3f800000U);
  FminmSHelper(2.0f, 1.0f, 0x3f800000U);
  FminmSHelper(-1.0f, 1.0f, 0xbf800000U);
  FminmSHelper(1.0f, -1.0f, 0xbf800000U);
  FminmSHelper(3.14f, 3.15f, 0x4048f5c3U);
  FminmSHelper(-100.0f, -50.0f, 0xc2c80000U);

  // Zero comparisons
  FminmSHelper(0.0f, 0.0f, 0x00000000U);
  FminmSHelper(base::bit_cast<float>(0x80000000U),
               base::bit_cast<float>(0x00000000U),
               0x80000000U);  // min(-0.0, +0.0) = -0.0
  FminmSHelper(base::bit_cast<float>(0x00000000U),
               base::bit_cast<float>(0x80000000U),
               0x80000000U);  // min(+0.0, -0.0) = -0.0
  FminmSHelper(0.0f, 1.0f, 0x00000000U);
  FminmSHelper(-1.0f, 0.0f, 0xbf800000U);

  // NaN handling (IEEE 754-2019: returns canonical qNaN)
  FminmSHelper(base::bit_cast<float>(0x7fc00000U),
               base::bit_cast<float>(0x3f800000U),
               0x7fc00000U);  // min(qNaN, 1.0) = qNaN
  FminmSHelper(base::bit_cast<float>(0x3f800000U),
               base::bit_cast<float>(0x7fc00000U),
               0x7fc00000U);  // min(1.0, qNaN) = qNaN
  FminmSHelper(base::bit_cast<float>(0x7fc00000U),
               base::bit_cast<float>(0x7fc00000U),
               0x7fc00000U);  // min(qNaN, qNaN) = qNaN
  FminmSHelper(base::bit_cast<float>(0x7ff00000U),
               base::bit_cast<float>(0x3f800000U),
               0x7fc00000U);  // min(sNaN, 1.0) = canonical qNaN
  FminmSHelper(base::bit_cast<float>(0xffc00000U),
               base::bit_cast<float>(0x3f800000U),
               0x7fc00000U);  // min(-qNaN, 1.0) = canonical qNaN

  // Infinity comparisons
  FminmSHelper(base::bit_cast<float>(0x7f800000U),
               base::bit_cast<float>(0x3f800000U),
               0x3f800000U);  // min(+Inf, 1.0) = 1.0
  FminmSHelper(base::bit_cast<float>(0x3f800000U),
               base::bit_cast<float>(0x7f800000U),
               0x3f800000U);  // min(1.0, +Inf) = 1.0
  FminmSHelper(base::bit_cast<float>(0xff800000U),
               base::bit_cast<float>(0x3f800000U),
               0xff800000U);  // min(-Inf, 1.0) = -Inf
  FminmSHelper(base::bit_cast<float>(0x7f800000U),
               base::bit_cast<float>(0xff800000U),
               0xff800000U);  // min(+Inf, -Inf) = -Inf
  FminmSHelper(base::bit_cast<float>(0x7f800000U),
               base::bit_cast<float>(0x7f800000U),
               0x7f800000U);  // min(+Inf, +Inf) = +Inf
  FminmSHelper(base::bit_cast<float>(0xff800000U),
               base::bit_cast<float>(0xff800000U),
               0xff800000U);  // min(-Inf, -Inf) = -Inf

  // Denormal numbers
  FminmSHelper(base::bit_cast<float>(0x00000001U),
               base::bit_cast<float>(0x3f800000U),
               0x00000001U);  // min(smallest denorm, 1.0)
  FminmSHelper(base::bit_cast<float>(0x3f800000U),
               base::bit_cast<float>(0x00000001U),
               0x00000001U);  // min(1.0, smallest denorm)
  FminmSHelper(base::bit_cast<float>(0x007fffffU),
               base::bit_cast<float>(0x00800000U),
               0x007fffffU);  // min(largest denorm, smallest normal)
  FminmSHelper(base::bit_cast<float>(0x00000001U),
               base::bit_cast<float>(0x00000002U),
               0x00000001U);  // min(denorm1, denorm2)

  // Boundary values
  FminmSHelper(base::bit_cast<float>(0x7f7fffffU),
               base::bit_cast<float>(0x7f800000U),
               0x7f7fffffU);  // min(FLT_MAX, +Inf)
  FminmSHelper(base::bit_cast<float>(0x00800000U),
               base::bit_cast<float>(0x00800001U),
               0x00800000U);  // min(FLT_MIN, FLT_MIN+1)
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fminm_d) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  auto FminmDHelper = [](double rs1, double rs2, uint64_t expected_bits) {
    auto fn = [](MacroAssembler& assm) { __ fminm_d(fa0, fa0, fa1); };
    auto res = GenAndRunTest<double, double>(rs1, rs2, fn);
    CHECK_EQ(expected_bits, base::bit_cast<uint64_t>(res));
  };
  // Normal comparisons
  FminmDHelper(1.0, 2.0, 0x3ff0000000000000ULL);
  FminmDHelper(2.0, 1.0, 0x3ff0000000000000ULL);
  FminmDHelper(-1.0, 1.0, 0xbff0000000000000ULL);
  FminmDHelper(1.0, -1.0, 0xbff0000000000000ULL);

  // Zero comparisons
  FminmDHelper(0.0, 0.0, 0x0000000000000000ULL);
  FminmDHelper(base::bit_cast<double>(0x8000000000000000ULL),
               base::bit_cast<double>(0x0000000000000000ULL),
               0x8000000000000000ULL);  // min(-0.0, +0.0) = -0.0
  FminmDHelper(base::bit_cast<double>(0x0000000000000000ULL),
               base::bit_cast<double>(0x8000000000000000ULL),
               0x8000000000000000ULL);  // min(+0.0, -0.0) = -0.0

  // NaN handling (returns canonical qNaN)
  FminmDHelper(base::bit_cast<double>(0x7ff8000000000000ULL),
               base::bit_cast<double>(0x3ff0000000000000ULL),
               0x7ff8000000000000ULL);  // min(qNaN, 1.0) = qNaN
  FminmDHelper(base::bit_cast<double>(0x3ff0000000000000ULL),
               base::bit_cast<double>(0x7ff8000000000000ULL),
               0x7ff8000000000000ULL);  // min(1.0, qNaN) = qNaN
  FminmDHelper(base::bit_cast<double>(0x7ff8000000000000ULL),
               base::bit_cast<double>(0x7ff8000000000000ULL),
               0x7ff8000000000000ULL);  // min(qNaN, qNaN) = qNaN
  FminmDHelper(base::bit_cast<double>(0x7ff0000000000001ULL),
               base::bit_cast<double>(0x3ff0000000000000ULL),
               0x7ff8000000000000ULL);  // min(sNaN, 1.0) = canonical qNaN
  FminmDHelper(base::bit_cast<double>(0xfff8000000000000ULL),
               base::bit_cast<double>(0x3ff0000000000000ULL),
               0x7ff8000000000000ULL);  // min(-qNaN, 1.0) = canonical qNaN

  // Infinity comparisons
  FminmDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
               base::bit_cast<double>(0x3ff0000000000000ULL),
               0x3ff0000000000000ULL);  // min(+Inf, 1.0) = 1.0
  FminmDHelper(base::bit_cast<double>(0xfff0000000000000ULL),
               base::bit_cast<double>(0x3ff0000000000000ULL),
               0xfff0000000000000ULL);  // min(-Inf, 1.0) = -Inf
  FminmDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
               base::bit_cast<double>(0xfff0000000000000ULL),
               0xfff0000000000000ULL);  // min(+Inf, -Inf) = -Inf

  // Denormal numbers
  FminmDHelper(base::bit_cast<double>(0x0000000000000001ULL),
               base::bit_cast<double>(0x3ff0000000000000ULL),
               0x0000000000000001ULL);  // min(smallest denorm, 1.0)
  FminmDHelper(base::bit_cast<double>(0x000fffffffffffffULL),
               base::bit_cast<double>(0x0010000000000000ULL),
               0x000fffffffffffffULL);  // min(largest denorm, smallest normal)
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fmaxm_s) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  auto FmaxmSHelper = [](float rs1, float rs2, uint32_t expected_bits) {
    auto fn = [](MacroAssembler& assm) { __ fmaxm_s(fa0, fa0, fa1); };
    auto res = GenAndRunTest<float, float>(rs1, rs2, fn);
    CHECK_EQ(expected_bits, base::bit_cast<uint32_t>(res));
  };
  // Normal comparisons
  FmaxmSHelper(1.0f, 2.0f, 0x40000000U);
  FmaxmSHelper(2.0f, 1.0f, 0x40000000U);
  FmaxmSHelper(-1.0f, 1.0f, 0x3f800000U);
  FmaxmSHelper(1.0f, -1.0f, 0x3f800000U);

  // Zero comparisons
  FmaxmSHelper(0.0f, 0.0f, 0x00000000U);
  FmaxmSHelper(base::bit_cast<float>(0x80000000U),
               base::bit_cast<float>(0x00000000U),
               0x00000000U);  // max(-0.0, +0.0) = +0.0
  FmaxmSHelper(base::bit_cast<float>(0x00000000U),
               base::bit_cast<float>(0x80000000U),
               0x00000000U);  // max(+0.0, -0.0) = +0.0

  // NaN handling (returns canonical qNaN)
  FmaxmSHelper(base::bit_cast<float>(0x7fc00000U),
               base::bit_cast<float>(0x3f800000U),
               0x7fc00000U);  // max(qNaN, 1.0) = qNaN
  FmaxmSHelper(base::bit_cast<float>(0x3f800000U),
               base::bit_cast<float>(0x7fc00000U),
               0x7fc00000U);  // max(1.0, qNaN) = qNaN
  FmaxmSHelper(base::bit_cast<float>(0x7fc00000U),
               base::bit_cast<float>(0x7fc00000U),
               0x7fc00000U);  // max(qNaN, qNaN) = qNaN
  FmaxmSHelper(base::bit_cast<float>(0x7ff00000U),
               base::bit_cast<float>(0x3f800000U),
               0x7fc00000U);  // max(sNaN, 1.0) = canonical qNaN
  FmaxmSHelper(base::bit_cast<float>(0xffc00000U),
               base::bit_cast<float>(0x3f800000U),
               0x7fc00000U);  // max(-qNaN, 1.0) = canonical qNaN

  // Infinity comparisons
  FmaxmSHelper(base::bit_cast<float>(0x7f800000U),
               base::bit_cast<float>(0x3f800000U),
               0x7f800000U);  // max(+Inf, 1.0) = +Inf
  FmaxmSHelper(base::bit_cast<float>(0xff800000U),
               base::bit_cast<float>(0x3f800000U),
               0x3f800000U);  // max(-Inf, 1.0) = 1.0
  FmaxmSHelper(base::bit_cast<float>(0x7f800000U),
               base::bit_cast<float>(0xff800000U),
               0x7f800000U);  // max(+Inf, -Inf) = +Inf

  // Denormal numbers
  FmaxmSHelper(base::bit_cast<float>(0x00000001U),
               base::bit_cast<float>(0x3f800000U),
               0x3f800000U);  // max(smallest denorm, 1.0)
  FmaxmSHelper(base::bit_cast<float>(0x007fffffU),
               base::bit_cast<float>(0x00800000U),
               0x00800000U);  // max(largest denorm, smallest normal)
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fmaxm_d) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  auto FmaxmDHelper = [](double rs1, double rs2, uint64_t expected_bits) {
    auto fn = [](MacroAssembler& assm) { __ fmaxm_d(fa0, fa0, fa1); };
    auto res = GenAndRunTest<double, double>(rs1, rs2, fn);
    CHECK_EQ(expected_bits, base::bit_cast<uint64_t>(res));
  };
  // Normal comparisons
  FmaxmDHelper(1.0, 2.0, 0x4000000000000000ULL);
  FmaxmDHelper(2.0, 1.0, 0x4000000000000000ULL);
  FmaxmDHelper(-1.0, 1.0, 0x3ff0000000000000ULL);
  FmaxmDHelper(1.0, -1.0, 0x3ff0000000000000ULL);

  // Zero comparisons
  FmaxmDHelper(0.0, 0.0, 0x0000000000000000ULL);
  FmaxmDHelper(base::bit_cast<double>(0x8000000000000000ULL),
               base::bit_cast<double>(0x0000000000000000ULL),
               0x0000000000000000ULL);  // max(-0.0, +0.0) = +0.0
  FmaxmDHelper(base::bit_cast<double>(0x0000000000000000ULL),
               base::bit_cast<double>(0x8000000000000000ULL),
               0x0000000000000000ULL);  // max(+0.0, -0.0) = +0.0

  // NaN handling (returns canonical qNaN)
  FmaxmDHelper(base::bit_cast<double>(0x7ff8000000000000ULL),
               base::bit_cast<double>(0x3ff0000000000000ULL),
               0x7ff8000000000000ULL);  // max(qNaN, 1.0) = qNaN
  FmaxmDHelper(base::bit_cast<double>(0x3ff0000000000000ULL),
               base::bit_cast<double>(0x7ff8000000000000ULL),
               0x7ff8000000000000ULL);  // max(1.0, qNaN) = qNaN
  FmaxmDHelper(base::bit_cast<double>(0x7ff8000000000000ULL),
               base::bit_cast<double>(0x7ff8000000000000ULL),
               0x7ff8000000000000ULL);  // max(qNaN, qNaN) = qNaN
  FmaxmDHelper(base::bit_cast<double>(0x7ff0000000000001ULL),
               base::bit_cast<double>(0x3ff0000000000000ULL),
               0x7ff8000000000000ULL);  // max(sNaN, 1.0) = canonical qNaN
  FmaxmDHelper(base::bit_cast<double>(0xfff8000000000000ULL),
               base::bit_cast<double>(0x3ff0000000000000ULL),
               0x7ff8000000000000ULL);  // max(-qNaN, 1.0) = canonical qNaN

  // Infinity comparisons
  FmaxmDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
               base::bit_cast<double>(0x3ff0000000000000ULL),
               0x7ff0000000000000ULL);  // max(+Inf, 1.0) = +Inf
  FmaxmDHelper(base::bit_cast<double>(0xfff0000000000000ULL),
               base::bit_cast<double>(0x3ff0000000000000ULL),
               0x3ff0000000000000ULL);  // max(-Inf, 1.0) = 1.0
  FmaxmDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
               base::bit_cast<double>(0xfff0000000000000ULL),
               0x7ff0000000000000ULL);  // max(+Inf, -Inf) = +Inf

  // Denormal numbers
  FmaxmDHelper(base::bit_cast<double>(0x0000000000000001ULL),
               base::bit_cast<double>(0x3ff0000000000000ULL),
               0x3ff0000000000000ULL);  // max(smallest denorm, 1.0)
  FmaxmDHelper(base::bit_cast<double>(0x000fffffffffffffULL),
               base::bit_cast<double>(0x0010000000000000ULL),
               0x0010000000000000ULL);  // max(largest denorm, smallest normal)
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_froundnx_s) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  auto FroundnxSHelper = [](float rs1, uint32_t expected_bits) {
    auto fn = [](MacroAssembler& assm) { __ froundnx_s(fa0, fa0); };
    auto res = GenAndRunTest<float, float>(rs1, fn);
    CHECK_EQ(expected_bits, base::bit_cast<uint32_t>(res));
  };
  // Normal values
  FroundnxSHelper(1.0f, 0x3f800000U);
  FroundnxSHelper(2.0f, 0x40000000U);
  FroundnxSHelper(0.5f, 0x00000000U);  // 0.5 rounds to 0 (RNE)
  FroundnxSHelper(-1.0f, 0xbf800000U);

  // Round to nearest (ties to even)
  FroundnxSHelper(1.5f, 0x40000000U);  // 1.5 -> 2.0
  FroundnxSHelper(2.0f, 0x40000000U);  // 2.0 -> 2.0
  FroundnxSHelper(3.0f, 0x40400000U);  // 3.0 -> 3.0
  FroundnxSHelper(3.5f, 0x40800000U);  // 3.5 -> 4.0
  FroundnxSHelper(4.0f, 0x40800000U);  // 4.0 -> 4.0
  FroundnxSHelper(5.0f, 0x40a00000U);  // 5.0 -> 5.0
  FroundnxSHelper(6.0f, 0x40c00000U);  // 6.0 -> 6.0
  FroundnxSHelper(7.0f, 0x40e00000U);  // 7.0 -> 7.0
  FroundnxSHelper(8.0f, 0x41000000U);  // 8.0 -> 8.0

  // Negative values
  FroundnxSHelper(-1.5f, 0xc0000000U);  // -1.5 -> -2.0
  FroundnxSHelper(-3.5f, 0xc0800000U);  // -3.5 -> -4.0

  // Zero
  FroundnxSHelper(0.0f, 0x00000000U);
  FroundnxSHelper(base::bit_cast<float>(0x80000000U), 0x80000000U);  // -0.0

  // NaN (returns canonical qNaN)
  FroundnxSHelper(base::bit_cast<float>(0x7fc00000U), 0x7fc00000U);  // qNaN
  FroundnxSHelper(base::bit_cast<float>(0x7ff00000U),
                  0x7fc00000U);  // sNaN -> canonical qNaN
  FroundnxSHelper(base::bit_cast<float>(0xffc00000U),
                  0x7fc00000U);  // -qNaN -> canonical qNaN

  // Infinity
  FroundnxSHelper(base::bit_cast<float>(0x7f800000U), 0x7f800000U);  // +Inf
  FroundnxSHelper(base::bit_cast<float>(0xff800000U), 0xff800000U);  // -Inf

  // Denormal numbers (round to zero)
  FroundnxSHelper(base::bit_cast<float>(0x00000001U),
                  0x00000000U);  // smallest denorm
  FroundnxSHelper(base::bit_cast<float>(0x007fffffU),
                  0x00000000U);  // largest denorm
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_froundnx_d) {
  if (!CpuFeatures::IsSupported(ZFA)) {
    return;
  }
  auto FroundnxDHelper = [](double rs1, uint64_t expected_bits) {
    auto fn = [](MacroAssembler& assm) { __ froundnx_d(fa0, fa0); };
    auto res = GenAndRunTest<double, double>(rs1, fn);
    CHECK_EQ(expected_bits, base::bit_cast<uint64_t>(res));
  };
  // Normal values
  FroundnxDHelper(1.0, 0x3ff0000000000000ULL);
  FroundnxDHelper(2.0, 0x4000000000000000ULL);
  FroundnxDHelper(0.5, 0x0000000000000000ULL);  // 0.5 rounds to 0 (RNE)
  FroundnxDHelper(-1.0, 0xbff0000000000000ULL);

  // Round to nearest (ties to even)
  FroundnxDHelper(1.5, 0x4000000000000000ULL);  // 1.5 -> 2.0
  FroundnxDHelper(2.0, 0x4000000000000000ULL);  // 2.0 -> 2.0
  FroundnxDHelper(3.0, 0x4008000000000000ULL);  // 3.0 -> 3.0
  FroundnxDHelper(3.5, 0x4010000000000000ULL);  // 3.5 -> 4.0
  FroundnxDHelper(4.0, 0x4010000000000000ULL);  // 4.0 -> 4.0
  FroundnxDHelper(5.0, 0x4014000000000000ULL);  // 5.0 -> 5.0
  FroundnxDHelper(6.0, 0x4018000000000000ULL);  // 6.0 -> 6.0
  FroundnxDHelper(7.0, 0x401c000000000000ULL);  // 7.0 -> 7.0
  FroundnxDHelper(8.0, 0x4020000000000000ULL);  // 8.0 -> 8.0

  // Negative values
  FroundnxDHelper(-1.5, 0xc000000000000000ULL);  // -1.5 -> -2.0
  FroundnxDHelper(-3.5, 0xc010000000000000ULL);  // -3.5 -> -4.0

  // Zero
  FroundnxDHelper(0.0, 0x0000000000000000ULL);
  FroundnxDHelper(base::bit_cast<double>(0x8000000000000000ULL),
                  0x8000000000000000ULL);  // -0.0

  // NaN (returns canonical qNaN)
  FroundnxDHelper(base::bit_cast<double>(0x7ff8000000000000ULL),
                  0x7ff8000000000000ULL);  // qNaN
  FroundnxDHelper(base::bit_cast<double>(0x7ff0000000000001ULL),
                  0x7ff8000000000000ULL);  // sNaN -> canonical qNaN
  FroundnxDHelper(base::bit_cast<double>(0xfff8000000000000ULL),
                  0x7ff8000000000000ULL);  // -qNaN -> canonical qNaN

  // Infinity
  FroundnxDHelper(base::bit_cast<double>(0x7ff0000000000000ULL),
                  0x7ff0000000000000ULL);  // +Inf
  FroundnxDHelper(base::bit_cast<double>(0xfff0000000000000ULL),
                  0xfff0000000000000ULL);  // -Inf

  // Denormal numbers (round to zero)
  FroundnxDHelper(base::bit_cast<double>(0x0000000000000001ULL),
                  0x0000000000000000ULL);  // smallest denorm
  FroundnxDHelper(base::bit_cast<double>(0x000fffffffffffffULL),
                  0x0000000000000000ULL);  // largest denorm
}

// --RVZFH Standard Extension --
TEST_F(AssemblerRISCV64Test, RISCV_UTEST_flh_fsh) {
  if (!CpuFeatures::IsSupported(ZFH)) {
    return;
  }
  auto fn = [](MacroAssembler& assm) {
    __ fsh(fa0, a0, 0);
    __ flh(fa0, a0, 0);
  };
  GenAndRunTestForLoadStore<uint16_t>(
      Float16::FromFloat32(-2345.678f).get_bits(), fn);
}

const std::vector<std::pair<double, uint16_t>> fp16_test_values() {
  std::pair<double, uint16_t> kValues[] = {
      std::make_pair(2.980232238769532e-8,
                     DoubleToFloat16(5.960464477539063e-8f)),
      std::make_pair(1.1, DoubleToFloat16(1.1)),
      std::make_pair(0.00006103515625, DoubleToFloat16(0.00006103515625)),
      std::make_pair(static_cast<double>(2051), DoubleToFloat16(2051.0))};
  return std::vector<std::pair<double, uint16_t>>(&kValues[0],
                                                  &kValues[arraysize(kValues)]);
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvthd) {
  if (!CpuFeatures::IsSupported(ZFH)) {
    return;
  }
  auto value = fp16_test_values();
  for (auto i = value.begin(); i != value.end(); ++i) {
    auto fn = [](MacroAssembler& assm) {
      __ fcvt_h_d(fa1, fa0);
      __ fmv_x_h(a0, fa1);
    };
    auto res = GenAndRunTest<uint16_t, double>(i->first, fn);
    CHECK_EQ(i->second, res);
  }
}

namespace {

struct H {
  static uint16_t bits(float f) { return Float16::FromFloat32(f).get_bits(); }
};

template <typename F>
uint16_t RunHalfUnary(uint16_t input, F&& op) {
  auto fn = [&op](MacroAssembler& assm) {
    __ fmv_h_x(fa0, a0);
    op(assm);
    __ fmv_x_h(a0, fa0);
  };
  return static_cast<uint16_t>(
      GenAndRunTest<uint64_t, uint64_t>(static_cast<uint64_t>(input), fn));
}

template <typename F>
uint16_t RunHalfBinary(uint16_t a, uint16_t b, F&& op) {
  auto fn = [&op](MacroAssembler& assm) {
    __ fmv_h_x(fa0, a0);
    __ fmv_h_x(fa1, a1);
    op(assm);
    __ fmv_x_h(a0, fa0);
  };
  return static_cast<uint16_t>(GenAndRunTest<uint64_t, uint64_t>(
      static_cast<uint64_t>(a), static_cast<uint64_t>(b), fn));
}

template <typename F>
uint16_t RunHalfTernary(uint16_t a, uint16_t b, uint16_t c, F&& op) {
  auto fn = [&op](MacroAssembler& assm) {
    __ fmv_h_x(fa0, a0);
    __ fmv_h_x(fa1, a1);
    __ fmv_h_x(fa2, a2);
    op(assm);
    __ fmv_x_h(a0, fa0);
  };
  return static_cast<uint16_t>(GenAndRunTest<uint64_t, uint64_t>(
      static_cast<uint64_t>(a), static_cast<uint64_t>(b),
      static_cast<uint64_t>(c), fn));
}

template <typename F>
int32_t RunHalfCompare(uint16_t a, uint16_t b, F&& op) {
  auto fn = [&op](MacroAssembler& assm) {
    __ fmv_h_x(fa0, a0);
    __ fmv_h_x(fa1, a1);
    op(assm);
  };
  return GenAndRunTest<int32_t, uint64_t>(static_cast<uint64_t>(a),
                                          static_cast<uint64_t>(b), fn);
}

constexpr uint16_t kFP16NegZero = 0x8000u;
constexpr uint16_t kFP16NegInfinity = 0xFC00u;

}  // namespace

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fadd_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fadd_h(fa0, fa0, fa1); };
  FOR_FLOAT32_INPUTS(i) {
    FOR_FLOAT32_INPUTS(j) {
      Float16 hi = Float16::FromFloat32(i);
      Float16 hj = Float16::FromFloat32(j);
      float sum = hi.ToFloat32() + hj.ToFloat32();
      if (std::isnan(sum)) sum = std::numeric_limits<float>::quiet_NaN();
      uint16_t expected = Float16::FromFloat32(sum).get_bits();
      CHECK_EQ(expected, RunHalfBinary(hi.get_bits(), hj.get_bits(), op));
    }
  }
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fsub_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fsub_h(fa0, fa0, fa1); };
  auto check = [&op](float a, float b) {
    Float16 ha = Float16::FromFloat32(a);
    Float16 hb = Float16::FromFloat32(b);
    float result = ha.ToFloat32() - hb.ToFloat32();
    if (std::isnan(result)) result = std::numeric_limits<float>::quiet_NaN();
    uint16_t expected = Float16::FromFloat32(result).get_bits();
    CHECK_EQ(expected, RunHalfBinary(ha.get_bits(), hb.get_bits(), op));
  };
  check(3.0f, 2.0f);
  check(0.1f, 1.0f);
  check(1.0f, 1.0f);
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fmul_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fmul_h(fa0, fa0, fa1); };
  auto check = [&op](float a, float b) {
    Float16 ha = Float16::FromFloat32(a);
    Float16 hb = Float16::FromFloat32(b);
    float result = ha.ToFloat32() * hb.ToFloat32();
    if (std::isnan(result)) result = std::numeric_limits<float>::quiet_NaN();
    uint16_t expected = Float16::FromFloat32(result).get_bits();
    CHECK_EQ(expected, RunHalfBinary(ha.get_bits(), hb.get_bits(), op));
  };
  check(2.2f, 3.7f);
  check(1.4f, -2.13f);
  check(0.07f, 5.12f);
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fdiv_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fdiv_h(fa0, fa0, fa1); };
  auto check = [&op](float a, float b) {
    Float16 ha = Float16::FromFloat32(a);
    Float16 hb = Float16::FromFloat32(b);
    float result = ha.ToFloat32() / hb.ToFloat32();
    if (std::isnan(result)) result = std::numeric_limits<float>::quiet_NaN();
    uint16_t expected = Float16::FromFloat32(result).get_bits();
    CHECK_EQ(expected, RunHalfBinary(ha.get_bits(), hb.get_bits(), op));
  };
  check(3.0f, 2.0f);
  check(4.0f, -2.0f);
  check(1.0f, 0.0f);
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fsqrt_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fsqrt_h(fa0, fa0); };
  CHECK_EQ(H::bits(2.0f), RunHalfUnary(H::bits(4.0f), op));
  CHECK_EQ(H::bits(1.0f), RunHalfUnary(H::bits(1.0f), op));
  CHECK_EQ(H::bits(0.0f), RunHalfUnary(H::bits(0.0f), op));
  CHECK_EQ(kFP16qNaN, RunHalfUnary(H::bits(-1.0f), op) & kFP16qNaN);
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvt_h_s) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto fn = [](MacroAssembler& assm) {
    __ fcvt_h_s(fa0, fa0);
    __ fmv_x_h(a0, fa0);
  };
  CHECK_EQ(H::bits(1.0f), (GenAndRunTest<uint16_t, float>(1.0f, fn)));
  CHECK_EQ(H::bits(-2.0f), (GenAndRunTest<uint16_t, float>(-2.0f, fn)));
  CHECK_EQ(H::bits(0.0f), (GenAndRunTest<uint16_t, float>(0.0f, fn)));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvt_d_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto fn = [](MacroAssembler& assm) {
    __ fmv_h_x(fa0, a0);
    __ fcvt_d_h(fa0, fa0);
  };
  CHECK_EQ(1.0, (GenAndRunTest<double, uint64_t>(
                    static_cast<uint64_t>(H::bits(1.0f)), fn)));
  CHECK_EQ(-2.0, (GenAndRunTest<double, uint64_t>(
                     static_cast<uint64_t>(H::bits(-2.0f)), fn)));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvt_w_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto fn = [](MacroAssembler& assm) {
    __ fmv_h_x(fa0, a0);
    __ fcvt_w_h(a0, fa0);
  };
  CHECK_EQ(3, (GenAndRunTest<int32_t, uint64_t>(
                  static_cast<uint64_t>(H::bits(3.0f)), fn)));
  CHECK_EQ(-1, (GenAndRunTest<int32_t, uint64_t>(
                   static_cast<uint64_t>(H::bits(-1.0f)), fn)));
  CHECK_EQ(0, (GenAndRunTest<int32_t, uint64_t>(
                  static_cast<uint64_t>(H::bits(0.0f)), fn)));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvt_wu_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto fn = [](MacroAssembler& assm) {
    __ fmv_h_x(fa0, a0);
    __ fcvt_wu_h(a0, fa0);
  };
  CHECK_EQ(3u, (GenAndRunTest<uint32_t, uint64_t>(
                   static_cast<uint64_t>(H::bits(3.0f)), fn)));
  CHECK_EQ(0u, (GenAndRunTest<uint32_t, uint64_t>(
                   static_cast<uint64_t>(H::bits(-1.0f)), fn)));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvt_l_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto fn = [](MacroAssembler& assm) {
    __ fmv_h_x(fa0, a0);
    __ fcvt_l_h(a0, fa0);
  };
  CHECK_EQ(int64_t(3), (GenAndRunTest<int64_t, uint64_t>(
                           static_cast<uint64_t>(H::bits(3.0f)), fn)));
  CHECK_EQ(int64_t(-1), (GenAndRunTest<int64_t, uint64_t>(
                            static_cast<uint64_t>(H::bits(-1.0f)), fn)));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvt_lu_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto fn = [](MacroAssembler& assm) {
    __ fmv_h_x(fa0, a0);
    __ fcvt_lu_h(a0, fa0);
  };
  CHECK_EQ(uint64_t(3), (GenAndRunTest<uint64_t, uint64_t>(
                            static_cast<uint64_t>(H::bits(3.0f)), fn)));
  CHECK_EQ(uint64_t(0), (GenAndRunTest<uint64_t, uint64_t>(
                            static_cast<uint64_t>(H::bits(-1.0f)), fn)));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fsgnj_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fsgnj_h(fa0, fa0, fa1); };
  CHECK_EQ(H::bits(2.0f), RunHalfBinary(H::bits(2.0f), H::bits(1.0f), op));
  CHECK_EQ(H::bits(-2.0f), RunHalfBinary(H::bits(2.0f), H::bits(-1.0f), op));
  CHECK_EQ(H::bits(-2.0f), RunHalfBinary(H::bits(-2.0f), H::bits(-1.0f), op));
  CHECK_EQ(H::bits(2.0f), RunHalfBinary(H::bits(-2.0f), H::bits(1.0f), op));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fsgnjn_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fsgnjn_h(fa0, fa0, fa1); };
  CHECK_EQ(H::bits(-2.0f), RunHalfBinary(H::bits(2.0f), H::bits(1.0f), op));
  CHECK_EQ(H::bits(2.0f), RunHalfBinary(H::bits(2.0f), H::bits(-1.0f), op));
  CHECK_EQ(H::bits(-2.0f), RunHalfBinary(H::bits(-2.0f), H::bits(1.0f), op));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fsgnjx_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fsgnjx_h(fa0, fa0, fa1); };
  CHECK_EQ(H::bits(2.0f), RunHalfBinary(H::bits(2.0f), H::bits(1.0f), op));
  CHECK_EQ(H::bits(-2.0f), RunHalfBinary(H::bits(2.0f), H::bits(-1.0f), op));
  CHECK_EQ(H::bits(2.0f), RunHalfBinary(H::bits(-2.0f), H::bits(-1.0f), op));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_feq_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ feq_h(a0, fa0, fa1); };
  CHECK_EQ(1, RunHalfCompare(H::bits(1.0f), H::bits(1.0f), op));
  CHECK_EQ(0, RunHalfCompare(H::bits(1.0f), H::bits(2.0f), op));
  CHECK_EQ(0, RunHalfCompare(kFP16qNaN, kFP16qNaN, op));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_flt_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ flt_h(a0, fa0, fa1); };
  CHECK_EQ(1, RunHalfCompare(H::bits(1.0f), H::bits(2.0f), op));
  CHECK_EQ(0, RunHalfCompare(H::bits(2.0f), H::bits(1.0f), op));
  CHECK_EQ(0, RunHalfCompare(H::bits(1.0f), H::bits(1.0f), op));
  CHECK_EQ(0, RunHalfCompare(kFP16qNaN, H::bits(1.0f), op));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fle_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fle_h(a0, fa0, fa1); };
  CHECK_EQ(1, RunHalfCompare(H::bits(1.0f), H::bits(2.0f), op));
  CHECK_EQ(1, RunHalfCompare(H::bits(1.0f), H::bits(1.0f), op));
  CHECK_EQ(0, RunHalfCompare(H::bits(2.0f), H::bits(1.0f), op));
  CHECK_EQ(0, RunHalfCompare(kFP16qNaN, H::bits(1.0f), op));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fmin_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fmin_h(fa0, fa0, fa1); };
  CHECK_EQ(H::bits(1.0f), RunHalfBinary(H::bits(1.0f), H::bits(2.0f), op));
  CHECK_EQ(H::bits(-1.0f), RunHalfBinary(H::bits(1.0f), H::bits(-1.0f), op));
  CHECK_EQ(H::bits(1.0f), RunHalfBinary(H::bits(1.0f), kFP16qNaN, op));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fmax_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fmax_h(fa0, fa0, fa1); };
  CHECK_EQ(H::bits(2.0f), RunHalfBinary(H::bits(1.0f), H::bits(2.0f), op));
  CHECK_EQ(H::bits(1.0f), RunHalfBinary(H::bits(1.0f), H::bits(-1.0f), op));
  CHECK_EQ(H::bits(1.0f), RunHalfBinary(H::bits(1.0f), kFP16qNaN, op));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fmadd_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fmadd_h(fa0, fa0, fa1, fa2); };
  CHECK_EQ(H::bits(7.0f),
           RunHalfTernary(H::bits(2.0f), H::bits(3.0f), H::bits(1.0f), op));
  CHECK_EQ(H::bits(0.0f),
           RunHalfTernary(H::bits(1.0f), H::bits(1.0f), H::bits(-1.0f), op));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fmsub_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fmsub_h(fa0, fa0, fa1, fa2); };
  CHECK_EQ(H::bits(5.0f),
           RunHalfTernary(H::bits(2.0f), H::bits(3.0f), H::bits(1.0f), op));
  CHECK_EQ(H::bits(2.0f),
           RunHalfTernary(H::bits(3.0f), H::bits(2.0f), H::bits(4.0f), op));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fnmsub_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fnmsub_h(fa0, fa0, fa1, fa2); };
  CHECK_EQ(H::bits(-5.0f),
           RunHalfTernary(H::bits(2.0f), H::bits(3.0f), H::bits(1.0f), op));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fnmadd_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fnmadd_h(fa0, fa0, fa1, fa2); };
  CHECK_EQ(H::bits(-7.0f),
           RunHalfTernary(H::bits(2.0f), H::bits(3.0f), H::bits(1.0f), op));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvt_s_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto fn = [](MacroAssembler& assm) {
    __ fmv_h_x(fa0, a0);
    __ fcvt_s_h(fa0, fa0);
  };
  CHECK_EQ(1.0f, (GenAndRunTest<float, uint64_t>(
                     static_cast<uint64_t>(H::bits(1.0f)), fn)));
  CHECK_EQ(-2.0f, (GenAndRunTest<float, uint64_t>(
                      static_cast<uint64_t>(H::bits(-2.0f)), fn)));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvt_h_w) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto fn = [](MacroAssembler& assm) {
    __ fcvt_h_w(fa0, a0);
    __ fmv_x_h(a0, fa0);
  };
  CHECK_EQ(H::bits(3.0f), (GenAndRunTest<uint16_t, int32_t>(3, fn)));
  CHECK_EQ(H::bits(-1.0f), (GenAndRunTest<uint16_t, int32_t>(-1, fn)));
  CHECK_EQ(H::bits(0.0f), (GenAndRunTest<uint16_t, int32_t>(0, fn)));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvt_h_wu) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto fn = [](MacroAssembler& assm) {
    __ fcvt_h_wu(fa0, a0);
    __ fmv_x_h(a0, fa0);
  };
  CHECK_EQ(H::bits(3.0f), (GenAndRunTest<uint16_t, uint32_t>(3u, fn)));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvt_h_l) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto fn = [](MacroAssembler& assm) {
    __ fcvt_h_l(fa0, a0);
    __ fmv_x_h(a0, fa0);
  };
  CHECK_EQ(H::bits(3.0f), (GenAndRunTest<uint16_t, int64_t>(3, fn)));
  CHECK_EQ(H::bits(-1.0f), (GenAndRunTest<uint16_t, int64_t>(-1, fn)));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fcvt_h_lu) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto fn = [](MacroAssembler& assm) {
    __ fcvt_h_lu(fa0, a0);
    __ fmv_x_h(a0, fa0);
  };
  CHECK_EQ(H::bits(3.0f), (GenAndRunTest<uint16_t, uint64_t>(3u, fn)));
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_fclass_h) {
  if (!CpuFeatures::IsSupported(ZFH)) return;
  auto op = [](MacroAssembler& assm) { __ fclass_h(a0, fa0); };
  auto run = [&op](uint16_t input) {
    auto fn = [&op](MacroAssembler& assm) {
      __ fmv_h_x(fa0, a0);
      op(assm);
    };
    return GenAndRunTest<uint64_t, uint64_t>(static_cast<uint64_t>(input), fn);
  };
  CHECK_EQ(FClassFlag::kPositiveNormalNumber, run(H::bits(1.0f)));
  CHECK_EQ(FClassFlag::kNegativeNormalNumber, run(H::bits(-1.0f)));
  CHECK_EQ(FClassFlag::kPositiveZero, run(H::bits(0.0f)));
  CHECK_EQ(FClassFlag::kNegativeZero, run(kFP16NegZero));
  CHECK_EQ(FClassFlag::kPositiveInfinity, run(kFP16Infinity));
  CHECK_EQ(FClassFlag::kNegativeInfinity, run(kFP16NegInfinity));
  CHECK_EQ(FClassFlag::kQuietNaN, run(kFP16qNaN));
}

// -- 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_F(AssemblerRISCV64Test, RISCV0) {
  FOR_INT64_INPUTS(i) {
    auto fn = [i](MacroAssembler& assm) { __ RV_li(a0, i); };
    auto res = GenAndRunTest(fn);
    CHECK_EQ(i, res);
  }
}

TEST_F(AssemblerRISCV64Test, RISCVZicond) {
  if (!CpuFeatures::IsSupported(ZICOND)) {
    return;
  }

  FOR_INT64_INPUTS(i) {
    FOR_INT64_INPUTS(j) {
      auto fn = [i, j](MacroAssembler& assm) {
        __ li(a1, i);
        __ li(a0, j);
        __ MoveIfZero(a0, a1, a0);
      };
      auto res = GenAndRunTest(fn);
      CHECK_EQ(j != 0 ? j : i, res);
    }
  }

  FOR_INT64_INPUTS(i) {
    FOR_INT64_INPUTS(j) {
      auto fn = [i, j](MacroAssembler& assm) {
        __ li(a1, i);
        __ li(a2, j);
        __ czero_eqz(a0, a1, a2);
      };
      auto res = GenAndRunTest(fn);
      CHECK_EQ(j == 0 ? 0 : i, res);
    }
  }

  FOR_INT64_INPUTS(i) {
    FOR_INT64_INPUTS(j) {
      auto fn = [i, j](MacroAssembler& assm) {
        __ li(a1, i);
        __ li(a2, j);
        __ czero_nez(a0, a1, a2);
      };
      auto res = GenAndRunTest(fn);
      CHECK_EQ(j != 0 ? 0 : i, res);
    }
  }
}

#ifdef USE_SIMULATOR
TEST_F(AssemblerRISCV64Test, RISCVZicfiss) {
  if (!CpuFeatures::IsSupported(ZICFISS)) {
    return;
  }

  {
    auto fn = [](MacroAssembler& assm) {
      __ sspush_ra();
      __ sspopchk_ra();
    };
    GenAndRunTest(fn);
  }
  {
    auto fn = [](MacroAssembler& assm) {
      for (int i = 0; i <= kInitialShadowStackSize + 1000; i++) {
        __ li(t0, i);
        __ sspush_t0();
      }
      for (int i = kInitialShadowStackSize + 1000; i >= 0; i--) {
        __ li(t0, i);
        __ sspopchk_t0();
      }
    };
    GenAndRunTest(fn);
  }
}

TEST_F(AssemblerRISCV64Test, RISCVZicfiss_popchk) {
  if (!CpuFeatures::IsSupported(ZICFISS)) {
    return;
  }
  i::v8_flags.sim_abort_on_shadowstack_mismatch = false;

  Isolate* isolate = i_isolate();
  {
    auto fn = [](MacroAssembler& assm) {
      __ li(t0, -1);
      __ sspush_t0();
      __ li(t0, 0);
      __ sspopchk_t0();
    };
    GenAndRunTest(fn);
  }
  CHECK_EQ(
      isolate->CurrentPerIsolateThreadData()->simulator()->SSMismatchCount(),
      1);
}

TEST_F(AssemblerRISCV64Test, RISCVZicfiss_popchk2) {
  if (!CpuFeatures::IsSupported(ZICFISS)) {
    return;
  }
  i::v8_flags.sim_abort_on_shadowstack_mismatch = false;

  Isolate* isolate = i_isolate();
  {
    auto fn = [](MacroAssembler& assm) {
      __ li(t0, -1);
      __ sspush_t0();
      __ li(t0, 0);
      __ sspush_t0();

      __ sspopchk_t0();
      __ li(t0, 3);
      __ sspopchk_t0();
    };
    GenAndRunTest(fn);
  }
  CHECK_EQ(
      isolate->CurrentPerIsolateThreadData()->simulator()->SSMismatchCount(),
      1);
}
#endif

TEST_F(AssemblerRISCV64Test, RISCVLi) {
  FOR_INT64_INPUTS(i) {
    auto fn = [i](MacroAssembler& assm) { __ RecursiveLi(a0, i); };
    auto res = GenAndRunTest(fn);
    CHECK_EQ(i, res);
  }
  for (int i = 0; i < 64; i++) {
    auto fn = [i](MacroAssembler& assm) { __ RecursiveLi(a0, 1 << i); };
    auto res = GenAndRunTest(fn);
    CHECK_EQ(1 << i, res);
  }
}

TEST_F(AssemblerRISCV64Test, RISCVLiEstimate) {
  Isolate* isolate = i_isolate();
  FOR_INT64_INPUTS(i) {
    HandleScope scope(isolate);
    MacroAssembler assm(isolate, v8::internal::CodeObjectRequired{true});
    Label a, b;
    assm.bind(&a);
    assm.RecordComment("V8 RV_li");
    assm.RV_li(a0, i);
    int count_a = assm.InstructionsGeneratedSince(&a);
    assm.bind(&b);
    assm.RecordComment("LLVM li");
    assm.RecursiveLi(a0, i);
    int count_b = assm.InstructionsGeneratedSince(&b);
    CHECK_LE(count_a, count_b);
  }
}

TEST_F(AssemblerRISCV64Test, RISCV1) {
  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_F(AssemblerRISCV64Test, RISCV2) {
  Isolate* isolate = 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_F(AssemblerRISCV64Test, RISCV3) {
  // Test floating point instructions.

  Isolate* isolate = 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>(isolate, 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_F(AssemblerRISCV64Test, RISCV4) {
  // Test moves between floating point and
  // integer registers.

  Isolate* isolate = 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>(isolate, 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_F(AssemblerRISCV64Test, RISCV5) {
  // Test conversions between doubles and
  // integers.

  Isolate* isolate = 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>(isolate, 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_F(AssemblerRISCV64Test, RISCV6) {
  // Test simple memory loads and stores.

  Isolate* isolate = 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>(isolate, 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_F(AssemblerRISCV64Test, FCLASS) {
  {
    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_F(AssemblerRISCV64Test, RISCV7) {
  // Test floating point compare and
  // branch instructions.

  Isolate* isolate = 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>(isolate, 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_F(AssemblerRISCV64Test, RISCV9) {
  // Test BRANCH improvements.

  Isolate* isolate = i_isolate();
  HandleScope scope(isolate);

  MacroAssembler assm(isolate, v8::internal::CodeObjectRequired{true});
  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_F(AssemblerRISCV64Test, NAN_BOX) {
  // Test float NaN-boxing.

  // 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 | base::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)base::bit_cast<uint32_t>(1234.56f), res);
  }

  // Test signaling NaN in FMV.S
  {
    auto fn = [](MacroAssembler& assm) {
      __ fmv_w_x(fa0, a0);
      __ fmv_s(ft1, fa0);
      __ fmv_s(fa0, ft1);
    };
    auto res = GenAndRunTest<uint32_t>(0x7f400000, fn);
    CHECK_EQ((uint32_t)base::bit_cast<uint32_t>(0x7f400000), res);
  }

  // Test signaling NaN in FMV.D
  {
    auto fn = [](MacroAssembler& assm) {
      __ fmv_d_x(fa0, a0);
      __ fmv_d(ft1, fa0);
      __ fmv_d(fa0, ft1);
    };
    auto res = GenAndRunTest<uint64_t>(0x7ff4000000000000, fn);
    CHECK_EQ((uint64_t)base::bit_cast<uint64_t>(0x7ff4000000000000), res);
  }

  // Test FLW and FSW
  Isolate* isolate = 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>(isolate, fn);

  t.a = -123.45;
  t.box = 0;
  t.res = 0;
  f.Call(&t, 0, 0, 0, 0);

  CHECK_EQ(0xFFFFFFFF00000000 | base::bit_cast<int32_t>(t.a), t.box);
  CHECK_EQ((uint64_t)base::bit_cast<uint32_t>(t.a), t.res);
}

TEST_F(AssemblerRISCV64Test, RVC_CI) {
  // Test RV64C extension CI type instructions.
  if (!CpuFeatures::IsSupported(RVC)) return;

  // 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_F(AssemblerRISCV64Test, RVC_CIW) {
  if (!CpuFeatures::IsSupported(RVC)) return;

  // 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_F(AssemblerRISCV64Test, RVC_CR) {
  // Test RV64C extension CR type instructions.
  if (!CpuFeatures::IsSupported(RVC)) return;

  // 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_F(AssemblerRISCV64Test, RVC_CA) {
  // Test RV64C extension CA type instructions.
  if (!CpuFeatures::IsSupported(RVC)) return;

  // 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_F(AssemblerRISCV64Test, RVC_LOAD_STORE_SP) {
  // Test RV64C extension fldsp/fsdsp, lwsp/swsp, ldsp/sdsp.
  if (!CpuFeatures::IsSupported(RVC)) return;

  {
    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_F(AssemblerRISCV64Test, RVC_LOAD_STORE_COMPRESSED) {
  // Test RV64C extension fld,  lw, ld.
  if (!CpuFeatures::IsSupported(RVC)) return;

  Isolate* isolate = 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>(isolate, 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>(isolate, 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>(isolate, 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_F(AssemblerRISCV64Test, RVC_JUMP) {
  if (!CpuFeatures::IsSupported(RVC)) return;

  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_F(AssemblerRISCV64Test, RVC_CB) {
  // Test RV64C extension CI type instructions.
  if (!CpuFeatures::IsSupported(RVC)) return;

  // 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_F(AssemblerRISCV64Test, RVC_CB_BRANCH) {
  if (!CpuFeatures::IsSupported(RVC)) return;
  // Test floating point compare and
  // branch instructions.

  Isolate* isolate = 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>(isolate, 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);
}

#ifdef USE_SIMULATOR  // Remove it when v8 can detect zcb.
TEST_F(AssemblerRISCV64Test, RVC_ZCB) {
  // Test RV64C extension Zcb instructions.

  Isolate* isolate = i_isolate();
  HandleScope scope(isolate);

  // Test c.lbu / c.sb.
  {
    uint8_t mem[4];
    auto fn = [](MacroAssembler& assm) {
      __ c_lbu(a1, a0, 0);
      __ c_lbu(a2, a0, 3);
      __ c_sb(a1, a0, 1);
      __ c_sb(a2, a0, 2);
    };
    auto f = AssembleCode<F3>(isolate, fn);

    mem[0] = 0xAB;
    mem[1] = 0;
    mem[2] = 0;
    mem[3] = 0x80;
    f.Call(mem, 0, 0, 0, 0);
    CHECK_EQ(0xAB, mem[1]);
    CHECK_EQ(0x80, mem[2]);
  }

  // Test c.lhu / c.lh / c.sh.
  {
    uint8_t mem[4];
    auto fn = [](MacroAssembler& assm) {
      __ c_lhu(a1, a0, 0);  // a1 = zext(mem[0:1])
      __ addi(a1, a1, 1);
      __ c_sh(a1, a0, 0);   // mem[0:1] = a1.
      __ c_lh(a2, a0, 2);   // a2 = sext(mem[2:3])
      __ srli(a2, a2, 63);  // a2 = 1 if sign-extended.
      __ c_sb(a2, a0, 1);
    };
    auto f = AssembleCode<F3>(isolate, fn);

    mem[0] = 0x34;
    mem[1] = 0x12;
    mem[2] = 0x00;
    mem[3] = 0x80;
    f.Call(mem, 0, 0, 0, 0);
    CHECK_EQ(0x35, mem[0]);
    CHECK_EQ(0x01, mem[1]);
    CHECK_EQ(0x00, mem[2]);
    CHECK_EQ(0x80, mem[3]);
  }

  // Test c.mul
  {
    auto fn = [](MacroAssembler& assm) {
      __ RV_li(a1, 6);
      __ c_mul(a0, a1);
    };
    auto res = GenAndRunTest<int64_t>(7, fn);
    CHECK_EQ(42, res);
  }

  // Test c.zext.b
  {
    auto fn = [](MacroAssembler& assm) { __ c_zext_b(a0); };
    auto res = GenAndRunTest<int64_t>(0x1234'5678'9ABC'DEFF, fn);
    CHECK_EQ(0xFF, res);
  }

  // Test c.sext.b
  {
    auto fn = [](MacroAssembler& assm) { __ c_sext_b(a0); };
    auto res = GenAndRunTest<int64_t>(0x80, fn);
    CHECK_EQ(-128, res);
  }

  // Test c.zext.h
  {
    auto fn = [](MacroAssembler& assm) { __ c_zext_h(a0); };
    auto res = GenAndRunTest<int64_t>(0x1234'5678'9ABC'DEFF, fn);
    CHECK_EQ(0xDEFF, res);
  }

  // Test c.sext.h
  {
    auto fn = [](MacroAssembler& assm) { __ c_sext_h(a0); };
    auto res = GenAndRunTest<int64_t>(0x1234'5678'9ABC'8000, fn);
    CHECK_EQ(static_cast<int64_t>(0xFFFFFFFFFFFF8000), res);
  }

  // Test c.zext.w
  {
    auto fn = [](MacroAssembler& assm) { __ c_zext_w(a0); };
    auto res = GenAndRunTest<int64_t>(0x1234'5678'9ABC'DEFF, fn);
    CHECK_EQ(static_cast<int64_t>(0x9ABCDEFF), res);
  }

  // Test c.not
  {
    auto fn = [](MacroAssembler& assm) { __ c_not(a0); };
    auto res = GenAndRunTest<int64_t>(0x1234, fn);
    CHECK_EQ(static_cast<int64_t>(0xFFFFFFFFFFFFEDCB), res);
  }
}
#endif

TEST_F(AssemblerRISCV64Test, TARGET_ADDR) {
  Isolate* isolate = i_isolate();
  HandleScope scope(isolate);

#ifdef RISCV_USE_SV39
  // This is the series of instructions to load 39 bit address 0x00304abfe961
  uint32_t buffer[4] = {0x304ac537, 0xfe950513, 0x851513, 0x6156513};
  MacroAssembler assm(isolate, v8::internal::CodeObjectRequired{true});

  uintptr_t addr = reinterpret_cast<uintptr_t>(&buffer[0]);
  Address res = __ target_constant_address_at(static_cast<Address>(addr));
  CHECK_EQ(0x00304abfe961L, res);
#else
  // 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{true});

  uintptr_t addr = reinterpret_cast<uintptr_t>(&buffer[0]);
  Address res = __ target_constant_address_at(static_cast<Address>(addr));
  CHECK_EQ(0x0123456789abL, res);
#endif
}

TEST_F(AssemblerRISCV64Test, SET_TARGET_ADDR) {
  Isolate* isolate = i_isolate();
  HandleScope scope(isolate);

#ifdef RISCV_USE_SV39
  // This is the series of instructions to load 39 bit address 0x00304abfe961
  uint32_t buffer[4] = {0x304ac537, 0xfe950513, 0x851513, 0x6156513};

  MacroAssembler assm(isolate, v8::internal::CodeObjectRequired{true});

  uintptr_t addr = reinterpret_cast<uintptr_t>(&buffer[0]);
  __ set_target_value_at(static_cast<Address>(addr), 0x00304abfe961L, nullptr,
                         FLUSH_ICACHE_IF_NEEDED);
  uintptr_t constant_pool = 0;
  Address res = __ target_address_at(static_cast<Address>(addr),
                                     static_cast<Address>(constant_pool));
  CHECK_EQ(0x00304abfe961L, res);
#else
  // 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{true});

  uintptr_t addr = reinterpret_cast<uintptr_t>(&buffer[0]);
  __ set_target_value_at(static_cast<Address>(addr), 0xba9876543210L, nullptr,
                         FLUSH_ICACHE_IF_NEEDED);
  Address res = __ target_constant_address_at(static_cast<Address>(addr));
  CHECK_EQ(0xba9876543210L, res);
#endif
}

// 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_F(AssemblerRISCV64Test, OUT_OF_RANGE_CVT) {
  {  // 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(AssemblerRISCV64Test, F_NAN) {
  // test floating-point compare w/ NaN, +/-Inf

  // 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_F(AssemblerRISCV64Test, jump_tables1) {
  // Test jump tables with forward jumps.

  Isolate* isolate = i_isolate();
  HandleScope scope(isolate);

  const int kNumCases = 128;
  int values[kNumCases];
  isolate->random_number_generator()->NextBytes(values, sizeof(values));

  auto fn = [values](MacroAssembler& assm) {
    __ addi(sp, sp, -8);
    __ Sd(ra, MemOperand(sp));
    __ Align(8);

    Label labels[kNumCases];
    {
      int pc_offset_before = assm.pc_offset();
      MacroAssembler::BlockPoolsScope block_pools(
          &assm, (kNumCases * 2 + 6) * kInstrSize);
      // Blocking the trampoline scope shouldn't generate code,
      // because that may interfere with the alignment.
      CHECK_EQ(pc_offset_before, assm.pc_offset());

      __ auipc(ra, 0);
      __ slli(t3, a0, 3);
      __ add(t3, t3, ra);
      __ Ld(t3, MemOperand(t3, 6 * kInstrSize));
      __ jr(t3);
      __ nop();  // For 8 byte alignment.
      for (int i = 0; i < kNumCases; ++i) {
        __ dq(&labels[i]);
      }
    }

    Label done;
    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>(isolate, 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_F(AssemblerRISCV64Test, jump_tables2) {
  // Test jump tables with backward jumps.

  Isolate* isolate = i_isolate();
  HandleScope scope(isolate);

  const int kNumCases = 128;
  int values[kNumCases];
  isolate->random_number_generator()->NextBytes(values, sizeof(values));

  auto fn = [values](MacroAssembler& assm) {
    Label dispatch;
    __ addi(sp, sp, -8);
    __ Sd(ra, MemOperand(sp));
    __ j(&dispatch);

    Label labels[kNumCases];
    Label done;
    for (int i = 0; i < kNumCases; ++i) {
      __ bind(&labels[i]);
      __ RV_li(a0, values[i]);
      __ j(&done);
    }

    {
      const int kAlignment = 8;
      const int kMargin = (kAlignment - 1) + (kNumCases * 2 + 6) * kInstrSize;
      MacroAssembler::BlockPoolsScope block_pools(&assm, kMargin);
      __ Align(kAlignment);  // This can emit up to (kAlignment - 1) bytes.
      __ bind(&dispatch);
      __ auipc(ra, 0);
      __ slli(t3, a0, 3);
      __ add(t3, t3, ra);
      __ Ld(t3, MemOperand(t3, 6 * kInstrSize));
      __ jr(t3);
      __ nop();  // For 8 byte alignment.
      for (int i = 0; i < kNumCases; ++i) {
        __ dq(&labels[i]);
      }
    }

    __ bind(&done);
    __ Ld(ra, MemOperand(sp));
    __ addi(sp, sp, 8);
  };

  auto f = AssembleCode<F1>(isolate, 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_F(AssemblerRISCV64Test, jump_tables3) {
  // Test jump tables with backward jumps and embedded heap objects.

  Isolate* isolate = 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);
  }

  auto fn = [values](MacroAssembler& assm) {
    Label dispatch;
    __ addi(sp, sp, -8);
    __ Sd(ra, MemOperand(sp));
    __ j(&dispatch);

    Label labels[kNumCases];
    Label done;
    for (int i = 0; i < kNumCases; ++i) {
      __ bind(&labels[i]);
      Tagged<Object> obj = *values[i];
      int64_t imm64 = obj.ptr();
      __ RV_li(a0, imm64);
      __ j(&done);
    }

    {
      const int kAlignment = 8;
      const int kMargin = (kAlignment - 1) + (kNumCases * 2 + 6) * kInstrSize;
      MacroAssembler::BlockPoolsScope block_pools(&assm, kMargin);
      __ Align(kAlignment);  // This can emit up to (kAlignment - 1) bytes.
      __ bind(&dispatch);
      __ auipc(ra, 0);
      __ slli(t3, a0, 3);
      __ add(t3, t3, ra);
      __ Ld(t3, MemOperand(t3, 6 * kInstrSize));
      __ jr(t3);
      __ nop();  // For 8 byte alignment.
      for (int i = 0; i < kNumCases; ++i) {
        __ dq(&labels[i]);
      }
    }

    __ bind(&done);
    __ Ld(ra, MemOperand(sp));
    __ addi(sp, sp, 8);
  };

  auto f = AssembleCode<F1>(isolate, fn);

  for (int i = 0; i < kNumCases; ++i) {
    Handle<Object> result(
        Tagged<Object>(reinterpret_cast<Address>(f.Call(i, 0, 0, 0, 0))),
        isolate);
#ifdef OBJECT_PRINT
    ::printf("f(%d) = ", i);
    Print(*result, std::cout);
    ::printf("\n");
#endif
    CHECK(values[i].is_identical_to(result));
  }
}

TEST_F(AssemblerRISCV64Test, 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, 12312874234};

  Isolate* isolate = i_isolate();
  HandleScope scope(isolate);
  MacroAssembler assm(isolate, v8::internal::CodeObjectRequired{true});
  for (auto p : immediates) {
    Label a;
    assm.bind(&a);
    assm.RV_li(t0, p);
    int expected_count = assm.RV_li_count(p, true);
    int count = assm.InstructionsGeneratedSince(&a);
    CHECK_EQ(count, expected_count);
  }
}

#define UTEST_LOAD_STORE_RVV(ldname, stname, SEW, arry)                      \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_##stname##ldname##SEW) {          \
    if (!CpuFeatures::IsSupported(RVV)) {                             \
      return;                                                                \
    }                                                                        \
                                                                             \
    Isolate* isolate = i_isolate();                                          \
    HandleScope scope(isolate);                                              \
    int8_t src[kMaxElements];                                                \
    for (size_t i = 0; i < sizeof(src); i++) src[i] = arry[i % arry.size()]; \
    int8_t dst[kMaxElements];                                                \
    auto fn = [](MacroAssembler& assm) {                                     \
      __ VU.set(t0, zero_reg, SEW, m1);                                      \
      __ vl(v2, a0, 0, SEW);                                                 \
      __ vs(v2, a1, 0, SEW);                                                 \
    };                                                                       \
    GenAndRunTest<int32_t, int64_t>((int64_t)src, (int64_t)dst, fn);         \
    CHECK(!memcmp(src, dst, CpuFeatures::vlen() / 8));                       \
  }

UTEST_LOAD_STORE_RVV(vl, vs, E8, compiler::ValueHelper::GetVector<int8_t>())

TEST_F(AssemblerRISCV64Test, RVV_VFMV) {
  if (!CpuFeatures::IsSupported(RVV)) {
    return;
  }

  Isolate* isolate = i_isolate();
  HandleScope scope(isolate);
  for (float a : compiler::ValueHelper::GetVector<float>()) {
    float src = a;
    float dst[kMaxElements] = {0};
    float ref[kMaxElements];
    for (int i = 0; i < kMaxElements; i++) {
      ref[i] = a;
    }
    auto fn = [](MacroAssembler& assm) {
      __ VU.set(t0, zero_reg, VSew::E32, 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, CpuFeatures::vlen() / 8));
  }
}

TEST_F(AssemblerRISCV64Test, RVV_VFMV_signaling_NaN) {
  if (!CpuFeatures::IsSupported(RVV)) {
    return;
  }

  {
    uint32_t n = CpuFeatures::vlen() / 64;
    int64_t rs1_fval = 0x7FF4000000000000;
    int64_t dst[kMaxElements] = {0};
    auto fn = [](MacroAssembler& assm) {
      __ VU.set(t0, zero_reg, VSew::E64, m1);
      __ fmv_d_x(ft0, a0);
      __ vfmv_vf(v1, ft0);
      __ vs(v1, a1, 0, VSew::E64);
    };
    GenAndRunTest<int64_t, int64_t>((int64_t)rs1_fval, (int64_t)dst, fn);
    for (uint32_t i = 0; i < n; i++) {
      CHECK_EQ(rs1_fval, dst[i]);
    }
  }

  {
    uint32_t n = CpuFeatures::vlen() / 32;
    int32_t rs1_fval = 0x7F400000;
    int32_t dst[kMaxElements] = {0};
    auto fn = [](MacroAssembler& assm) {
      __ VU.set(t0, zero_reg, VSew::E32, m1);
      __ fmv_w_x(ft0, a0);
      __ vfmv_vf(v1, ft0);
      __ vs(v1, a1, 0, VSew::E32);
    };
    GenAndRunTest<int64_t, int64_t>((int64_t)rs1_fval, (int64_t)dst, fn);
    for (uint32_t i = 0; i < n; i++) {
      CHECK_EQ(rs1_fval, dst[i]);
    }
  }
}

TEST_F(AssemblerRISCV64Test, RVV_VFNEG_signaling_NaN) {
  if (!CpuFeatures::IsSupported(RVV)) {
    return;
  }

  {
    uint32_t n = CpuFeatures::vlen() / 64;
    int64_t rs1_fval = 0x7FF4000000000000;
    int64_t expected_fval = 0xFFF4000000000000;
    int64_t dst[kMaxElements] = {0};
    auto fn = [](MacroAssembler& assm) {
      __ VU.set(t0, zero_reg, VSew::E64, m1);
      __ fmv_d_x(ft0, a0);
      __ vfmv_vf(v1, ft0);
      __ vfneg_vv(v2, v1);
      __ vs(v2, a1, 0, VSew::E64);
    };
    GenAndRunTest<int64_t, int64_t>((int64_t)rs1_fval, (int64_t)dst, fn);
    for (uint32_t i = 0; i < n; i++) {
      CHECK_EQ(expected_fval, dst[i]);
    }
  }

  {
    uint32_t n = CpuFeatures::vlen() / 32;
    int32_t rs1_fval = 0x7F400000;
    int32_t expected_fval = 0xFF400000;
    int32_t dst[kMaxElements] = {0};
    auto fn = [](MacroAssembler& assm) {
      __ VU.set(t0, zero_reg, VSew::E32, m1);
      __ fmv_w_x(ft0, a0);
      __ vfmv_vf(v1, ft0);
      __ vfneg_vv(v2, v1);
      __ vs(v2, a1, 0, VSew::E32);
    };
    GenAndRunTest<int64_t, int64_t>((int64_t)rs1_fval, (int64_t)dst, fn);
    for (uint32_t i = 0; i < n; i++) {
      CHECK_EQ(expected_fval, dst[i]);
    }
  }
}

// Tests for Floating-Point scalar move instructions between vector and scalar f
// register
#define UTEST_RVV_VF_MV_FORM_WITH_RES(instr_name, reg1, reg2, width, type)    \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name##_##width) {          \
    if (!CpuFeatures::IsSupported(RVV)) {                              \
      return;                                                                 \
    }                                                                         \
                                                                              \
    uint32_t n = CpuFeatures::vlen() / width;                                 \
    for (type fval : compiler::ValueHelper::GetVector<type>()) {              \
      int##width##_t rs1_fval = base::bit_cast<int##width##_t>(fval);         \
      int##width##_t res[kMaxElements] = {0};                                 \
      for (uint32_t i = 0; i < n; i++) res[i] = (rs1_fval + i + 1);           \
      auto fn = [](MacroAssembler& assm) {                                    \
        __ VU.set(t0, zero_reg, VSew::E##width, m1);                          \
        width == 32 ? __ flw(ft0, a0, 0) : __ fld(ft0, a0, 0);                \
        __ vl(v1, a1, 0, VSew::E##width);                                     \
        __ instr_name(reg1, reg2);                                            \
        width == 32 ? __ fsw(ft0, a0, 0) : __ fsd(ft0, a0, 0);                \
        __ vs(v1, a1, 0, VSew::E##width);                                     \
      };                                                                      \
      GenAndRunTest<int64_t, int64_t>((int64_t)&rs1_fval, (int64_t)res, fn);  \
      for (uint32_t i = 0; i < n; i++) {                                      \
        CHECK_EQ(i == 0 ? rs1_fval : res[i], res[i]);                         \
      }                                                                       \
    }                                                                         \
  }                                                                           \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name##_##width##_##sNaN) { \
    if (!CpuFeatures::IsSupported(RVV)) {                              \
      return;                                                                 \
    }                                                                         \
                                                                              \
    uint32_t n = CpuFeatures::vlen() / width;                                 \
    int##width##_t rs1_fval = width == 32 ? 0x7F400000 : 0x7FF4000000000000;  \
    int##width##_t res[kMaxElements] = {0};                                   \
    for (uint32_t i = 0; i < n; i++) res[i] = (rs1_fval + i + 1);             \
    auto fn = [](MacroAssembler& assm) {                                      \
      __ VU.set(t0, zero_reg, VSew::E##width, m1);                            \
      width == 32 ? __ flw(ft0, a0, 0) : __ fld(ft0, a0, 0);                  \
      __ vl(v1, a1, 0, VSew::E##width);                                       \
      __ instr_name(reg1, reg2);                                              \
      width == 32 ? __ fsw(ft0, a0, 0) : __ fsd(ft0, a0, 0);                  \
      __ vs(v1, a1, 0, VSew::E##width);                                       \
    };                                                                        \
    GenAndRunTest<int64_t, int64_t>((int64_t)&rs1_fval, (int64_t)res, fn);    \
    for (uint32_t i = 0; i < n; i++) {                                        \
      CHECK_EQ(i == 0 ? rs1_fval : res[i], res[i]);                           \
    }                                                                         \
  }

UTEST_RVV_VF_MV_FORM_WITH_RES(vfmv_fs, ft0, v1, 32, float)
UTEST_RVV_VF_MV_FORM_WITH_RES(vfmv_fs, ft0, v1, 64, double)
UTEST_RVV_VF_MV_FORM_WITH_RES(vfmv_sf, v1, ft0, 32, float)
UTEST_RVV_VF_MV_FORM_WITH_RES(vfmv_sf, v1, ft0, 64, double)
#undef UTEST_RVV_VF_MV_FORM_WITH_RES

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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name##_##width) {        \
    if (!CpuFeatures::IsSupported(RVV)) {                            \
      return;                                                               \
    }                                                                       \
                                                                            \
    int##width##_t result[kMaxElements] = {0};                              \
    auto fn = [&result](MacroAssembler& assm) {                             \
      __ VU.set(t0, zero_reg, VSew::E##width, m1);                          \
      __ vmv_vx(v0, a0);                                                    \
      __ vmv_vx(v1, a1);                                                    \
      __ instr_name(v0, v0, v1);                                            \
      __ li(t1, int64_t(result));                                           \
      __ vs(v0, t1, 0, VSew::E##width);                                     \
    };                                                                      \
    for (int##width##_t rs1_val : array) {                                  \
      for (int##width##_t rs2_val : array) {                                \
        GenAndRunTest<int32_t, int32_t>(rs1_val, rs2_val, fn);              \
        for (unsigned int i = 0; i < CpuFeatures::vlen() / width; i++)      \
          CHECK_EQ(static_cast<int##width##_t>(expect_res), result[i]);     \
      }                                                                     \
    }                                                                       \
  }

// 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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name##_##width) {        \
    if (!CpuFeatures::IsSupported(RVV)) {                            \
      return;                                                               \
    }                                                                       \
                                                                            \
    int##width##_t result[kMaxElements] = {0};                              \
    auto fn = [&result](MacroAssembler& assm) {                             \
      __ VU.set(t0, zero_reg, VSew::E##width, m1);                          \
      __ vmv_vx(v0, a0);                                                    \
      __ instr_name(v0, v0, a1);                                            \
      __ li(t1, int64_t(result));                                           \
      __ vs(v0, t1, 0, VSew::E##width);                                     \
    };                                                                      \
    for (int##width##_t rs1_val : array) {                                  \
      for (int##width##_t rs2_val : array) {                                \
        GenAndRunTest<int32_t, int32_t>(rs1_val, rs2_val, fn);              \
        for (unsigned int i = 0; i < CpuFeatures::vlen() / width; i++)      \
          CHECK_EQ(static_cast<int##width##_t>(expect_res), result[i]);     \
      }                                                                     \
    }                                                                       \
  }

// 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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name##_##width) {        \
    if (!CpuFeatures::IsSupported(RVV)) {                            \
      return;                                                               \
    }                                                                       \
                                                                            \
    int##width##_t result[kMaxElements] = {0};                              \
    for (int##width##_t rs1_val : array) {                                  \
      for (int##width##_t rs2_val : array) {                                \
        auto fn = [rs2_val, &result](MacroAssembler& assm) {                \
          __ VU.set(t0, zero_reg, VSew::E##width, m1);                      \
          __ vmv_vx(v0, a0);                                                \
          __ instr_name(v0, v0, ToImm5(rs2_val));                           \
          __ li(t1, int64_t(result));                                       \
          __ vs(v0, t1, 0, VSew::E##width);                                 \
        };                                                                  \
        GenAndRunTest<int32_t, int32_t>(rs1_val, fn);                       \
        for (unsigned int i = 0; i < CpuFeatures::vlen() / width; i++)      \
          CHECK_EQ(static_cast<int##width##_t>(expect_res), result[i]);     \
      }                                                                     \
    }                                                                       \
  }

#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, expect_op)               \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_FLOAT_##instr_name) {           \
    if (!CpuFeatures::IsSupported(RVV)) {                                  \
      return;                                                              \
    }                                                                      \
                                                                           \
    float result[kMaxElements] = {0.0};                                    \
    auto fn = [&result](MacroAssembler& assm) {                            \
      __ VU.set(t0, zero_reg, VSew::E32, m1);                              \
      __ vfmv_vf(v0, fa0);                                                 \
      __ vfmv_vf(v1, fa1);                                                 \
      __ instr_name(v0, v0, v1);                                           \
      __ vfmv_fs(fa0, v0);                                                 \
      __ li(a3, Operand(int64_t(result)));                                 \
      __ vs(v0, a3, 0, E32);                                               \
    };                                                                     \
    for (float rs1_fval : compiler::ValueHelper::GetVector<float>()) {     \
      for (float rs2_fval : compiler::ValueHelper::GetVector<float>()) {   \
        GenAndRunTest<float, float>(rs1_fval, rs2_fval, fn);               \
        for (unsigned i = 0; i < CpuFeatures::vlen() / 32; i++) {          \
          CHECK_FLOAT_EQ(                                                  \
              UseCanonicalNan<float>((rs1_fval)expect_op(rs2_fval)),       \
              result[i]);                                                  \
          result[i] = 0.0;                                                 \
        }                                                                  \
      }                                                                    \
    }                                                                      \
  }                                                                        \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_DOUBLE_##instr_name) {          \
    if (!CpuFeatures::IsSupported(RVV)) {                                  \
      return;                                                              \
    }                                                                      \
                                                                           \
    double result[kMaxElements] = {0.0};                                   \
    auto fn = [&result](MacroAssembler& assm) {                            \
      __ VU.set(t0, zero_reg, VSew::E64, m1);                              \
      __ vfmv_vf(v0, fa0);                                                 \
      __ vfmv_vf(v1, fa1);                                                 \
      __ instr_name(v0, v0, v1);                                           \
      __ vfmv_fs(fa0, v0);                                                 \
      __ li(a3, Operand(int64_t(result)));                                 \
      __ vs(v0, a3, 0, E64);                                               \
    };                                                                     \
    for (double rs1_fval : compiler::ValueHelper::GetVector<double>()) {   \
      for (double rs2_fval : compiler::ValueHelper::GetVector<double>()) { \
        GenAndRunTest<double, double>(rs1_fval, rs2_fval, fn);             \
        for (unsigned i = 0; i < CpuFeatures::vlen() / 64; i++) {          \
          CHECK_DOUBLE_EQ(                                                 \
              UseCanonicalNan<double>((rs1_fval)expect_op(rs2_fval)),      \
              result[i]);                                                  \
          result[i] = 0.0;                                                 \
        }                                                                  \
      }                                                                    \
    }                                                                      \
  }                                                                        \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_FP16_##instr_name) {            \
    if (!CpuFeatures::IsSupported(RVV)) {                                  \
      return;                                                              \
    }                                                                      \
                                                                           \
    uint16_t result[kMaxElements] = {0};                                   \
    auto fn = [&result](MacroAssembler& assm) {                            \
      __ VU.set(t0, zero_reg, VSew::E16, m1);                              \
      __ vmv_vx(v0, a0);                                                   \
      __ vmv_vx(v1, a1);                                                   \
      __ instr_name(v0, v0, v1);                                           \
      __ vmv_xs(a0, v0);                                                   \
      __ li(a3, Operand(int64_t(result)));                                 \
      __ vs(v0, a3, 0, E16);                                               \
    };                                                                     \
    for (float rs1_fval : compiler::ValueHelper::GetVector<float>()) {     \
      for (float rs2_fval : compiler::ValueHelper::GetVector<float>()) {   \
        Float16 rs1_f16 = Float16::FromFloat32(rs1_fval);                  \
        Float16 rs2_f16 = Float16::FromFloat32(rs2_fval);                  \
        GenAndRunTest<uint32_t, uint32_t>(rs1_f16.get_bits(),              \
                                          rs2_f16.get_bits(), fn);         \
        for (unsigned i = 0; i < CpuFeatures::vlen() / 16; i++) {          \
          auto res_f16 = Float16::FromBits(result[i]);                     \
          float expect = UseCanonicalNan<float>(                           \
              (rs1_f16.ToFloat32())expect_op(rs2_f16.ToFloat32()));        \
          CHECK_EQ(Float16::FromFloat32(expect).get_bits(),                \
                   res_f16.get_bits());                                    \
          result[i] = 0.0;                                                 \
        }                                                                  \
      }                                                                    \
    }                                                                      \
  }

// 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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {              \
    if (!CpuFeatures::IsSupported(RVV)) {                        \
      return;                                                           \
    }                                                                   \
                                                                        \
    auto fn = [](MacroAssembler& assm) {                                \
      __ VU.set(t0, zero_reg, VSew::E32, 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_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_RES(vfadd_vv, +)
UTEST_RVV_VF_VF_FORM_WITH_OP(vfadd_vf, ARRAY_FLOAT, +)
UTEST_RVV_VF_VV_FORM_WITH_RES(vfsub_vv, -)
// UTEST_RVV_VF_VF_FORM_WITH_OP(vfsub_vf, ARRAY_FLOAT, -)
UTEST_RVV_VF_VV_FORM_WITH_RES(vfmul_vv, *)
// UTEST_RVV_VF_VF_FORM_WITH_OP(vfmul_vf, ARRAY_FLOAT, *)
UTEST_RVV_VF_VV_FORM_WITH_RES(vfdiv_vv, /)
// 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

// Tests for vector widening floating-point arithmetic instructions between
// vector and vector
#define UTEST_RVV_VFW_VV_FORM_WITH_RES(instr_name, tested_op, is_first_double, \
                                       check_fn)                               \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_FLOAT_WIDENING_##instr_name) {      \
    if (!CpuFeatures::IsSupported(RVV)) {                               \
      return;                                                                  \
    }                                                                          \
                                                                               \
    size_t n = CpuFeatures::vlen() / 32;                                       \
    double result[kMaxElements] = {0.0};                                       \
    auto fn = [&result](MacroAssembler& assm) {                                \
      if (is_first_double) {                                                   \
        __ fcvt_d_s(fa0, fa0);                                                 \
        __ VU.set(t0, zero_reg, VSew::E64, m2);                                \
        __ vfmv_vf(v2, fa0);                                                   \
      }                                                                        \
      __ VU.set(t0, zero_reg, VSew::E32, m1);                                  \
      if (!is_first_double) {                                                  \
        __ vfmv_vf(v2, fa0);                                                   \
      }                                                                        \
      __ vfmv_vf(v4, fa1);                                                     \
      __ instr_name(v0, v2, v4);                                               \
      __ li(t1, Operand(int64_t(result)));                                     \
      __ vs(v0, t1, 0, VSew::E64);                                             \
    };                                                                         \
    for (float rs1_fval : compiler::ValueHelper::GetVector<float>()) {         \
      for (float rs2_fval : compiler::ValueHelper::GetVector<float>()) {       \
        GenAndRunTest<double, float>(rs1_fval, rs2_fval, fn);                  \
        for (size_t i = 0; i < n; i++) {                                       \
          CHECK_DOUBLE_EQ(                                                     \
              check_fn(rs1_fval, rs2_fval)                                     \
                  ? std::numeric_limits<double>::quiet_NaN()                   \
                  : UseCanonicalNan<double>(static_cast<double>(               \
                        rs1_fval) tested_op static_cast<double>(rs2_fval)),    \
              result[i]);                                                      \
          result[i] = 0.0;                                                     \
        }                                                                      \
      }                                                                        \
    }                                                                          \
  }

// Tests for vector widening floating-point arithmetic instructions between
// vector and scalar
#define UTEST_RVV_VFW_VF_FORM_WITH_RES(instr_name, tested_op, is_first_double, \
                                       check_fn)                               \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_FLOAT_WIDENING_##instr_name) {      \
    if (!CpuFeatures::IsSupported(RVV)) {                               \
      return;                                                                  \
    }                                                                          \
                                                                               \
    size_t n = CpuFeatures::vlen() / 32;                                       \
    double result[kMaxElements] = {0.0};                                       \
    auto fn = [&result](MacroAssembler& assm) {                                \
      __ VU.set(t0, zero_reg, VSew::E32, m1);                                  \
      if (is_first_double) {                                                   \
        __ fcvt_d_s(fa0, fa0);                                                 \
        __ VU.set(t0, zero_reg, VSew::E64, m2);                                \
        __ vfmv_vf(v2, fa0);                                                   \
      }                                                                        \
      __ VU.set(t0, zero_reg, VSew::E32, m1);                                  \
      if (!is_first_double) {                                                  \
        __ vfmv_vf(v2, fa0);                                                   \
      }                                                                        \
      __ instr_name(v0, v2, fa1);                                              \
      __ li(t1, Operand(int64_t(result)));                                     \
      __ vs(v0, t1, 0, VSew::E64);                                             \
    };                                                                         \
    for (float rs1_fval : compiler::ValueHelper::GetVector<float>()) {         \
      for (float rs2_fval : compiler::ValueHelper::GetVector<float>()) {       \
        GenAndRunTest<double, float>(rs1_fval, rs2_fval, fn);                  \
        for (size_t i = 0; i < n; i++) {                                       \
          CHECK_DOUBLE_EQ(                                                     \
              check_fn(rs1_fval, rs2_fval)                                     \
                  ? std::numeric_limits<double>::quiet_NaN()                   \
                  : UseCanonicalNan<double>(static_cast<double>(               \
                        rs1_fval) tested_op static_cast<double>(rs2_fval)),    \
              result[i]);                                                      \
          result[i] = 0.0;                                                     \
        }                                                                      \
      }                                                                        \
    }                                                                          \
  }

#define UTEST_RVV_VFW_VV_FORM_WITH_OP(instr_name, tested_op, is_first_double, \
                                      check_fn)                               \
  UTEST_RVV_VFW_VV_FORM_WITH_RES(instr_name, tested_op, is_first_double,      \
                                 check_fn)
#define UTEST_RVV_VFW_VF_FORM_WITH_OP(instr_name, tested_op, is_first_double, \
                                      check_fn)                               \
  UTEST_RVV_VFW_VF_FORM_WITH_RES(instr_name, tested_op, is_first_double,      \
                                 check_fn)

template <typename T>
static inline bool is_invalid_fmul(T src1, T src2) {
  return (isinf(src1) && src2 == static_cast<T>(0.0)) ||
         (src1 == static_cast<T>(0.0) && isinf(src2));
}

template <typename T>
static inline bool is_invalid_fadd(T src1, T src2) {
  return (isinf(src1) && isinf(src2) &&
          std::signbit(src1) != std::signbit(src2));
}

template <typename T>
static inline bool is_invalid_fsub(T src1, T src2) {
  return (isinf(src1) && isinf(src2) &&
          std::signbit(src1) == std::signbit(src2));
}

UTEST_RVV_VFW_VV_FORM_WITH_OP(vfwadd_vv, +, false, is_invalid_fadd)
UTEST_RVV_VFW_VF_FORM_WITH_OP(vfwadd_vf, +, false, is_invalid_fadd)
UTEST_RVV_VFW_VV_FORM_WITH_OP(vfwsub_vv, -, false, is_invalid_fsub)
UTEST_RVV_VFW_VF_FORM_WITH_OP(vfwsub_vf, -, false, is_invalid_fsub)
UTEST_RVV_VFW_VV_FORM_WITH_OP(vfwadd_wv, +, true, is_invalid_fadd)
UTEST_RVV_VFW_VF_FORM_WITH_OP(vfwadd_wf, +, true, is_invalid_fadd)
UTEST_RVV_VFW_VV_FORM_WITH_OP(vfwsub_wv, -, true, is_invalid_fsub)
UTEST_RVV_VFW_VF_FORM_WITH_OP(vfwsub_wf, -, true, is_invalid_fsub)
UTEST_RVV_VFW_VV_FORM_WITH_OP(vfwmul_vv, *, false, is_invalid_fmul)
UTEST_RVV_VFW_VF_FORM_WITH_OP(vfwmul_vf, *, false, is_invalid_fmul)

#undef UTEST_RVV_VF_VV_FORM_WITH_OP
#undef UTEST_RVV_VF_VF_FORM_WITH_OP

// Tests for vector widening floating-point fused multiply-add Instructions
// between vectors
#define UTEST_RVV_VFW_FMA_VV_FORM_WITH_RES(instr_name, array, expect_res)     \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_FLOAT_WIDENING_##instr_name) {     \
    if (!CpuFeatures::IsSupported(RVV)) {                              \
      return;                                                                 \
    }                                                                         \
                                                                              \
    auto fn = [](MacroAssembler& assm) {                                      \
      __ VU.set(t0, zero_reg, VSew::E32, m1);                                 \
      __ vfmv_vf(v0, fa0);                                                    \
      __ vfmv_vf(v2, fa1);                                                    \
      __ vfmv_vf(v4, fa2);                                                    \
      __ instr_name(v0, v2, v4);                                              \
      __ VU.set(t0, zero_reg, VSew::E64, m1);                                 \
      __ vfmv_fs(fa0, v0);                                                    \
    };                                                                        \
    for (float rs1_fval : array) {                                            \
      for (float rs2_fval : array) {                                          \
        for (float rs3_fval : array) {                                        \
          double rs1_dval = base::bit_cast<double>(                           \
              (uint64_t)base::bit_cast<uint32_t>(rs1_fval) << 32 |            \
              base::bit_cast<uint32_t>(rs1_fval));                            \
          double rs2_dval = static_cast<double>(rs2_fval);                    \
          double rs3_dval = static_cast<double>(rs3_fval);                    \
          double res =                                                        \
              GenAndRunTest<double, float>(rs1_fval, rs2_fval, rs3_fval, fn); \
          CHECK_DOUBLE_EQ((expect_res), res);                                 \
        }                                                                     \
      }                                                                       \
    }                                                                         \
  }

// Tests for vector single-width floating-point fused multiply-add Instructions
// between vectors and scalar
#define UTEST_RVV_VFW_FMA_VF_FORM_WITH_RES(instr_name, array, expect_res)     \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_FLOAT_WIDENING_##instr_name) {     \
    if (!CpuFeatures::IsSupported(RVV)) {                              \
      return;                                                                 \
    }                                                                         \
                                                                              \
    auto fn = [](MacroAssembler& assm) {                                      \
      __ VU.set(t0, zero_reg, VSew::E32, m1);                                 \
      __ vfmv_vf(v0, fa0);                                                    \
      __ vfmv_vf(v2, fa2);                                                    \
      __ instr_name(v0, fa1, v2);                                             \
      __ VU.set(t0, zero_reg, VSew::E64, m1);                                 \
      __ vfmv_fs(fa0, v0);                                                    \
    };                                                                        \
    for (float rs1_fval : array) {                                            \
      for (float rs2_fval : array) {                                          \
        for (float rs3_fval : array) {                                        \
          double rs1_dval = base::bit_cast<double>(                           \
              (uint64_t)base::bit_cast<uint32_t>(rs1_fval) << 32 |            \
              base::bit_cast<uint32_t>(rs1_fval));                            \
          double rs2_dval = static_cast<double>(rs2_fval);                    \
          double rs3_dval = static_cast<double>(rs3_fval);                    \
          double res =                                                        \
              GenAndRunTest<double, float>(rs1_fval, rs2_fval, rs3_fval, fn); \
          CHECK_DOUBLE_EQ((expect_res), res);                                 \
        }                                                                     \
      }                                                                       \
    }                                                                         \
  }

#define ARRAY_FLOAT compiler::ValueHelper::GetVector<float>()
UTEST_RVV_VFW_FMA_VV_FORM_WITH_RES(vfwmacc_vv, ARRAY_FLOAT,
                                   std::fma(rs2_dval, rs3_dval, rs1_dval))
UTEST_RVV_VFW_FMA_VF_FORM_WITH_RES(vfwmacc_vf, ARRAY_FLOAT,
                                   std::fma(rs2_dval, rs3_dval, rs1_dval))
UTEST_RVV_VFW_FMA_VV_FORM_WITH_RES(vfwnmacc_vv, ARRAY_FLOAT,
                                   std::fma(rs2_dval, -rs3_dval, -rs1_dval))
UTEST_RVV_VFW_FMA_VF_FORM_WITH_RES(vfwnmacc_vf, ARRAY_FLOAT,
                                   std::fma(rs2_dval, -rs3_dval, -rs1_dval))
UTEST_RVV_VFW_FMA_VV_FORM_WITH_RES(vfwmsac_vv, ARRAY_FLOAT,
                                   std::fma(rs2_dval, rs3_dval, -rs1_dval))
UTEST_RVV_VFW_FMA_VF_FORM_WITH_RES(vfwmsac_vf, ARRAY_FLOAT,
                                   std::fma(rs2_dval, rs3_dval, -rs1_dval))
UTEST_RVV_VFW_FMA_VV_FORM_WITH_RES(vfwnmsac_vv, ARRAY_FLOAT,
                                   std::fma(rs2_dval, -rs3_dval, rs1_dval))
UTEST_RVV_VFW_FMA_VF_FORM_WITH_RES(vfwnmsac_vf, ARRAY_FLOAT,
                                   std::fma(rs2_dval, -rs3_dval, rs1_dval))

#undef ARRAY_FLOAT
#undef UTEST_RVV_VFW_FMA_VV_FORM_WITH_RES
#undef UTEST_RVV_VFW_FMA_VF_FORM_WITH_RES

// 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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {                   \
    if (!CpuFeatures::IsSupported(RVV)) {                             \
      return;                                                                \
    }                                                                        \
                                                                             \
    auto fn = [](MacroAssembler& assm) {                                     \
      __ VU.set(t0, zero_reg, VSew::E32, 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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name) {                   \
    if (!CpuFeatures::IsSupported(RVV)) {                             \
      return;                                                                \
    }                                                                        \
                                                                             \
    auto fn = [](MacroAssembler& assm) {                                     \
      __ VU.set(t0, zero_reg, VSew::E32, 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_WITH_RES
#undef UTEST_RVV_FMA_VF_FORM_WITH_RES

// Tests for vector Widening Floating-Point Reduction Instructions
#define UTEST_RVV_VFW_REDSUM_VV_FORM_WITH_RES(instr_name)                 \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_FLOAT_WIDENING_##instr_name) { \
    if (!CpuFeatures::IsSupported(RVV)) {                          \
      return;                                                             \
    }                                                                     \
                                                                          \
    auto fn = [](MacroAssembler& assm) {                                  \
      __ VU.set(t0, zero_reg, VSew::E32, m1);                             \
      __ vfmv_vf(v2, fa0);                                                \
      __ vfmv_vf(v4, fa0);                                                \
      __ instr_name(v0, v2, v4);                                          \
      __ VU.set(t0, zero_reg, VSew::E64, m1);                             \
      __ vfmv_fs(fa0, v0);                                                \
    };                                                                    \
    for (float rs1_fval : compiler::ValueHelper::GetVector<float>()) {    \
      std::vector<double> temp_arr(CpuFeatures::vlen() / 32,              \
                                   static_cast<double>(rs1_fval));        \
      double expect_res = base::bit_cast<double>(                         \
          (uint64_t)base::bit_cast<uint32_t>(rs1_fval) << 32 |            \
          base::bit_cast<uint32_t>(rs1_fval));                            \
      for (double val : temp_arr) {                                       \
        if (is_invalid_fadd(expect_res, val)) {                           \
          expect_res = std::numeric_limits<float>::quiet_NaN();           \
          break;                                                          \
        }                                                                 \
        expect_res += val;                                                \
        if (std::isnan(expect_res)) {                                     \
          expect_res = std::numeric_limits<double>::quiet_NaN();          \
          break;                                                          \
        }                                                                 \
      }                                                                   \
      double res = GenAndRunTest<double, float>(rs1_fval, fn);            \
      CHECK_DOUBLE_EQ(UseCanonicalNan<double>(expect_res), res);          \
    }                                                                     \
  }

UTEST_RVV_VFW_REDSUM_VV_FORM_WITH_RES(vfwredusum_vs)
UTEST_RVV_VFW_REDSUM_VV_FORM_WITH_RES(vfwredosum_vs)

#undef UTEST_RVV_VFW_REDSUM_VV_FORM_WITH_RES
// 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_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name##_E32M2_E16M1) {     \
    if (!CpuFeatures::IsSupported(RVV)) {                             \
      return;                                                                \
    }                                                                        \
    constexpr FPURoundingMode vxrm = RNE;                                    \
                                                                             \
    Isolate* isolate = 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);                                                   \
          __ VU.set(t0, zero_reg, VSew::E32, m2);                            \
          __ vl(v2, a0, 0, VSew::E32);                                       \
          __ VU.set(t0, zero_reg, VSew::E16, m1);                            \
          __ instr_name(v4, v2, shift);                                      \
          __ vs(v4, a1, 0, VSew::E16);                                       \
        };                                                                   \
        struct T {                                                           \
          sign##int32_t src[kMaxElements] = {0};                             \
          sign##int16_t dst[kMaxElements] = {0};                             \
          sign##int16_t ref[kMaxElements] = {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, CpuFeatures::vlen() / 8));               \
      }                                                                      \
    }                                                                        \
  }

UTEST_RVV_VNCLIP_E32M2_E16M1(vnclipu_vi, u)
UTEST_RVV_VNCLIP_E32M2_E16M1(vnclip_vi, )

#undef UTEST_RVV_VNCLIP_E32M2_E16M1

// Tests for vector integer extension instructions
#define UTEST_RVV_VI_VIE_FORM_WITH_RES(instr_name, type, width, frac_width, \
                                       array, expect_res)                   \
  TEST_F(AssemblerRISCV64Test,                                              \
         RISCV_UTEST_##instr_name##_##width##_##frac_width) {               \
    if (!CpuFeatures::IsSupported(RVV)) {                            \
      return;                                                               \
    }                                                                       \
    uint32_t n = CpuFeatures::vlen() / frac_width;                          \
                                                                            \
    for (int##frac_width##_t x : array) {                                   \
      int##frac_width##_t src[kMaxElements] = {0};                          \
      type dst[kMaxElements] = {0};                                         \
      for (uint32_t i = 0; i < n; i++) src[i] = x;                          \
      auto fn = [](MacroAssembler& assm) {                                  \
        __ VU.set(t0, zero_reg, VSew::E##frac_width, m1);                   \
        __ vl(v1, a0, 0, VSew::E##frac_width);                              \
        __ VU.set(t0, zero_reg, VSew::E##width, m1);                        \
        __ instr_name(v2, v1);                                              \
        __ vs(v2, a1, 0, VSew::E##width);                                   \
      };                                                                    \
      GenAndRunTest<int64_t, int64_t>((int64_t)src, (int64_t)dst, fn);      \
      for (uint32_t i = 0; i < n; i++) {                                    \
        CHECK_EQ(expect_res, dst[i]);                                       \
      }                                                                     \
    }                                                                       \
  }

#define ARRAY(type) compiler::ValueHelper::GetVector<type>()

UTEST_RVV_VI_VIE_FORM_WITH_RES(vzext_vf2, uint64_t, 64, 32, ARRAY(int32_t),
                               static_cast<uint64_t>(dst[i]))
UTEST_RVV_VI_VIE_FORM_WITH_RES(vzext_vf4, uint64_t, 64, 16, ARRAY(int16_t),
                               static_cast<uint64_t>(dst[i]))
UTEST_RVV_VI_VIE_FORM_WITH_RES(vzext_vf8, uint64_t, 64, 8, ARRAY(int8_t),
                               static_cast<uint64_t>(dst[i]))
UTEST_RVV_VI_VIE_FORM_WITH_RES(vzext_vf2, uint32_t, 32, 16, ARRAY(int16_t),
                               static_cast<uint32_t>(dst[i]))
UTEST_RVV_VI_VIE_FORM_WITH_RES(vzext_vf4, uint32_t, 32, 8, ARRAY(int8_t),
                               static_cast<uint32_t>(dst[i]))
UTEST_RVV_VI_VIE_FORM_WITH_RES(vzext_vf2, uint16_t, 16, 8, ARRAY(int8_t),
                               static_cast<uint16_t>(dst[i]))

UTEST_RVV_VI_VIE_FORM_WITH_RES(vsext_vf2, int64_t, 64, 32, ARRAY(int32_t),
                               static_cast<int64_t>(dst[i]))
UTEST_RVV_VI_VIE_FORM_WITH_RES(vsext_vf4, int64_t, 64, 16, ARRAY(int16_t),
                               static_cast<int64_t>(dst[i]))
UTEST_RVV_VI_VIE_FORM_WITH_RES(vsext_vf8, int64_t, 64, 8, ARRAY(int8_t),
                               static_cast<int64_t>(dst[i]))
UTEST_RVV_VI_VIE_FORM_WITH_RES(vsext_vf2, int32_t, 32, 16, ARRAY(int16_t),
                               static_cast<int32_t>(dst[i]))
UTEST_RVV_VI_VIE_FORM_WITH_RES(vsext_vf4, int32_t, 32, 8, ARRAY(int8_t),
                               static_cast<int32_t>(dst[i]))
UTEST_RVV_VI_VIE_FORM_WITH_RES(vsext_vf2, int16_t, 16, 8, ARRAY(int8_t),
                               static_cast<int16_t>(dst[i]))

#undef UTEST_RVV_VI_VIE_FORM_WITH_RES

static constexpr double double_sNaN[] = {
    std::numeric_limits<double>::signaling_NaN(),
    -std::numeric_limits<double>::signaling_NaN()};
static constexpr float float_sNaN[] = {
    std::numeric_limits<float>::signaling_NaN(),
    -std::numeric_limits<float>::signaling_NaN()};
// Tests for vector Floating-Point merge instruction
#define UTEST_RVV_VF_VFMERGE_VF_FORM_WITH_RES(                                 \
    number /*prevent redefinition*/, type, int_type, width, array, expect_res) \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_vfmerge_vf_##type##_##number) {     \
    if (!CpuFeatures::IsSupported(RVV)) {                               \
      return;                                                                  \
    }                                                                          \
    uint32_t n = CpuFeatures::vlen() / width;                                  \
                                                                               \
    for (type fval : array) {                                                  \
      int_type rs1_fval = base::bit_cast<int_type>(fval);                      \
      for (uint32_t mask = 0; mask < (1 << n); mask++) {                       \
        int_type src[kMaxElements] = {0};                                      \
        int_type dst[kMaxElements] = {0};                                      \
        dst[0] = rs1_fval;                                                     \
        for (uint32_t i = 0; i < n; i++) src[i] = i;                           \
        auto fn = [mask](MacroAssembler& assm) {                               \
          __ VU.set(t0, zero_reg, VSew::E##width, m1);                         \
          __ vl(v1, a0, 0, VSew::E##width);                                    \
          __ vl(v24, a1, 0, VSew::E##width);                                   \
          __ li(t0, mask);                                                     \
          __ vmv_vx(v0, t0);                                                   \
          __ vfmv_fs(ft0, v24);                                                \
          __ vfmerge_vf(v2, ft0, v1);                                          \
          __ vs(v2, a1, 0, VSew::E##width);                                    \
        };                                                                     \
        GenAndRunTest<int64_t, int64_t>((int64_t)src, (int64_t)dst, fn);       \
        for (uint32_t i = 0; i < n; i++) {                                     \
          CHECK_EQ(expect_res, dst[i]);                                        \
        }                                                                      \
      }                                                                        \
    }                                                                          \
  }

UTEST_RVV_VF_VFMERGE_VF_FORM_WITH_RES(
    1, double, int64_t, 64, compiler::ValueHelper::GetVector<double>(),
    ((mask >> i) & 0x1) ? rs1_fval : src[i])
UTEST_RVV_VF_VFMERGE_VF_FORM_WITH_RES(2, float, int32_t, 32,
                                      compiler::ValueHelper::GetVector<float>(),
                                      ((mask >> i) & 0x1) ? rs1_fval : src[i])
UTEST_RVV_VF_VFMERGE_VF_FORM_WITH_RES(3, double, int64_t, 64,
                                      base::ArrayVector(double_sNaN),
                                      ((mask >> i) & 0x1) ? rs1_fval : src[i])
UTEST_RVV_VF_VFMERGE_VF_FORM_WITH_RES(4, float, int32_t, 32,
                                      base::ArrayVector(float_sNaN),
                                      ((mask >> i) & 0x1) ? rs1_fval : src[i])
#undef UTEST_RVV_VF_VFMERGE_VF_FORM_WITH_RES

// Test for vslidedown_vi
#define UTEST_RVV_VP_VSLIDEDOWN_VI_FORM_WITH_RES(type, width, array, offset) \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_vslidedown_vi_##type) {           \
    if (!CpuFeatures::IsSupported(RVV)) {                             \
      return;                                                                \
    }                                                                        \
    uint32_t n = CpuFeatures::vlen() / width;                                \
    uint32_t max_offset = n > 0b11111 ? 0b11111 : n;                         \
                                                                             \
    for (type x : array) {                                                   \
      for (uint32_t offset = 0; offset <= max_offset; offset++) {            \
        type src[kMaxElements] = {0};                                        \
        type dst[kMaxElements] = {0};                                        \
        for (uint32_t i = 0; i < n; i++) src[i] = x + i;                     \
        auto fn = [offset](MacroAssembler& assm) {                           \
          __ VU.set(t0, zero_reg, VSew::E##width, m1);                       \
          __ vl(v1, a0, 0, VSew::E##width);                                  \
          __ vslidedown_vi(v2, v1, offset);                                  \
          __ vs(v2, a1, 0, VSew::E##width);                                  \
        };                                                                   \
        GenAndRunTest<int64_t, int64_t>((int64_t)src, (int64_t)dst, fn);     \
        for (uint32_t i = 0; i < n; i++) {                                   \
          CHECK_EQ(i < n - offset ? src[i + offset] : 0, dst[i]);            \
        }                                                                    \
      }                                                                      \
    }                                                                        \
  }

UTEST_RVV_VP_VSLIDEDOWN_VI_FORM_WITH_RES(int64_t, 64, ARRAY(int64_t), offset)
UTEST_RVV_VP_VSLIDEDOWN_VI_FORM_WITH_RES(int32_t, 32, ARRAY(int32_t), offset)
UTEST_RVV_VP_VSLIDEDOWN_VI_FORM_WITH_RES(int16_t, 16, ARRAY(int16_t), offset)
UTEST_RVV_VP_VSLIDEDOWN_VI_FORM_WITH_RES(int8_t, 8, ARRAY(int8_t), offset)

UTEST_RVV_VP_VSLIDEDOWN_VI_FORM_WITH_RES(uint32_t, 32, ARRAY(uint32_t), offset)
UTEST_RVV_VP_VSLIDEDOWN_VI_FORM_WITH_RES(uint16_t, 16, ARRAY(uint16_t), offset)
UTEST_RVV_VP_VSLIDEDOWN_VI_FORM_WITH_RES(uint8_t, 8, ARRAY(uint8_t), offset)
#undef UTEST_RVV_VP_VSLIDEDOWN_VI_FORM_WITH_RES

// Test for vslideup_vi
#define UTEST_RVV_VP_VSLIDEUP_VI_FORM_WITH_RES(type, width, array, offset) \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_vslideup_vi_##type) {           \
    if (!CpuFeatures::IsSupported(RVV)) {                           \
      return;                                                              \
    }                                                                      \
    uint32_t n = CpuFeatures::vlen() / width;                              \
    uint32_t max_offset = n > 0b11111 ? 0b11111 : n;                       \
                                                                           \
    for (type x : array) {                                                 \
      for (uint32_t offset = 0; offset <= max_offset; offset++) {          \
        type src[kMaxElements] = {0};                                      \
        type dst[kMaxElements] = {0};                                      \
        for (uint32_t i = 0; i < n; i++) src[i] = x + i;                   \
        auto fn = [offset](MacroAssembler& assm) {                         \
          __ VU.set(t0, zero_reg, VSew::E##width, m1);                     \
          __ vl(v1, a0, 0, VSew::E##width);                                \
          __ vslideup_vi(v2, v1, offset);                                  \
          __ vs(v2, a1, 0, VSew::E##width);                                \
        };                                                                 \
        GenAndRunTest<int64_t, int64_t>((int64_t)src, (int64_t)dst, fn);   \
        for (uint32_t i = 0; i < n; i++) {                                 \
          CHECK_EQ(i < offset ? dst[i] : src[i - offset], dst[i]);         \
        }                                                                  \
      }                                                                    \
    }                                                                      \
  }

UTEST_RVV_VP_VSLIDEUP_VI_FORM_WITH_RES(int64_t, 64, ARRAY(int64_t), offset)
UTEST_RVV_VP_VSLIDEUP_VI_FORM_WITH_RES(int32_t, 32, ARRAY(int32_t), offset)
UTEST_RVV_VP_VSLIDEUP_VI_FORM_WITH_RES(int16_t, 16, ARRAY(int16_t), offset)
UTEST_RVV_VP_VSLIDEUP_VI_FORM_WITH_RES(int8_t, 8, ARRAY(int8_t), offset)

UTEST_RVV_VP_VSLIDEUP_VI_FORM_WITH_RES(uint32_t, 32, ARRAY(uint32_t), offset)
UTEST_RVV_VP_VSLIDEUP_VI_FORM_WITH_RES(uint16_t, 16, ARRAY(uint16_t), offset)
UTEST_RVV_VP_VSLIDEUP_VI_FORM_WITH_RES(uint8_t, 8, ARRAY(uint8_t), offset)
#undef UTEST_RVV_VP_VSLIDEUP_VI_FORM_WITH_RES

// Test for vslidedown_vx
#define UTEST_RVV_VP_VSLIDEDOWN_VX_FORM_WITH_RES(type, width, array)         \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_vslidedown_vx_##type) {           \
    if (!CpuFeatures::IsSupported(RVV)) {                             \
      return;                                                                \
    }                                                                        \
    uint32_t n = CpuFeatures::vlen() / width;                                \
                                                                             \
    for (type x : array) {                                                   \
      for (uint32_t offset = 0; offset <= n; offset++) {                     \
        type src[kMaxElements] = {0};                                        \
        type dst[kMaxElements] = {0};                                        \
        for (uint32_t i = 0; i < n; i++) src[i] = x + i;                     \
        auto fn = [](MacroAssembler& assm) {                                 \
          __ VU.set(t0, zero_reg, VSew::E##width, m1);                       \
          __ vl(v1, a0, 0, VSew::E##width);                                  \
          __ vslidedown_vx(v2, v1, a2);                                      \
          __ vs(v2, a1, 0, VSew::E##width);                                  \
        };                                                                   \
        type rs2_val = (type)offset;                                         \
        GenAndRunTest<int64_t, int64_t>((int64_t)src, (int64_t)dst, rs2_val, \
                                        fn);                                 \
        for (uint32_t i = 0; i < n; i++) {                                   \
          CHECK_EQ(i < n - rs2_val ? src[i + rs2_val] : 0, dst[i]);          \
        }                                                                    \
      }                                                                      \
    }                                                                        \
  }

UTEST_RVV_VP_VSLIDEDOWN_VX_FORM_WITH_RES(int64_t, 64, ARRAY(int64_t))
UTEST_RVV_VP_VSLIDEDOWN_VX_FORM_WITH_RES(int32_t, 32, ARRAY(int32_t))
UTEST_RVV_VP_VSLIDEDOWN_VX_FORM_WITH_RES(int16_t, 16, ARRAY(int16_t))
UTEST_RVV_VP_VSLIDEDOWN_VX_FORM_WITH_RES(int8_t, 8, ARRAY(int8_t))

UTEST_RVV_VP_VSLIDEDOWN_VX_FORM_WITH_RES(uint32_t, 32, ARRAY(uint32_t))
UTEST_RVV_VP_VSLIDEDOWN_VX_FORM_WITH_RES(uint16_t, 16, ARRAY(uint16_t))
UTEST_RVV_VP_VSLIDEDOWN_VX_FORM_WITH_RES(uint8_t, 8, ARRAY(uint8_t))

// Test for vslideup_vx
#define UTEST_RVV_VP_VSLIDEUP_VX_FORM_WITH_RES(type, width, array)           \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_vslideup_vx_##type) {             \
    if (!CpuFeatures::IsSupported(RVV)) {                             \
      return;                                                                \
    }                                                                        \
    uint32_t n = CpuFeatures::vlen() / width;                                \
                                                                             \
    for (type x : array) {                                                   \
      for (uint32_t offset = 0; offset <= n; offset++) {                     \
        type src[kMaxElements] = {0};                                        \
        type dst[kMaxElements] = {0};                                        \
        for (uint32_t i = 0; i < n; i++) src[i] = x + i;                     \
        auto fn = [](MacroAssembler& assm) {                                 \
          __ VU.set(t0, zero_reg, VSew::E##width, m1);                       \
          __ vl(v1, a0, 0, VSew::E##width);                                  \
          __ vslideup_vx(v2, v1, a2);                                        \
          __ vs(v2, a1, 0, VSew::E##width);                                  \
        };                                                                   \
        type rs2_val = (type)offset;                                         \
        GenAndRunTest<int64_t, int64_t>((int64_t)src, (int64_t)dst, rs2_val, \
                                        fn);                                 \
        for (uint32_t i = 0; i < n; i++) {                                   \
          CHECK_EQ((type)i < rs2_val ? dst[i] : src[i - rs2_val], dst[i]);   \
        }                                                                    \
      }                                                                      \
    }                                                                        \
  }
UTEST_RVV_VP_VSLIDEUP_VX_FORM_WITH_RES(int64_t, 64, ARRAY(int64_t))
UTEST_RVV_VP_VSLIDEUP_VX_FORM_WITH_RES(int32_t, 32, ARRAY(int32_t))
UTEST_RVV_VP_VSLIDEUP_VX_FORM_WITH_RES(int16_t, 16, ARRAY(int16_t))
UTEST_RVV_VP_VSLIDEUP_VX_FORM_WITH_RES(int8_t, 8, ARRAY(int8_t))

UTEST_RVV_VP_VSLIDEUP_VX_FORM_WITH_RES(uint32_t, 32, ARRAY(uint32_t))
UTEST_RVV_VP_VSLIDEUP_VX_FORM_WITH_RES(uint16_t, 16, ARRAY(uint16_t))
UTEST_RVV_VP_VSLIDEUP_VX_FORM_WITH_RES(uint8_t, 8, ARRAY(uint8_t))
#undef UTEST_RVV_VP_VSLIDEUP_VX_FORM_WITH_RES

#define UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(instr_name, type, width, array, \
                                              expect_res)                     \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name##_##type) {           \
    if (!CpuFeatures::IsSupported(RVV)) {                              \
      return;                                                                 \
    }                                                                         \
    uint32_t n = CpuFeatures::vlen() / width;                                 \
                                                                              \
    for (type x : array) {                                                    \
      type src[kMaxElements] = {0};                                           \
      type dst[kMaxElements] = {0};                                           \
      for (uint32_t i = 0; i < n; i++) src[i] = x + i;                        \
      auto fn = [](MacroAssembler& assm) {                                    \
        __ VU.set(t0, zero_reg, VSew::E##width, m1);                          \
        __ vl(v1, a0, 0, VSew::E##width);                                     \
        __ instr_name(v2, v1, a2);                                            \
        __ vs(v2, a1, 0, VSew::E##width);                                     \
      };                                                                      \
      type rs2_val = x + x;                                                   \
      GenAndRunTest<int64_t, int64_t>((int64_t)src, (int64_t)dst, rs2_val,    \
                                      fn);                                    \
      for (uint32_t i = 0; i < n; i++) {                                      \
        CHECK_EQ(expect_res, dst[i]);                                         \
      }                                                                       \
    }                                                                         \
  }

// Test for vslide1down_vx
UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1down_vx, int64_t, 64,
                                      ARRAY(int64_t),
                                      (i + 1) < n ? src[i + 1] : rs2_val)
UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1down_vx, int32_t, 32,
                                      ARRAY(int32_t),
                                      (i + 1) < n ? src[i + 1] : rs2_val)
UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1down_vx, int16_t, 16,
                                      ARRAY(int16_t),
                                      (i + 1) < n ? src[i + 1] : rs2_val)
UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1down_vx, int8_t, 8, ARRAY(int8_t),
                                      (i + 1) < n ? src[i + 1] : rs2_val)

UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1down_vx, uint32_t, 32,
                                      ARRAY(uint32_t),
                                      (i + 1) < n ? src[i + 1] : rs2_val)
UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1down_vx, uint16_t, 16,
                                      ARRAY(uint16_t),
                                      (i + 1) < n ? src[i + 1] : rs2_val)
UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1down_vx, uint8_t, 8,
                                      ARRAY(uint8_t),
                                      (i + 1) < n ? src[i + 1] : rs2_val)

// Test for vslide1up_vx
UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1up_vx, int64_t, 64, ARRAY(int64_t),
                                      (int64_t)i < 1 ? rs2_val : src[i - 1])
UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1up_vx, int32_t, 32, ARRAY(int32_t),
                                      (int32_t)i < 1 ? rs2_val : src[i - 1])
UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1up_vx, int16_t, 16, ARRAY(int16_t),
                                      (int16_t)i < 1 ? rs2_val : src[i - 1])
UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1up_vx, int8_t, 8, ARRAY(int8_t),
                                      (int8_t)i < 1 ? rs2_val : src[i - 1])

UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1up_vx, uint32_t, 32,
                                      ARRAY(uint32_t),
                                      (uint32_t)i < 1 ? rs2_val : src[i - 1])
UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1up_vx, uint16_t, 16,
                                      ARRAY(uint16_t),
                                      (uint16_t)i < 1 ? rs2_val : src[i - 1])
UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES(vslide1up_vx, uint8_t, 8, ARRAY(uint8_t),
                                      (uint8_t)i < 1 ? rs2_val : src[i - 1])
#undef UTEST_RVV_VP_VSLIDE1_VX_FORM_WITH_RES

#define UTEST_RVV_VP_VSLIDE1_VF_FORM_WITH_RES(instr_name, type, width, fval,  \
                                              array, expect_res)              \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_##instr_name##_##width##_##fval) { \
    if (!CpuFeatures::IsSupported(RVV)) {                              \
      return;                                                                 \
    }                                                                         \
    uint32_t n = CpuFeatures::vlen() / width;                                 \
                                                                              \
    for (type x : array) {                                                    \
      type src[kMaxElements] = {0};                                           \
      type dst[kMaxElements] = {0};                                           \
      src[0] = base::bit_cast<type>(fval);                                    \
      for (uint32_t i = 1; i < n; i++) src[i] = x + i;                        \
      auto fn = [](MacroAssembler& assm) {                                    \
        __ VU.set(t0, zero_reg, VSew::E##width, m1);                          \
        __ vl(v1, a0, 0, VSew::E##width);                                     \
        width == 32 ? __ flw(fa0, a0, 0) : __ fld(fa0, a0, 0);                \
        __ instr_name(v2, v1, fa0);                                           \
        __ vs(v2, a1, 0, VSew::E##width);                                     \
      };                                                                      \
      GenAndRunTest<int64_t, int64_t>((int64_t)src, (int64_t)dst, fn);        \
      for (uint32_t i = 0; i < n; i++) {                                      \
        CHECK_EQ(expect_res, dst[i]);                                         \
      }                                                                       \
    }                                                                         \
  }

// Test for vfslide1down_vf
UTEST_RVV_VP_VSLIDE1_VF_FORM_WITH_RES(vfslide1down_vf, int64_t, 64,
                                      0x40934A3D70A3D70A /*1234.56*/,
                                      ARRAY(int64_t),
                                      (i + 1) < n ? src[i + 1] : src[0])
UTEST_RVV_VP_VSLIDE1_VF_FORM_WITH_RES(vfslide1down_vf, int32_t, 32,
                                      0x449A51EC /*1234.56f*/, ARRAY(int32_t),
                                      (i + 1) < n ? src[i + 1] : src[0])
// Test for vfslide1down_vf_signaling_NaN
UTEST_RVV_VP_VSLIDE1_VF_FORM_WITH_RES(vfslide1down_vf, int64_t, 64,
                                      0x7FF4000000000000, ARRAY(int64_t),
                                      (i + 1) < n ? src[i + 1] : src[0])
UTEST_RVV_VP_VSLIDE1_VF_FORM_WITH_RES(vfslide1down_vf, int32_t, 32, 0x7F400000,
                                      ARRAY(int32_t),
                                      (i + 1) < n ? src[i + 1] : src[0])
// Test for vfslide1up_vf
UTEST_RVV_VP_VSLIDE1_VF_FORM_WITH_RES(vfslide1up_vf, int64_t, 64,
                                      0x40934A3D70A3D70A /*1234.56*/,
                                      ARRAY(int64_t),
                                      (int64_t)i < 1 ? src[0] : src[i - 1])
UTEST_RVV_VP_VSLIDE1_VF_FORM_WITH_RES(vfslide1up_vf, int32_t, 32,
                                      0x449A51EC /*1234.56f*/, ARRAY(int32_t),
                                      (int32_t)i < 1 ? src[0] : src[i - 1])
// Test for vfslide1up_vf_signaling_NaN
UTEST_RVV_VP_VSLIDE1_VF_FORM_WITH_RES(vfslide1up_vf, int64_t, 64,
                                      0x7FF4000000000000, ARRAY(int64_t),
                                      (int64_t)i < 1 ? src[0] : src[i - 1])
UTEST_RVV_VP_VSLIDE1_VF_FORM_WITH_RES(vfslide1up_vf, int32_t, 32, 0x7F400000,
                                      ARRAY(int32_t),
                                      (int32_t)i < 1 ? src[0] : src[i - 1])
#undef UTEST_RVV_VP_VSLIDE1_VF_FORM_WITH_RES
#undef ARRAY

#define UTEST_VFIRST_M_WITH_WIDTH(width)                            \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_vfirst_m_##width) {      \
    if (!CpuFeatures::IsSupported(RVV)) {                    \
      return;                                                       \
    }                                                               \
    uint32_t vlen = CpuFeatures::vlen();                            \
    uint32_t n = vlen / width;                                      \
                                                                    \
    for (uint32_t i = 0; i <= n; i++) {                             \
      uint64_t src[kMaxElements] = {0};                             \
      src[0] = 1L << i;                                             \
      auto fn = [](MacroAssembler& assm) {                          \
        __ VU.set(t0, zero_reg, VSew::E##width, m1);                \
        __ vl(v2, a0, 0, VSew::E##width);                           \
        __ vfirst_m(a0, v2);                                        \
      };                                                            \
      auto res = GenAndRunTest<int64_t, int64_t>((int64_t)src, fn); \
      CHECK_EQ(i < n ? i : (int64_t)-1, res);                       \
    }                                                               \
  }

UTEST_VFIRST_M_WITH_WIDTH(64)
UTEST_VFIRST_M_WITH_WIDTH(32)
UTEST_VFIRST_M_WITH_WIDTH(16)
UTEST_VFIRST_M_WITH_WIDTH(8)

#undef UTEST_VFIRST_M_WITH_WIDTH

#define UTEST_VCPOP_M_WITH_WIDTH(width)                               \
  TEST_F(AssemblerRISCV64Test, RISCV_UTEST_vcpop_m_##width) {         \
    if (!CpuFeatures::IsSupported(RVV)) {                      \
      return;                                                         \
    }                                                                 \
    uint32_t vlen = CpuFeatures::vlen();                              \
    uint32_t n = vlen / width;                                        \
                                                                      \
    for (uint16_t x : compiler::ValueHelper::GetVector<uint16_t>()) { \
      uint64_t src[kMaxElements] = {0};                               \
      src[0] = x >> (16 - n);                                         \
      auto fn = [](MacroAssembler& assm) {                            \
        __ VU.set(t0, zero_reg, VSew::E##width, m1);                  \
        __ vl(v2, a0, 0, VSew::E##width);                             \
        __ vcpop_m(a0, v2);                                           \
      };                                                              \
      auto res = GenAndRunTest<int64_t, int64_t>((int64_t)src, fn);   \
      CHECK_EQ(__builtin_popcountl(src[0]), res);                     \
    }                                                                 \
  }

UTEST_VCPOP_M_WITH_WIDTH(64)
UTEST_VCPOP_M_WITH_WIDTH(32)
UTEST_VCPOP_M_WITH_WIDTH(16)
UTEST_VCPOP_M_WITH_WIDTH(8)

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_WasmRvvS128const) {
  if (!CpuFeatures::IsSupported(RVV)) {
    return;
  }

  for (uint64_t x : compiler::ValueHelper::GetVector<int64_t>()) {
    for (uint64_t y : compiler::ValueHelper::GetVector<int64_t>()) {
      uint64_t src[2] = {x, y};
      uint8_t vals[16];
      volatile uint64_t result[kMaxElements] = {0};
      memcpy(vals, src, sizeof(vals));
      auto fn = [vals, &result](MacroAssembler& assm) {
        __ Push(kScratchReg);
        __ WasmRvvS128const(v10, vals);
        __ li(t1, Operand(int64_t(result)));
        __ VU.set(t0, zero_reg, VSew::E64, m1);
        __ vs(v10, t1, 0, VSew::E64);
        __ Pop(kScratchReg);
      };
      GenAndRunTest(fn);
      CHECK_EQ(result[0], x);
      CHECK_EQ(result[1], y);
    }
  }
}

TEST_F(AssemblerRISCV64Test, RISCV_UTEST_FP16_Round) {
  if (!CpuFeatures::IsSupported(RVV)) return;

  uint16_t result[kMaxElements] = {0};

  auto test_round = [&result](float input_f, float expected_f,
                              FPURoundingMode mode) {
    Float16 input_f16 = Float16::FromFloat32(input_f);
    Float16 expected_f16 = Float16::FromFloat32(expected_f);
    auto fn = [&result, mode](MacroAssembler& assm) {
      __ Push(kScratchReg);
      __ VU.set(t0, zero_reg, VSew::E16, m1);
      __ vmv_vx(v1, a0);  // src data in v1 (test dst==src case)
      __ vmv_vx(v2, a0);  // v_scratch
      switch (mode) {
        case RNE:
          __ Round(v1, v1, kScratchReg, v2);
          break;
        case RTZ:
          __ Trunc(v1, v1, kScratchReg, v2);
          break;
        case RDN:
          __ Floor(v1, v1, kScratchReg, v2);
          break;
        case RUP:
          __ Ceil(v1, v1, kScratchReg, v2);
          break;
        default:
          UNREACHABLE();
      }
      __ Pop(kScratchReg);
      __ vmv_xs(a0, v1);
      __ li(a3, Operand(int64_t(result)));
      __ vs(v1, a3, 0, E16);
    };
    auto res = GenAndRunTest<uint32_t, uint32_t>(input_f16.get_bits(), fn);
    for (unsigned i = 0; i < CpuFeatures::vlen() / 16; i++) {
      CHECK_EQ(expected_f16.get_bits(), result[i]);
      result[i] = 0;
    }
    CHECK_EQ(expected_f16.get_bits(), static_cast<uint16_t>(res));
  };

  // Round (RNE)
  test_round(1.5f, 2.0f, RNE);
  test_round(2.5f, 2.0f, RNE);
  test_round(-1.5f, -2.0f, RNE);
  // Trunc (RTZ)
  test_round(1.5f, 1.0f, RTZ);
  test_round(-1.5f, -1.0f, RTZ);
  // Floor (RDN)
  test_round(1.5f, 1.0f, RDN);
  test_round(-1.5f, -2.0f, RDN);
  // Ceil (RUP)
  test_round(1.5f, 2.0f, RUP);
  test_round(-1.5f, -1.0f, RUP);
  // Integers / zero
  test_round(3.0f, 3.0f, RNE);
  test_round(-3.0f, -3.0f, RTZ);
  test_round(0.0f, 0.0f, RDN);
  test_round(-0.0f, -0.0f, RUP);

  // Infinities and NaN (same result regardless of rounding mode)
  auto test_special = [&result](float input_f, auto check_fn) {
    Float16 f16 = Float16::FromFloat32(input_f);
    for (int mode = 0; mode < 4; mode++) {
      FPURoundingMode frm = static_cast<FPURoundingMode>(mode);
      auto fn = [&result, frm](MacroAssembler& assm) {
        __ Push(kScratchReg);
        __ VU.set(t0, zero_reg, VSew::E16, m1);
        __ vmv_vx(v1, a0);
        __ vmv_vx(v2, a0);
        switch (frm) {
          case RNE:
            __ Round(v1, v1, kScratchReg, v2);
            break;
          case RTZ:
            __ Trunc(v1, v1, kScratchReg, v2);
            break;
          case RDN:
            __ Floor(v1, v1, kScratchReg, v2);
            break;
          case RUP:
            __ Ceil(v1, v1, kScratchReg, v2);
            break;
          default:
            UNREACHABLE();
        }
        __ Pop(kScratchReg);
        __ vmv_xs(a0, v1);
        __ li(a3, Operand(int64_t(result)));
        __ vs(v1, a3, 0, E16);
      };
      auto res = GenAndRunTest<uint32_t, uint32_t>(f16.get_bits(), fn);
      check_fn(res);
      for (unsigned i = 0; i < CpuFeatures::vlen() / 16; i++) {
        check_fn(result[i]);
        result[i] = 0;
      }
    }
  };

  test_special(1.0f / 0.0f, [](uint16_t val) {
    CHECK_EQ(Float16::FromFloat32(1.0f / 0.0f).get_bits(), val);
  });
  test_special(-1.0f / 0.0f, [](uint16_t val) {
    CHECK_EQ(Float16::FromFloat32(-1.0f / 0.0f).get_bits(), val);
  });
  test_special(std::numeric_limits<float>::quiet_NaN(), [](uint16_t val) {
    CHECK(std::isnan(Float16::FromBits(val).ToFloat32()));
  });
}

#undef UTEST_VCPOP_M_WITH_WIDTH

#undef __

}  // namespace internal
}  // namespace v8
