| /* |
| * Copyright (C) 2023 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "assembler_riscv64.h" |
| |
| #include <inttypes.h> |
| |
| #include <map> |
| |
| #include "base/bit_utils.h" |
| #include "utils/assembler_test.h" |
| |
| #define __ GetAssembler()-> |
| |
| namespace art HIDDEN { |
| namespace riscv64 { |
| |
| struct RISCV64CpuRegisterCompare { |
| bool operator()(const XRegister& a, const XRegister& b) const { return a < b; } |
| }; |
| |
| class AssemblerRISCV64Test : public AssemblerTest<Riscv64Assembler, |
| Riscv64Label, |
| XRegister, |
| FRegister, |
| int32_t> { |
| public: |
| using Base = AssemblerTest<Riscv64Assembler, |
| Riscv64Label, |
| XRegister, |
| FRegister, |
| int32_t>; |
| |
| AssemblerRISCV64Test() |
| : instruction_set_features_(Riscv64InstructionSetFeatures::FromVariant("default", nullptr)) {} |
| |
| protected: |
| Riscv64Assembler* CreateAssembler(ArenaAllocator* allocator) override { |
| return new (allocator) Riscv64Assembler(allocator, instruction_set_features_.get()); |
| } |
| |
| InstructionSet GetIsa() override { return InstructionSet::kRiscv64; } |
| |
| // Clang's assembler takes advantage of certain extensions for emitting constants with `li` |
| // but our assembler does not. For now, we use a simple `-march` to avoid the divergence. |
| // TODO(riscv64): Implement these more efficient patterns in assembler. |
| void SetUseSimpleMarch(bool value) { |
| use_simple_march_ = value; |
| } |
| |
| std::vector<std::string> GetAssemblerCommand() override { |
| std::vector<std::string> result = Base::GetAssemblerCommand(); |
| if (use_simple_march_) { |
| auto it = std::find_if(result.begin(), |
| result.end(), |
| [](const std::string& s) { return StartsWith(s, "-march="); }); |
| CHECK(it != result.end()); |
| *it = "-march=rv64imafd"; |
| } |
| return result; |
| } |
| |
| std::vector<std::string> GetDisassemblerCommand() override { |
| std::vector<std::string> result = Base::GetDisassemblerCommand(); |
| if (use_simple_march_) { |
| auto it = std::find_if(result.begin(), |
| result.end(), |
| [](const std::string& s) { return StartsWith(s, "--mattr="); }); |
| CHECK(it != result.end()); |
| *it = "--mattr=+F,+D,+A"; |
| } |
| return result; |
| } |
| |
| void SetUpHelpers() override { |
| if (secondary_register_names_.empty()) { |
| secondary_register_names_.emplace(Zero, "zero"); |
| secondary_register_names_.emplace(RA, "ra"); |
| secondary_register_names_.emplace(SP, "sp"); |
| secondary_register_names_.emplace(GP, "gp"); |
| secondary_register_names_.emplace(TP, "tp"); |
| secondary_register_names_.emplace(T0, "t0"); |
| secondary_register_names_.emplace(T1, "t1"); |
| secondary_register_names_.emplace(T2, "t2"); |
| secondary_register_names_.emplace(S0, "s0"); // s0/fp |
| secondary_register_names_.emplace(S1, "s1"); |
| secondary_register_names_.emplace(A0, "a0"); |
| secondary_register_names_.emplace(A1, "a1"); |
| secondary_register_names_.emplace(A2, "a2"); |
| secondary_register_names_.emplace(A3, "a3"); |
| secondary_register_names_.emplace(A4, "a4"); |
| secondary_register_names_.emplace(A5, "a5"); |
| secondary_register_names_.emplace(A6, "a6"); |
| secondary_register_names_.emplace(A7, "a7"); |
| secondary_register_names_.emplace(S2, "s2"); |
| secondary_register_names_.emplace(S3, "s3"); |
| secondary_register_names_.emplace(S4, "s4"); |
| secondary_register_names_.emplace(S5, "s5"); |
| secondary_register_names_.emplace(S6, "s6"); |
| secondary_register_names_.emplace(S7, "s7"); |
| secondary_register_names_.emplace(S8, "s8"); |
| secondary_register_names_.emplace(S9, "s9"); |
| secondary_register_names_.emplace(S10, "s10"); |
| secondary_register_names_.emplace(S11, "s11"); |
| secondary_register_names_.emplace(T3, "t3"); |
| secondary_register_names_.emplace(T4, "t4"); |
| secondary_register_names_.emplace(T5, "t5"); |
| secondary_register_names_.emplace(T6, "t6"); |
| } |
| } |
| |
| void TearDown() override { |
| AssemblerTest::TearDown(); |
| } |
| |
| std::vector<Riscv64Label> GetAddresses() override { |
| UNIMPLEMENTED(FATAL) << "Feature not implemented yet"; |
| UNREACHABLE(); |
| } |
| |
| ArrayRef<const XRegister> GetRegisters() override { |
| static constexpr XRegister kXRegisters[] = { |
| Zero, |
| RA, |
| SP, |
| GP, |
| TP, |
| T0, |
| T1, |
| T2, |
| S0, |
| S1, |
| A0, |
| A1, |
| A2, |
| A3, |
| A4, |
| A5, |
| A6, |
| A7, |
| S2, |
| S3, |
| S4, |
| S5, |
| S6, |
| S7, |
| S8, |
| S9, |
| S10, |
| S11, |
| T3, |
| T4, |
| T5, |
| T6, |
| }; |
| return ArrayRef<const XRegister>(kXRegisters); |
| } |
| |
| ArrayRef<const FRegister> GetFPRegisters() override { |
| static constexpr FRegister kFRegisters[] = { |
| FT0, |
| FT1, |
| FT2, |
| FT3, |
| FT4, |
| FT5, |
| FT6, |
| FT7, |
| FS0, |
| FS1, |
| FA0, |
| FA1, |
| FA2, |
| FA3, |
| FA4, |
| FA5, |
| FA6, |
| FA7, |
| FS2, |
| FS3, |
| FS4, |
| FS5, |
| FS6, |
| FS7, |
| FS8, |
| FS9, |
| FS10, |
| FS11, |
| FT8, |
| FT9, |
| FT10, |
| FT11, |
| }; |
| return ArrayRef<const FRegister>(kFRegisters); |
| } |
| |
| std::string GetSecondaryRegisterName(const XRegister& reg) override { |
| CHECK(secondary_register_names_.find(reg) != secondary_register_names_.end()); |
| return secondary_register_names_[reg]; |
| } |
| |
| int32_t CreateImmediate(int64_t imm_value) override { |
| return dchecked_integral_cast<int32_t>(imm_value); |
| } |
| |
| template <typename Emit> |
| std::string RepeatInsn(size_t count, const std::string& insn, Emit&& emit) { |
| std::string result; |
| for (; count != 0u; --count) { |
| result += insn; |
| emit(); |
| } |
| return result; |
| } |
| |
| std::string EmitNops(size_t size) { |
| // TODO(riscv64): Support "C" Standard Extension. |
| DCHECK_ALIGNED(size, sizeof(uint32_t)); |
| const size_t num_nops = size / sizeof(uint32_t); |
| return RepeatInsn(num_nops, "nop\n", [&]() { __ Nop(); }); |
| } |
| |
| template <typename EmitLoadConst> |
| void TestLoadConst64(const std::string& test_name, |
| bool can_use_tmp, |
| EmitLoadConst&& emit_load_const) { |
| std::string expected; |
| // Test standard immediates. Unlike other instructions, `Li()` accepts an `int64_t` but |
| // this is unsupported by `CreateImmediate()`, so we cannot use `RepeatRIb()` for these. |
| // Note: This `CreateImmediateValuesBits()` call does not produce any values where |
| // `LoadConst64()` would emit different code from `Li()`. |
| for (int64_t value : CreateImmediateValuesBits(64, /*as_uint=*/ false)) { |
| emit_load_const(A0, value); |
| expected += "li a0, " + std::to_string(value) + "\n"; |
| } |
| // Test various registers with a few small values. |
| // (Even Zero is an accepted register even if that does not really load the requested value.) |
| for (XRegister reg : GetRegisters()) { |
| ScratchRegisterScope srs(GetAssembler()); |
| srs.ExcludeXRegister(reg); |
| std::string rd = GetRegisterName(reg); |
| emit_load_const(reg, -1); |
| expected += "li " + rd + ", -1\n"; |
| emit_load_const(reg, 0); |
| expected += "li " + rd + ", 0\n"; |
| emit_load_const(reg, 1); |
| expected += "li " + rd + ", 1\n"; |
| } |
| // Test some significant values. Some may just repeat the tests above but other values |
| // show some complex patterns, even exposing a value where clang (and therefore also this |
| // assembler) does not generate the shortest sequence. |
| // For the following values, `LoadConst64()` emits the same code as `Li()`. |
| int64_t test_values1[] = { |
| // Small values, either ADDI, ADDI+SLLI, LUI, or LUI+ADDIW. |
| // The ADDI+LUI is presumably used to allow shorter code for RV64C. |
| -4097, -4096, -4095, -2176, -2049, -2048, -2047, -1025, -1024, -1023, -2, -1, |
| 0, 1, 2, 1023, 1024, 1025, 2047, 2048, 2049, 2176, 4095, 4096, 4097, |
| // Just below std::numeric_limits<int32_t>::min() |
| INT64_C(-0x80000001), // LUI+ADDI |
| INT64_C(-0x80000800), // LUI+ADDI |
| INT64_C(-0x80000801), // LUI+ADDIW+SLLI+ADDI; LUI+ADDI+ADDI would be shorter. |
| INT64_C(-0x80000800123), // LUI+ADDIW+SLLI+ADDI |
| INT64_C(0x0123450000000123), // LUI+SLLI+ADDI |
| INT64_C(-0x7654300000000123), // LUI+SLLI+ADDI |
| INT64_C(0x0fffffffffff0000), // LUI+SRLI |
| INT64_C(0x0ffffffffffff000), // LUI+SRLI |
| INT64_C(0x0ffffffffffff010), // LUI+ADDIW+SRLI |
| INT64_C(0x0fffffffffffff10), // ADDI+SLLI+ADDI; LUI+ADDIW+SRLI would be same length. |
| INT64_C(0x0fffffffffffff80), // ADDI+SRLI |
| INT64_C(0x0ffffffff7ffff80), // LUI+ADDI+SRLI |
| INT64_C(0x0123450000001235), // LUI+SLLI+ADDI+SLLI+ADDI |
| INT64_C(0x0123450000001234), // LUI+SLLI+ADDI+SLLI |
| INT64_C(0x0000000fff808010), // LUI+SLLI+SRLI |
| INT64_C(0x00000000fff80801), // LUI+SLLI+SRLI |
| INT64_C(0x00000000ffffffff), // ADDI+SRLI |
| INT64_C(0x00000001ffffffff), // ADDI+SRLI |
| INT64_C(0x00000003ffffffff), // ADDI+SRLI |
| INT64_C(0x00000000ffc00801), // LUI+ADDIW+SLLI+ADDI |
| INT64_C(0x00000001fffff7fe), // ADDI+SLLI+SRLI |
| }; |
| for (int64_t value : test_values1) { |
| emit_load_const(A0, value); |
| expected += "li a0, " + std::to_string(value) + "\n"; |
| } |
| // For the following values, `LoadConst64()` emits different code than `Li()`. |
| std::pair<int64_t, const char*> test_values2[] = { |
| // Li: LUI+ADDIW+SLLI+ADDI+SLLI+ADDI+SLLI+ADDI |
| // LoadConst: LUI+ADDIW+LUI+ADDIW+SLLI+ADD (using TMP) |
| { INT64_C(0x1234567812345678), |
| "li {reg1}, 0x12345678 / 8\n" // Trailing zero bits in high word are handled by SLLI. |
| "li {reg2}, 0x12345678\n" |
| "slli {reg1}, {reg1}, 32 + 3\n" |
| "add {reg1}, {reg1}, {reg2}\n" }, |
| { INT64_C(0x1234567887654321), |
| "li {reg1}, 0x12345678 + 1\n" // One higher to compensate for negative TMP. |
| "li {reg2}, 0x87654321 - 0x100000000\n" |
| "slli {reg1}, {reg1}, 32\n" |
| "add {reg1}, {reg1}, {reg2}\n" }, |
| { INT64_C(-0x1234567887654321), |
| "li {reg1}, -0x12345678 - 1\n" // High 32 bits of the constant. |
| "li {reg2}, 0x100000000 - 0x87654321\n" // Low 32 bits of the constant. |
| "slli {reg1}, {reg1}, 32\n" |
| "add {reg1}, {reg1}, {reg2}\n" }, |
| |
| // Li: LUI+SLLI+ADDI+SLLI+ADDI+SLLI |
| // LoadConst: LUI+LUI+SLLI+ADD (using TMP) |
| { INT64_C(0x1234500012345000), |
| "lui {reg1}, 0x12345\n" |
| "lui {reg2}, 0x12345\n" |
| "slli {reg1}, {reg1}, 44 - 12\n" |
| "add {reg1}, {reg1}, {reg2}\n" }, |
| { INT64_C(0x0123450012345000), |
| "lui {reg1}, 0x12345\n" |
| "lui {reg2}, 0x12345\n" |
| "slli {reg1}, {reg1}, 40 - 12\n" |
| "add {reg1}, {reg1}, {reg2}\n" }, |
| |
| // Li: LUI+ADDIW+SLLI+ADDI+SLLI+ADDI |
| // LoadConst: LUI+LUI+ADDIW+SLLI+ADD (using TMP) |
| { INT64_C(0x0001234512345678), |
| "lui {reg1}, 0x12345\n" |
| "li {reg2}, 0x12345678\n" |
| "slli {reg1}, {reg1}, 32 - 12\n" |
| "add {reg1}, {reg1}, {reg2}\n" }, |
| { INT64_C(0x0012345012345678), |
| "lui {reg1}, 0x12345\n" |
| "li {reg2}, 0x12345678\n" |
| "slli {reg1}, {reg1}, 36 - 12\n" |
| "add {reg1}, {reg1}, {reg2}\n" }, |
| }; |
| for (auto [value, fmt] : test_values2) { |
| emit_load_const(A0, value); |
| if (can_use_tmp) { |
| std::string base = fmt; |
| ReplaceReg(REG1_TOKEN, GetRegisterName(A0), &base); |
| ReplaceReg(REG2_TOKEN, GetRegisterName(TMP), &base); |
| expected += base; |
| } else { |
| expected += "li a0, " + std::to_string(value) + "\n"; |
| } |
| } |
| |
| DriverStr(expected, test_name); |
| } |
| |
| auto GetPrintBcond() { |
| return [](const std::string& cond, |
| [[maybe_unused]] const std::string& opposite_cond, |
| const std::string& args, |
| const std::string& target) { |
| return "b" + cond + args + ", " + target + "\n"; |
| }; |
| } |
| |
| auto GetPrintBcondOppositeAndJ(const std::string& skip_label) { |
| return [=]([[maybe_unused]] const std::string& cond, |
| const std::string& opposite_cond, |
| const std::string& args, |
| const std::string& target) { |
| return "b" + opposite_cond + args + ", " + skip_label + "f\n" + |
| "j " + target + "\n" + |
| skip_label + ":\n"; |
| }; |
| } |
| |
| auto GetPrintBcondOppositeAndTail(const std::string& skip_label, const std::string& base_label) { |
| return [=]([[maybe_unused]] const std::string& cond, |
| const std::string& opposite_cond, |
| const std::string& args, |
| const std::string& target) { |
| return "b" + opposite_cond + args + ", " + skip_label + "f\n" + |
| base_label + ":\n" + |
| "auipc t6, %pcrel_hi(" + target + ")\n" + |
| "jalr x0, %pcrel_lo(" + base_label + "b)(t6)\n" + |
| skip_label + ":\n"; |
| }; |
| } |
| |
| // Helper function for basic tests that all branch conditions map to the correct opcodes, |
| // whether with branch expansion (a conditional branch with opposite condition over an |
| // unconditional branch) or without. |
| template <typename PrintBcond> |
| std::string EmitBcondForAllConditions(Riscv64Label* label, |
| const std::string& target, |
| PrintBcond&& print_bcond, |
| bool is_bare) { |
| XRegister rs = A0; |
| __ Beqz(rs, label, is_bare); |
| __ Bnez(rs, label, is_bare); |
| __ Blez(rs, label, is_bare); |
| __ Bgez(rs, label, is_bare); |
| __ Bltz(rs, label, is_bare); |
| __ Bgtz(rs, label, is_bare); |
| XRegister rt = A1; |
| __ Beq(rs, rt, label, is_bare); |
| __ Bne(rs, rt, label, is_bare); |
| __ Ble(rs, rt, label, is_bare); |
| __ Bge(rs, rt, label, is_bare); |
| __ Blt(rs, rt, label, is_bare); |
| __ Bgt(rs, rt, label, is_bare); |
| __ Bleu(rs, rt, label, is_bare); |
| __ Bgeu(rs, rt, label, is_bare); |
| __ Bltu(rs, rt, label, is_bare); |
| __ Bgtu(rs, rt, label, is_bare); |
| |
| return |
| print_bcond("eq", "ne", "z a0", target) + |
| print_bcond("ne", "eq", "z a0", target) + |
| print_bcond("le", "gt", "z a0", target) + |
| print_bcond("ge", "lt", "z a0", target) + |
| print_bcond("lt", "ge", "z a0", target) + |
| print_bcond("gt", "le", "z a0", target) + |
| print_bcond("eq", "ne", " a0, a1", target) + |
| print_bcond("ne", "eq", " a0, a1", target) + |
| print_bcond("le", "gt", " a0, a1", target) + |
| print_bcond("ge", "lt", " a0, a1", target) + |
| print_bcond("lt", "ge", " a0, a1", target) + |
| print_bcond("gt", "le", " a0, a1", target) + |
| print_bcond("leu", "gtu", " a0, a1", target) + |
| print_bcond("geu", "ltu", " a0, a1", target) + |
| print_bcond("ltu", "geu", " a0, a1", target) + |
| print_bcond("gtu", "leu", " a0, a1", target); |
| } |
| |
| // Test Bcond for forward branches with all conditions. |
| // The gap must be such that either all branches expand, or none does. |
| template <typename PrintBcond> |
| void TestBcondForward(const std::string& test_name, |
| size_t gap_size, |
| const std::string& target_label, |
| PrintBcond&& print_bcond, |
| bool is_bare = false) { |
| std::string expected; |
| Riscv64Label label; |
| expected += EmitBcondForAllConditions(&label, target_label + "f", print_bcond, is_bare); |
| expected += EmitNops(gap_size); |
| __ Bind(&label); |
| expected += target_label + ":\n"; |
| DriverStr(expected, test_name); |
| } |
| |
| // Test Bcond for backward branches with all conditions. |
| // The gap must be such that either all branches expand, or none does. |
| template <typename PrintBcond> |
| void TestBcondBackward(const std::string& test_name, |
| size_t gap_size, |
| const std::string& target_label, |
| PrintBcond&& print_bcond, |
| bool is_bare = false) { |
| std::string expected; |
| Riscv64Label label; |
| __ Bind(&label); |
| expected += target_label + ":\n"; |
| expected += EmitNops(gap_size); |
| expected += EmitBcondForAllConditions(&label, target_label + "b", print_bcond, is_bare); |
| DriverStr(expected, test_name); |
| } |
| |
| size_t MaxOffset13BackwardDistance() { |
| return 4 * KB; |
| } |
| |
| size_t MaxOffset13ForwardDistance() { |
| // TODO(riscv64): Support "C" Standard Extension, max forward distance 4KiB - 2. |
| return 4 * KB - 4; |
| } |
| |
| size_t MaxOffset21BackwardDistance() { |
| return 1 * MB; |
| } |
| |
| size_t MaxOffset21ForwardDistance() { |
| // TODO(riscv64): Support "C" Standard Extension, max forward distance 1MiB - 2. |
| return 1 * MB - 4; |
| } |
| |
| template <typename PrintBcond> |
| void TestBeqA0A1Forward(const std::string& test_name, |
| size_t nops_size, |
| const std::string& target_label, |
| PrintBcond&& print_bcond, |
| bool is_bare = false) { |
| std::string expected; |
| Riscv64Label label; |
| __ Beq(A0, A1, &label, is_bare); |
| expected += print_bcond("eq", "ne", " a0, a1", target_label + "f"); |
| expected += EmitNops(nops_size); |
| __ Bind(&label); |
| expected += target_label + ":\n"; |
| DriverStr(expected, test_name); |
| } |
| |
| template <typename PrintBcond> |
| void TestBeqA0A1Backward(const std::string& test_name, |
| size_t nops_size, |
| const std::string& target_label, |
| PrintBcond&& print_bcond, |
| bool is_bare = false) { |
| std::string expected; |
| Riscv64Label label; |
| __ Bind(&label); |
| expected += target_label + ":\n"; |
| expected += EmitNops(nops_size); |
| __ Beq(A0, A1, &label, is_bare); |
| expected += print_bcond("eq", "ne", " a0, a1", target_label + "b"); |
| DriverStr(expected, test_name); |
| } |
| |
| // Test a branch setup where expanding one branch causes expanding another branch |
| // which causes expanding another branch, etc. The argument `cascade` determines |
| // whether we push the first branch to expand, or not. |
| template <typename PrintBcond> |
| void TestBeqA0A1MaybeCascade(const std::string& test_name, |
| bool cascade, |
| PrintBcond&& print_bcond) { |
| const size_t kNumBeqs = MaxOffset13ForwardDistance() / sizeof(uint32_t) / 2u; |
| auto label_name = [](size_t i) { return ".L" + std::to_string(i); }; |
| |
| std::string expected; |
| std::vector<Riscv64Label> labels(kNumBeqs); |
| for (size_t i = 0; i != kNumBeqs; ++i) { |
| __ Beq(A0, A1, &labels[i]); |
| expected += print_bcond("eq", "ne", " a0, a1", label_name(i)); |
| } |
| if (cascade) { |
| expected += EmitNops(sizeof(uint32_t)); |
| } |
| for (size_t i = 0; i != kNumBeqs; ++i) { |
| expected += EmitNops(2 * sizeof(uint32_t)); |
| __ Bind(&labels[i]); |
| expected += label_name(i) + ":\n"; |
| } |
| DriverStr(expected, test_name); |
| } |
| |
| auto GetPrintJalRd() { |
| return [=](XRegister rd, const std::string& target) { |
| std::string rd_name = GetRegisterName(rd); |
| return "jal " + rd_name + ", " + target + "\n"; |
| }; |
| } |
| |
| auto GetPrintCallRd(const std::string& base_label) { |
| return [=](XRegister rd, const std::string& target) { |
| std::string rd_name = GetRegisterName(rd); |
| std::string temp_name = (rd != Zero) ? rd_name : GetRegisterName(TMP); |
| return base_label + ":\n" + |
| "auipc " + temp_name + ", %pcrel_hi(" + target + ")\n" + |
| "jalr " + rd_name + ", %pcrel_lo(" + base_label + "b)(" + temp_name + ")\n"; |
| }; |
| } |
| |
| template <typename PrintJalRd> |
| void TestJalRdForward(const std::string& test_name, |
| size_t gap_size, |
| const std::string& label_name, |
| PrintJalRd&& print_jalrd, |
| bool is_bare = false) { |
| std::string expected; |
| Riscv64Label label; |
| for (XRegister reg : GetRegisters()) { |
| __ Jal(reg, &label, is_bare); |
| expected += print_jalrd(reg, label_name + "f"); |
| } |
| expected += EmitNops(gap_size); |
| __ Bind(&label); |
| expected += label_name + ":\n"; |
| DriverStr(expected, test_name); |
| } |
| |
| template <typename PrintJalRd> |
| void TestJalRdBackward(const std::string& test_name, |
| size_t gap_size, |
| const std::string& label_name, |
| PrintJalRd&& print_jalrd, |
| bool is_bare = false) { |
| std::string expected; |
| Riscv64Label label; |
| __ Bind(&label); |
| expected += label_name + ":\n"; |
| expected += EmitNops(gap_size); |
| for (XRegister reg : GetRegisters()) { |
| __ Jal(reg, &label, is_bare); |
| expected += print_jalrd(reg, label_name + "b"); |
| } |
| DriverStr(expected, test_name); |
| } |
| |
| auto GetEmitJ(bool is_bare = false) { |
| return [=](Riscv64Label* label) { __ J(label, is_bare); }; |
| } |
| |
| auto GetEmitJal() { |
| return [=](Riscv64Label* label) { __ Jal(label); }; |
| } |
| |
| auto GetPrintJ() { |
| return [=](const std::string& target) { |
| return "j " + target + "\n"; |
| }; |
| } |
| |
| auto GetPrintJal() { |
| return [=](const std::string& target) { |
| return "jal " + target + "\n"; |
| }; |
| } |
| |
| auto GetPrintTail(const std::string& base_label) { |
| return [=](const std::string& target) { |
| return base_label + ":\n" + |
| "auipc t6, %pcrel_hi(" + target + ")\n" + |
| "jalr x0, %pcrel_lo(" + base_label + "b)(t6)\n"; |
| }; |
| } |
| |
| auto GetPrintCall(const std::string& base_label) { |
| return [=](const std::string& target) { |
| return base_label + ":\n" + |
| "auipc ra, %pcrel_hi(" + target + ")\n" + |
| "jalr ra, %pcrel_lo(" + base_label + "b)(ra)\n"; |
| }; |
| } |
| |
| template <typename EmitBuncond, typename PrintBuncond> |
| void TestBuncondForward(const std::string& test_name, |
| size_t gap_size, |
| const std::string& label_name, |
| EmitBuncond&& emit_buncond, |
| PrintBuncond&& print_buncond) { |
| std::string expected; |
| Riscv64Label label; |
| emit_buncond(&label); |
| expected += print_buncond(label_name + "f"); |
| expected += EmitNops(gap_size); |
| __ Bind(&label); |
| expected += label_name + ":\n"; |
| DriverStr(expected, test_name); |
| } |
| |
| template <typename EmitBuncond, typename PrintBuncond> |
| void TestBuncondBackward(const std::string& test_name, |
| size_t gap_size, |
| const std::string& label_name, |
| EmitBuncond&& emit_buncond, |
| PrintBuncond&& print_buncond) { |
| std::string expected; |
| Riscv64Label label; |
| __ Bind(&label); |
| expected += label_name + ":\n"; |
| expected += EmitNops(gap_size); |
| emit_buncond(&label); |
| expected += print_buncond(label_name + "b"); |
| DriverStr(expected, test_name); |
| } |
| |
| template <typename EmitOp> |
| void TestAddConst(const std::string& test_name, |
| size_t bits, |
| const std::string& suffix, |
| EmitOp&& emit_op) { |
| int64_t kImm12s[] = { |
| 0, 1, 2, 0xff, 0x100, 0x1ff, 0x200, 0x3ff, 0x400, 0x7ff, |
| -1, -2, -0x100, -0x101, -0x200, -0x201, -0x400, -0x401, -0x800, |
| }; |
| int64_t kSimplePositiveValues[] = { |
| 0x800, 0x801, 0xbff, 0xc00, 0xff0, 0xff7, 0xff8, 0xffb, 0xffc, 0xffd, 0xffe, |
| }; |
| int64_t kSimpleNegativeValues[] = { |
| -0x801, -0x802, -0xbff, -0xc00, -0xff0, -0xff8, -0xffc, -0xffe, -0xfff, -0x1000, |
| }; |
| std::vector<int64_t> large_values = CreateImmediateValuesBits(bits, /*as_uint=*/ false); |
| auto kept_end = std::remove_if(large_values.begin(), |
| large_values.end(), |
| [](int64_t value) { return IsInt<13>(value); }); |
| large_values.erase(kept_end, large_values.end()); |
| large_values.push_back(0xfff); |
| |
| std::string expected; |
| for (XRegister rd : GetRegisters()) { |
| std::string rd_name = GetRegisterName(rd); |
| std::string addi_rd = ART_FORMAT("addi{} {}, ", suffix, rd_name); |
| std::string add_rd = ART_FORMAT("add{} {}, ", suffix, rd_name); |
| for (XRegister rs1 : GetRegisters()) { |
| ScratchRegisterScope srs(GetAssembler()); |
| srs.ExcludeXRegister(rs1); |
| srs.ExcludeXRegister(rd); |
| |
| std::string rs1_name = GetRegisterName(rs1); |
| std::string tmp_name = GetRegisterName((rs1 != TMP) ? TMP : TMP2); |
| std::string addi_tmp = ART_FORMAT("addi{} {}, ", suffix, tmp_name); |
| |
| for (int64_t imm : kImm12s) { |
| emit_op(rd, rs1, imm); |
| expected += ART_FORMAT("{}{}, {}\n", addi_rd, rs1_name, std::to_string(imm)); |
| } |
| |
| auto emit_simple_ops = [&](ArrayRef<const int64_t> imms, int64_t adjustment) { |
| for (int64_t imm : imms) { |
| emit_op(rd, rs1, imm); |
| expected += ART_FORMAT("{}{}, {}\n", addi_tmp, rs1_name, std::to_string(adjustment)); |
| expected += |
| ART_FORMAT("{}{}, {}\n", addi_rd, tmp_name, std::to_string(imm - adjustment)); |
| } |
| }; |
| emit_simple_ops(ArrayRef<const int64_t>(kSimplePositiveValues), 0x7ff); |
| emit_simple_ops(ArrayRef<const int64_t>(kSimpleNegativeValues), -0x800); |
| |
| for (int64_t imm : large_values) { |
| emit_op(rd, rs1, imm); |
| expected += ART_FORMAT("li {}, {}\n", tmp_name, std::to_string(imm)); |
| expected += ART_FORMAT("{}{}, {}\n", add_rd, rs1_name, tmp_name); |
| } |
| } |
| } |
| DriverStr(expected, test_name); |
| } |
| |
| template <typename GetTemp, typename EmitOp> |
| std::string RepeatLoadStoreArbitraryOffset(const std::string& head, |
| GetTemp&& get_temp, |
| EmitOp&& emit_op) { |
| int64_t kImm12s[] = { |
| 0, 1, 2, 0xff, 0x100, 0x1ff, 0x200, 0x3ff, 0x400, 0x7ff, |
| -1, -2, -0x100, -0x101, -0x200, -0x201, -0x400, -0x401, -0x800, |
| }; |
| int64_t kSimplePositiveOffsetsAlign8[] = { |
| 0x800, 0x801, 0xbff, 0xc00, 0xff0, 0xff4, 0xff6, 0xff7 |
| }; |
| int64_t kSimplePositiveOffsetsAlign4[] = { |
| 0xff8, 0xff9, 0xffa, 0xffb |
| }; |
| int64_t kSimplePositiveOffsetsAlign2[] = { |
| 0xffc, 0xffd |
| }; |
| int64_t kSimplePositiveOffsetsNoAlign[] = { |
| 0xffe |
| }; |
| int64_t kSimpleNegativeOffsets[] = { |
| -0x801, -0x802, -0xbff, -0xc00, -0xff0, -0xff8, -0xffc, -0xffe, -0xfff, -0x1000, |
| }; |
| int64_t kSplitOffsets[] = { |
| 0xfff, 0x1000, 0x1001, 0x17ff, 0x1800, 0x1fff, 0x2000, 0x2001, 0x27ff, 0x2800, |
| 0x7fffe7ff, 0x7fffe800, 0x7fffefff, 0x7ffff000, 0x7ffff001, 0x7ffff7ff, |
| -0x1001, -0x1002, -0x17ff, -0x1800, -0x1801, -0x2000, -0x2001, -0x2800, -0x2801, |
| -0x7ffff000, -0x7ffff001, -0x7ffff800, -0x7ffff801, -0x7fffffff, -0x80000000, |
| }; |
| int64_t kSpecialOffsets[] = { |
| 0x7ffff800, 0x7ffff801, 0x7ffffffe, 0x7fffffff |
| }; |
| |
| std::string expected; |
| for (XRegister rs1 : GetRegisters()) { |
| XRegister tmp = get_temp(rs1); |
| if (tmp == kNoXRegister) { |
| continue; // Unsupported register combination. |
| } |
| std::string tmp_name = GetRegisterName(tmp); |
| ScratchRegisterScope srs(GetAssembler()); |
| srs.ExcludeXRegister(rs1); |
| std::string rs1_name = GetRegisterName(rs1); |
| |
| for (int64_t imm : kImm12s) { |
| emit_op(rs1, imm); |
| expected += ART_FORMAT("{}, {}({})\n", head, std::to_string(imm), rs1_name); |
| } |
| |
| auto emit_simple_ops = [&](ArrayRef<const int64_t> imms, int64_t adjustment) { |
| for (int64_t imm : imms) { |
| emit_op(rs1, imm); |
| expected += |
| ART_FORMAT("addi {}, {}, {}\n", tmp_name, rs1_name, std::to_string(adjustment)); |
| expected += ART_FORMAT("{}, {}({})\n", head, std::to_string(imm - adjustment), tmp_name); |
| } |
| }; |
| emit_simple_ops(ArrayRef<const int64_t>(kSimplePositiveOffsetsAlign8), 0x7f8); |
| emit_simple_ops(ArrayRef<const int64_t>(kSimplePositiveOffsetsAlign4), 0x7fc); |
| emit_simple_ops(ArrayRef<const int64_t>(kSimplePositiveOffsetsAlign2), 0x7fe); |
| emit_simple_ops(ArrayRef<const int64_t>(kSimplePositiveOffsetsNoAlign), 0x7ff); |
| emit_simple_ops(ArrayRef<const int64_t>(kSimpleNegativeOffsets), -0x800); |
| |
| for (int64_t imm : kSplitOffsets) { |
| emit_op(rs1, imm); |
| uint32_t imm20 = ((imm >> 12) + ((imm >> 11) & 1)) & 0xfffff; |
| int32_t small_offset = (imm & 0xfff) - ((imm & 0x800) << 1); |
| expected += ART_FORMAT("lui {}, {}\n", tmp_name, std::to_string(imm20)); |
| expected += ART_FORMAT("add {}, {}, {}\n", tmp_name, tmp_name, rs1_name); |
| expected += ART_FORMAT("{},{}({})\n", head, std::to_string(small_offset), tmp_name); |
| } |
| |
| for (int64_t imm : kSpecialOffsets) { |
| emit_op(rs1, imm); |
| expected += ART_FORMAT("lui {}, 0x80000\n", tmp_name); |
| expected += |
| ART_FORMAT("addiw {}, {}, {}\n", tmp_name, tmp_name, std::to_string(imm - 0x80000000)); |
| expected += ART_FORMAT("add {}, {}, {}\n", tmp_name, tmp_name, rs1_name); |
| expected += ART_FORMAT("{}, ({})\n", head, tmp_name); |
| } |
| } |
| return expected; |
| } |
| |
| void TestLoadStoreArbitraryOffset(const std::string& test_name, |
| const std::string& insn, |
| void (Riscv64Assembler::*fn)(XRegister, XRegister, int32_t), |
| bool is_store) { |
| std::string expected; |
| for (XRegister rd : GetRegisters()) { |
| ScratchRegisterScope srs(GetAssembler()); |
| srs.ExcludeXRegister(rd); |
| auto get_temp = [&](XRegister rs1) { |
| if (is_store) { |
| return (rs1 != TMP && rd != TMP) |
| ? TMP |
| : (rs1 != TMP2 && rd != TMP2) ? TMP2 : kNoXRegister; |
| } else { |
| return rs1 != TMP ? TMP : TMP2; |
| } |
| }; |
| expected += RepeatLoadStoreArbitraryOffset( |
| insn + " " + GetRegisterName(rd), |
| get_temp, |
| [&](XRegister rs1, int64_t offset) { (GetAssembler()->*fn)(rd, rs1, offset); }); |
| } |
| DriverStr(expected, test_name); |
| } |
| |
| void TestFPLoadStoreArbitraryOffset(const std::string& test_name, |
| const std::string& insn, |
| void (Riscv64Assembler::*fn)(FRegister, XRegister, int32_t)) { |
| std::string expected; |
| for (FRegister rd : GetFPRegisters()) { |
| expected += RepeatLoadStoreArbitraryOffset( |
| insn + " " + GetFPRegName(rd), |
| [&](XRegister rs1) { return rs1 != TMP ? TMP : TMP2; }, |
| [&](XRegister rs1, int64_t offset) { (GetAssembler()->*fn)(rd, rs1, offset); }); |
| } |
| DriverStr(expected, test_name); |
| } |
| |
| void TestLoadLiteral(const std::string& test_name, bool with_padding_for_long) { |
| std::string expected; |
| Literal* narrow_literal = __ NewLiteral<uint32_t>(0x12345678); |
| Literal* wide_literal = __ NewLiteral<uint64_t>(0x1234567887654321); |
| auto print_load = [&](const std::string& load, XRegister rd, const std::string& label) { |
| std::string rd_name = GetRegisterName(rd); |
| expected += "1:\n" |
| "auipc " + rd_name + ", %pcrel_hi(" + label + "f)\n" + |
| load + " " + rd_name + ", %pcrel_lo(1b)(" + rd_name + ")\n"; |
| }; |
| for (XRegister reg : GetRegisters()) { |
| if (reg != Zero) { |
| __ Loadw(reg, narrow_literal); |
| print_load("lw", reg, "2"); |
| __ Loadwu(reg, narrow_literal); |
| print_load("lwu", reg, "2"); |
| __ Loadd(reg, wide_literal); |
| print_load("ld", reg, "3"); |
| } |
| } |
| std::string tmp = GetRegisterName(TMP); |
| auto print_fp_load = [&](const std::string& load, FRegister rd, const std::string& label) { |
| std::string rd_name = GetFPRegName(rd); |
| expected += "1:\n" |
| "auipc " + tmp + ", %pcrel_hi(" + label + "f)\n" + |
| load + " " + rd_name + ", %pcrel_lo(1b)(" + tmp + ")\n"; |
| }; |
| for (FRegister freg : GetFPRegisters()) { |
| __ FLoadw(freg, narrow_literal); |
| print_fp_load("flw", freg, "2"); |
| __ FLoadd(freg, wide_literal); |
| print_fp_load("fld", freg, "3"); |
| } |
| // All literal loads above emit 8 bytes of code. The narrow literal shall emit 4 bytes of code. |
| // If we do not add another instruction, we shall end up with padding before the long literal. |
| expected += EmitNops(with_padding_for_long ? 0u : sizeof(uint32_t)); |
| expected += "2:\n" |
| ".4byte 0x12345678\n" + |
| std::string(with_padding_for_long ? ".4byte 0\n" : "") + |
| "3:\n" |
| ".8byte 0x1234567887654321\n"; |
| DriverStr(expected, test_name); |
| } |
| |
| std::string RepeatFFFFRoundingMode( |
| void (Riscv64Assembler::*f)(FRegister, FRegister, FRegister, FRegister, FPRoundingMode), |
| const std::string& fmt) { |
| CHECK(f != nullptr); |
| std::string str; |
| for (FRegister reg1 : GetFPRegisters()) { |
| for (FRegister reg2 : GetFPRegisters()) { |
| for (FRegister reg3 : GetFPRegisters()) { |
| for (FRegister reg4 : GetFPRegisters()) { |
| for (FPRoundingMode rm : kRoundingModes) { |
| (GetAssembler()->*f)(reg1, reg2, reg3, reg4, rm); |
| |
| std::string base = fmt; |
| ReplaceReg(REG1_TOKEN, GetFPRegName(reg1), &base); |
| ReplaceReg(REG2_TOKEN, GetFPRegName(reg2), &base); |
| ReplaceReg(REG3_TOKEN, GetFPRegName(reg3), &base); |
| ReplaceReg(REG4_TOKEN, GetFPRegName(reg4), &base); |
| ReplaceRoundingMode(rm, &base); |
| str += base; |
| str += "\n"; |
| } |
| } |
| } |
| } |
| } |
| return str; |
| } |
| |
| std::string RepeatFFFRoundingMode( |
| void (Riscv64Assembler::*f)(FRegister, FRegister, FRegister, FPRoundingMode), |
| const std::string& fmt) { |
| CHECK(f != nullptr); |
| std::string str; |
| for (FRegister reg1 : GetFPRegisters()) { |
| for (FRegister reg2 : GetFPRegisters()) { |
| for (FRegister reg3 : GetFPRegisters()) { |
| for (FPRoundingMode rm : kRoundingModes) { |
| (GetAssembler()->*f)(reg1, reg2, reg3, rm); |
| |
| std::string base = fmt; |
| ReplaceReg(REG1_TOKEN, GetFPRegName(reg1), &base); |
| ReplaceReg(REG2_TOKEN, GetFPRegName(reg2), &base); |
| ReplaceReg(REG3_TOKEN, GetFPRegName(reg3), &base); |
| ReplaceRoundingMode(rm, &base); |
| str += base; |
| str += "\n"; |
| } |
| } |
| } |
| } |
| return str; |
| } |
| |
| template <typename Reg1, typename Reg2> |
| std::string RepeatTemplatedRegistersRoundingMode( |
| void (Riscv64Assembler::*f)(Reg1, Reg2, FPRoundingMode), |
| ArrayRef<const Reg1> reg1_registers, |
| ArrayRef<const Reg2> reg2_registers, |
| std::string (Base::*GetName1)(const Reg1&), |
| std::string (Base::*GetName2)(const Reg2&), |
| const std::string& fmt) { |
| CHECK(f != nullptr); |
| std::string str; |
| for (Reg1 reg1 : reg1_registers) { |
| for (Reg2 reg2 : reg2_registers) { |
| for (FPRoundingMode rm : kRoundingModes) { |
| (GetAssembler()->*f)(reg1, reg2, rm); |
| |
| std::string base = fmt; |
| ReplaceReg(REG1_TOKEN, (this->*GetName1)(reg1), &base); |
| ReplaceReg(REG2_TOKEN, (this->*GetName2)(reg2), &base); |
| ReplaceRoundingMode(rm, &base); |
| str += base; |
| str += "\n"; |
| } |
| } |
| } |
| return str; |
| } |
| |
| std::string RepeatFFRoundingMode( |
| void (Riscv64Assembler::*f)(FRegister, FRegister, FPRoundingMode), |
| const std::string& fmt) { |
| return RepeatTemplatedRegistersRoundingMode(f, |
| GetFPRegisters(), |
| GetFPRegisters(), |
| &AssemblerRISCV64Test::GetFPRegName, |
| &AssemblerRISCV64Test::GetFPRegName, |
| fmt); |
| } |
| |
| std::string RepeatrFRoundingMode( |
| void (Riscv64Assembler::*f)(XRegister, FRegister, FPRoundingMode), |
| const std::string& fmt) { |
| return RepeatTemplatedRegistersRoundingMode(f, |
| GetRegisters(), |
| GetFPRegisters(), |
| &Base::GetSecondaryRegisterName, |
| &AssemblerRISCV64Test::GetFPRegName, |
| fmt); |
| } |
| |
| std::string RepeatFrRoundingMode( |
| void (Riscv64Assembler::*f)(FRegister, XRegister, FPRoundingMode), |
| const std::string& fmt) { |
| return RepeatTemplatedRegistersRoundingMode(f, |
| GetFPRegisters(), |
| GetRegisters(), |
| &AssemblerRISCV64Test::GetFPRegName, |
| &Base::GetSecondaryRegisterName, |
| fmt); |
| } |
| |
| template <typename InvalidAqRl> |
| std::string RepeatRRAqRl(void (Riscv64Assembler::*f)(XRegister, XRegister, AqRl), |
| const std::string& fmt, |
| InvalidAqRl&& invalid_aqrl) { |
| CHECK(f != nullptr); |
| std::string str; |
| for (XRegister reg1 : GetRegisters()) { |
| for (XRegister reg2 : GetRegisters()) { |
| for (AqRl aqrl : kAqRls) { |
| if (invalid_aqrl(aqrl)) { |
| continue; |
| } |
| (GetAssembler()->*f)(reg1, reg2, aqrl); |
| |
| std::string base = fmt; |
| ReplaceReg(REG1_TOKEN, GetRegisterName(reg1), &base); |
| ReplaceReg(REG2_TOKEN, GetRegisterName(reg2), &base); |
| ReplaceAqRl(aqrl, &base); |
| str += base; |
| str += "\n"; |
| } |
| } |
| } |
| return str; |
| } |
| |
| template <typename InvalidAqRl> |
| std::string RepeatRRRAqRl(void (Riscv64Assembler::*f)(XRegister, XRegister, XRegister, AqRl), |
| const std::string& fmt, |
| InvalidAqRl&& invalid_aqrl) { |
| CHECK(f != nullptr); |
| std::string str; |
| for (XRegister reg1 : GetRegisters()) { |
| for (XRegister reg2 : GetRegisters()) { |
| for (XRegister reg3 : GetRegisters()) { |
| for (AqRl aqrl : kAqRls) { |
| if (invalid_aqrl(aqrl)) { |
| continue; |
| } |
| (GetAssembler()->*f)(reg1, reg2, reg3, aqrl); |
| |
| std::string base = fmt; |
| ReplaceReg(REG1_TOKEN, GetRegisterName(reg1), &base); |
| ReplaceReg(REG2_TOKEN, GetRegisterName(reg2), &base); |
| ReplaceReg(REG3_TOKEN, GetRegisterName(reg3), &base); |
| ReplaceAqRl(aqrl, &base); |
| str += base; |
| str += "\n"; |
| } |
| } |
| } |
| } |
| return str; |
| } |
| |
| std::string RepeatRRRAqRl(void (Riscv64Assembler::*f)(XRegister, XRegister, XRegister, AqRl), |
| const std::string& fmt) { |
| return RepeatRRRAqRl(f, fmt, [](AqRl) { return false; }); |
| } |
| |
| std::string RepeatCsrrX(void (Riscv64Assembler::*f)(XRegister, uint32_t, XRegister), |
| const std::string& fmt) { |
| CHECK(f != nullptr); |
| std::vector<int64_t> csrs = CreateImmediateValuesBits(12, /*as_uint=*/ true); |
| std::string str; |
| for (XRegister reg1 : GetRegisters()) { |
| for (int64_t csr : csrs) { |
| for (XRegister reg2 : GetRegisters()) { |
| (GetAssembler()->*f)(reg1, dchecked_integral_cast<uint32_t>(csr), reg2); |
| |
| std::string base = fmt; |
| ReplaceReg(REG1_TOKEN, GetRegisterName(reg1), &base); |
| ReplaceCsrrImm(CSR_TOKEN, csr, &base); |
| ReplaceReg(REG2_TOKEN, GetRegisterName(reg2), &base); |
| str += base; |
| str += "\n"; |
| } |
| } |
| } |
| return str; |
| } |
| |
| std::string RepeatCsrrXi(void (Riscv64Assembler::*f)(XRegister, uint32_t, uint32_t), |
| const std::string& fmt) { |
| CHECK(f != nullptr); |
| std::vector<int64_t> csrs = CreateImmediateValuesBits(12, /*as_uint=*/ true); |
| std::vector<int64_t> uimms = CreateImmediateValuesBits(2, /*as_uint=*/ true); |
| std::string str; |
| for (XRegister reg : GetRegisters()) { |
| for (int64_t csr : csrs) { |
| for (int64_t uimm : uimms) { |
| (GetAssembler()->*f)( |
| reg, dchecked_integral_cast<uint32_t>(csr), dchecked_integral_cast<uint32_t>(uimm)); |
| |
| std::string base = fmt; |
| ReplaceReg(REG_TOKEN, GetRegisterName(reg), &base); |
| ReplaceCsrrImm(CSR_TOKEN, csr, &base); |
| ReplaceCsrrImm(UIMM_TOKEN, uimm, &base); |
| str += base; |
| str += "\n"; |
| } |
| } |
| } |
| return str; |
| } |
| |
| template <typename EmitCssrX> |
| void TestCsrrXMacro(const std::string& test_name, |
| const std::string& fmt, |
| EmitCssrX&& emit_csrrx) { |
| std::vector<int64_t> csrs = CreateImmediateValuesBits(12, /*as_uint=*/ true); |
| std::string expected; |
| for (XRegister reg : GetRegisters()) { |
| for (int64_t csr : csrs) { |
| emit_csrrx(dchecked_integral_cast<uint32_t>(csr), reg); |
| |
| std::string base = fmt; |
| ReplaceReg(REG_TOKEN, GetRegisterName(reg), &base); |
| ReplaceCsrrImm(CSR_TOKEN, csr, &base); |
| expected += base; |
| expected += "\n"; |
| } |
| } |
| DriverStr(expected, test_name); |
| } |
| |
| template <typename EmitCssrXi> |
| void TestCsrrXiMacro(const std::string& test_name, |
| const std::string& fmt, |
| EmitCssrXi&& emit_csrrxi) { |
| std::vector<int64_t> csrs = CreateImmediateValuesBits(12, /*as_uint=*/ true); |
| std::vector<int64_t> uimms = CreateImmediateValuesBits(2, /*as_uint=*/ true); |
| std::string expected; |
| for (int64_t csr : csrs) { |
| for (int64_t uimm : uimms) { |
| emit_csrrxi(dchecked_integral_cast<uint32_t>(csr), dchecked_integral_cast<uint32_t>(uimm)); |
| |
| std::string base = fmt; |
| ReplaceCsrrImm(CSR_TOKEN, csr, &base); |
| ReplaceCsrrImm(UIMM_TOKEN, uimm, &base); |
| expected += base; |
| expected += "\n"; |
| } |
| } |
| DriverStr(expected, test_name); |
| } |
| |
| private: |
| static constexpr const char* RM_TOKEN = "{rm}"; |
| static constexpr const char* AQRL_TOKEN = "{aqrl}"; |
| static constexpr const char* CSR_TOKEN = "{csr}"; |
| static constexpr const char* UIMM_TOKEN = "{uimm}"; |
| |
| static constexpr AqRl kAqRls[] = { AqRl::kNone, AqRl::kRelease, AqRl::kAcquire, AqRl::kAqRl }; |
| |
| static constexpr FPRoundingMode kRoundingModes[] = { |
| FPRoundingMode::kRNE, |
| FPRoundingMode::kRTZ, |
| FPRoundingMode::kRDN, |
| FPRoundingMode::kRUP, |
| FPRoundingMode::kRMM, |
| FPRoundingMode::kDYN |
| }; |
| |
| void ReplaceRoundingMode(FPRoundingMode rm, /*inout*/ std::string* str) { |
| const char* replacement; |
| switch (rm) { |
| case FPRoundingMode::kRNE: |
| replacement = "rne"; |
| break; |
| case FPRoundingMode::kRTZ: |
| replacement = "rtz"; |
| break; |
| case FPRoundingMode::kRDN: |
| replacement = "rdn"; |
| break; |
| case FPRoundingMode::kRUP: |
| replacement = "rup"; |
| break; |
| case FPRoundingMode::kRMM: |
| replacement = "rmm"; |
| break; |
| case FPRoundingMode::kDYN: |
| replacement = "dyn"; |
| break; |
| default: |
| LOG(FATAL) << "Unexpected value for rm: " << enum_cast<uint32_t>(rm); |
| UNREACHABLE(); |
| } |
| size_t rm_index = str->find(RM_TOKEN); |
| EXPECT_NE(rm_index, std::string::npos); |
| if (rm_index != std::string::npos) { |
| str->replace(rm_index, ConstexprStrLen(RM_TOKEN), replacement); |
| } |
| } |
| |
| void ReplaceAqRl(AqRl aqrl, /*inout*/ std::string* str) { |
| const char* replacement; |
| switch (aqrl) { |
| case AqRl::kNone: |
| replacement = ""; |
| break; |
| case AqRl::kRelease: |
| replacement = ".rl"; |
| break; |
| case AqRl::kAcquire: |
| replacement = ".aq"; |
| break; |
| case AqRl::kAqRl: |
| replacement = ".aqrl"; |
| break; |
| default: |
| LOG(FATAL) << "Unexpected value for `aqrl`: " << enum_cast<uint32_t>(aqrl); |
| UNREACHABLE(); |
| } |
| size_t aqrl_index = str->find(AQRL_TOKEN); |
| EXPECT_NE(aqrl_index, std::string::npos); |
| if (aqrl_index != std::string::npos) { |
| str->replace(aqrl_index, ConstexprStrLen(AQRL_TOKEN), replacement); |
| } |
| } |
| |
| static void ReplaceCsrrImm(const std::string& imm_token, |
| int64_t imm, |
| /*inout*/ std::string* str) { |
| size_t imm_index = str->find(imm_token); |
| EXPECT_NE(imm_index, std::string::npos); |
| if (imm_index != std::string::npos) { |
| str->replace(imm_index, imm_token.length(), std::to_string(imm)); |
| } |
| } |
| |
| std::map<XRegister, std::string, RISCV64CpuRegisterCompare> secondary_register_names_; |
| |
| std::unique_ptr<const Riscv64InstructionSetFeatures> instruction_set_features_; |
| bool use_simple_march_ = false; |
| }; |
| |
| TEST_F(AssemblerRISCV64Test, Toolchain) { EXPECT_TRUE(CheckTools()); } |
| |
| TEST_F(AssemblerRISCV64Test, Lui) { |
| DriverStr(RepeatRIb(&Riscv64Assembler::Lui, 20, "lui {reg}, {imm}"), "Lui"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Auipc) { |
| DriverStr(RepeatRIb(&Riscv64Assembler::Auipc, 20, "auipc {reg}, {imm}"), "Auipc"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Jal) { |
| // TODO(riscv64): Change "-19, 2" to "-20, 1" for "C" Standard Extension. |
| DriverStr(RepeatRIbS(&Riscv64Assembler::Jal, -19, 2, "jal {reg}, {imm}\n"), "Jal"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Jalr) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Jalr, -12, "jalr {reg1}, {reg2}, {imm}\n"), "Jalr"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Beq) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRRIbS(&Riscv64Assembler::Beq, -11, 2, "beq {reg1}, {reg2}, {imm}\n"), "Beq"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Bne) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRRIbS(&Riscv64Assembler::Bne, -11, 2, "bne {reg1}, {reg2}, {imm}\n"), "Bne"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Blt) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRRIbS(&Riscv64Assembler::Blt, -11, 2, "blt {reg1}, {reg2}, {imm}\n"), "Blt"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Bge) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRRIbS(&Riscv64Assembler::Bge, -11, 2, "bge {reg1}, {reg2}, {imm}\n"), "Bge"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Bltu) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRRIbS(&Riscv64Assembler::Bltu, -11, 2, "bltu {reg1}, {reg2}, {imm}\n"), "Bltu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Bgeu) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRRIbS(&Riscv64Assembler::Bgeu, -11, 2, "bgeu {reg1}, {reg2}, {imm}\n"), "Bgeu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Lb) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Lb, -12, "lb {reg1}, {imm}({reg2})"), "Lb"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Lh) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Lh, -12, "lh {reg1}, {imm}({reg2})"), "Lh"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Lw) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Lw, -12, "lw {reg1}, {imm}({reg2})"), "Lw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Ld) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Ld, -12, "ld {reg1}, {imm}({reg2})"), "Ld"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Lbu) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Lbu, -12, "lbu {reg1}, {imm}({reg2})"), "Lbu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Lhu) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Lhu, -12, "lhu {reg1}, {imm}({reg2})"), "Lhu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Lwu) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Lwu, -12, "lwu {reg1}, {imm}({reg2})"), "Lwu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sb) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Sb, -12, "sb {reg1}, {imm}({reg2})"), "Sb"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sh) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Sh, -12, "sh {reg1}, {imm}({reg2})"), "Sh"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sw) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Sw, -12, "sw {reg1}, {imm}({reg2})"), "Sw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sd) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Sd, -12, "sd {reg1}, {imm}({reg2})"), "Sd"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Addi) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Addi, -12, "addi {reg1}, {reg2}, {imm}"), "Addi"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Slti) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Slti, -12, "slti {reg1}, {reg2}, {imm}"), "Slti"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sltiu) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Sltiu, -12, "sltiu {reg1}, {reg2}, {imm}"), "Sltiu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Xori) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Xori, 11, "xori {reg1}, {reg2}, {imm}"), "Xori"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Ori) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Ori, -12, "ori {reg1}, {reg2}, {imm}"), "Ori"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Andi) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Andi, -12, "andi {reg1}, {reg2}, {imm}"), "Andi"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Slli) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Slli, 6, "slli {reg1}, {reg2}, {imm}"), "Slli"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Srli) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Srli, 6, "srli {reg1}, {reg2}, {imm}"), "Srli"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Srai) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Srai, 6, "srai {reg1}, {reg2}, {imm}"), "Srai"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Add) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Add, "add {reg1}, {reg2}, {reg3}"), "Add"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sub) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Sub, "sub {reg1}, {reg2}, {reg3}"), "Sub"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Slt) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Slt, "slt {reg1}, {reg2}, {reg3}"), "Slt"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sltu) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Sltu, "sltu {reg1}, {reg2}, {reg3}"), "Sltu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Xor) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Xor, "xor {reg1}, {reg2}, {reg3}"), "Xor"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Or) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Or, "or {reg1}, {reg2}, {reg3}"), "Or"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, And) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::And, "and {reg1}, {reg2}, {reg3}"), "And"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sll) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Sll, "sll {reg1}, {reg2}, {reg3}"), "Sll"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Srl) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Srl, "srl {reg1}, {reg2}, {reg3}"), "Srl"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sra) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Sra, "sra {reg1}, {reg2}, {reg3}"), "Sra"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Addiw) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Addiw, -12, "addiw {reg1}, {reg2}, {imm}"), "Addiw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Slliw) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Slliw, 5, "slliw {reg1}, {reg2}, {imm}"), "Slliw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Srliw) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Srliw, 5, "srliw {reg1}, {reg2}, {imm}"), "Srliw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sraiw) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Sraiw, 5, "sraiw {reg1}, {reg2}, {imm}"), "Sraiw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Addw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Addw, "addw {reg1}, {reg2}, {reg3}"), "Addw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Subw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Subw, "subw {reg1}, {reg2}, {reg3}"), "Subw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sllw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Sllw, "sllw {reg1}, {reg2}, {reg3}"), "Sllw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Srlw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Srlw, "srlw {reg1}, {reg2}, {reg3}"), "Srlw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sraw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Sraw, "sraw {reg1}, {reg2}, {reg3}"), "Sraw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Ecall) { |
| __ Ecall(); |
| DriverStr("ecall\n", "Ecall"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Ebreak) { |
| __ Ebreak(); |
| DriverStr("ebreak\n", "Ebreak"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Fence) { |
| auto get_fence_type_string = [](uint32_t fence_type) { |
| CHECK_LE(fence_type, 0xfu); |
| std::string result; |
| if ((fence_type & kFenceInput) != 0u) { |
| result += "i"; |
| } |
| if ((fence_type & kFenceOutput) != 0u) { |
| result += "o"; |
| } |
| if ((fence_type & kFenceRead) != 0u) { |
| result += "r"; |
| } |
| if ((fence_type & kFenceWrite) != 0u) { |
| result += "w"; |
| } |
| if (result.empty()) { |
| result += "0"; |
| } |
| return result; |
| }; |
| |
| std::string expected; |
| // Note: The `pred` and `succ` are 4 bits each. |
| // Some combinations are not really useful but the assembler can emit them all. |
| for (uint32_t pred = 0u; pred != 0x10; ++pred) { |
| for (uint32_t succ = 0u; succ != 0x10; ++succ) { |
| __ Fence(pred, succ); |
| expected += |
| "fence " + get_fence_type_string(pred) + ", " + get_fence_type_string(succ) + "\n"; |
| } |
| } |
| DriverStr(expected, "Fence"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FenceTso) { |
| __ FenceTso(); |
| DriverStr("fence.tso", "FenceTso"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FenceI) { |
| __ FenceI(); |
| DriverStr("fence.i", "FenceI"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Mul) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Mul, "mul {reg1}, {reg2}, {reg3}"), "Mul"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Mulh) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Mulh, "mulh {reg1}, {reg2}, {reg3}"), "Mulh"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Mulhsu) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Mulhsu, "mulhsu {reg1}, {reg2}, {reg3}"), "Mulhsu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Mulhu) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Mulhu, "mulhu {reg1}, {reg2}, {reg3}"), "Mulhu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Div) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Div, "div {reg1}, {reg2}, {reg3}"), "Div"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Divu) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Divu, "divu {reg1}, {reg2}, {reg3}"), "Divu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Rem) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Rem, "rem {reg1}, {reg2}, {reg3}"), "Rem"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Remu) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Remu, "remu {reg1}, {reg2}, {reg3}"), "Remu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Mulw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Mulw, "mulw {reg1}, {reg2}, {reg3}"), "Mulw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Divw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Divw, "divw {reg1}, {reg2}, {reg3}"), "Divw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Divuw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Divuw, "divuw {reg1}, {reg2}, {reg3}"), "Divuw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Remw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Remw, "remw {reg1}, {reg2}, {reg3}"), "Remw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Remuw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Remuw, "remuw {reg1}, {reg2}, {reg3}"), "Remuw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, LrW) { |
| auto invalid_aqrl = [](AqRl aqrl) { return aqrl == AqRl::kRelease; }; |
| DriverStr(RepeatRRAqRl(&Riscv64Assembler::LrW, "lr.w{aqrl} {reg1}, ({reg2})", invalid_aqrl), |
| "LrW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, LrD) { |
| auto invalid_aqrl = [](AqRl aqrl) { return aqrl == AqRl::kRelease; }; |
| DriverStr(RepeatRRAqRl(&Riscv64Assembler::LrD, "lr.d{aqrl} {reg1}, ({reg2})", invalid_aqrl), |
| "LrD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, ScW) { |
| auto invalid_aqrl = [](AqRl aqrl) { return aqrl == AqRl::kAcquire; }; |
| DriverStr( |
| RepeatRRRAqRl(&Riscv64Assembler::ScW, "sc.w{aqrl} {reg1}, {reg2}, ({reg3})", invalid_aqrl), |
| "ScW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, ScD) { |
| auto invalid_aqrl = [](AqRl aqrl) { return aqrl == AqRl::kAcquire; }; |
| DriverStr( |
| RepeatRRRAqRl(&Riscv64Assembler::ScD, "sc.d{aqrl} {reg1}, {reg2}, ({reg3})", invalid_aqrl), |
| "ScD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoSwapW) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoSwapW, "amoswap.w{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoSwapW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoSwapD) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoSwapD, "amoswap.d{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoSwapD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoAddW) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoAddW, "amoadd.w{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoAddW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoAddD) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoAddD, "amoadd.d{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoAddD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoXorW) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoXorW, "amoxor.w{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoXorW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoXorD) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoXorD, "amoxor.d{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoXorD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoAndW) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoAndW, "amoand.w{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoAndW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoAndD) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoAndD, "amoand.d{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoAndD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoOrW) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoOrW, "amoor.w{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoOrW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoOrD) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoOrD, "amoor.d{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoOrD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoMinW) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoMinW, "amomin.w{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoMinW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoMinD) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoMinD, "amomin.d{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoMinD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoMaxW) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoMaxW, "amomax.w{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoMaxW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoMaxD) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoMaxD, "amomax.d{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoMaxD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoMinuW) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoMinuW, "amominu.w{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoMinuW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoMinuD) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoMinuD, "amominu.d{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoMinuD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoMaxuW) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoMaxuW, "amomaxu.w{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoMaxuW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AmoMaxuD) { |
| DriverStr(RepeatRRRAqRl(&Riscv64Assembler::AmoMaxuD, "amomaxu.d{aqrl} {reg1}, {reg2}, ({reg3})"), |
| "AmoMaxuD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrrw) { |
| DriverStr(RepeatCsrrX(&Riscv64Assembler::Csrrw, "csrrw {reg1}, {csr}, {reg2}"), "Csrrw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrrs) { |
| DriverStr(RepeatCsrrX(&Riscv64Assembler::Csrrs, "csrrs {reg1}, {csr}, {reg2}"), "Csrrs"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrrc) { |
| DriverStr(RepeatCsrrX(&Riscv64Assembler::Csrrc, "csrrc {reg1}, {csr}, {reg2}"), "Csrrc"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrrwi) { |
| DriverStr(RepeatCsrrXi(&Riscv64Assembler::Csrrwi, "csrrwi {reg}, {csr}, {uimm}"), "Csrrwi"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrrsi) { |
| DriverStr(RepeatCsrrXi(&Riscv64Assembler::Csrrsi, "csrrsi {reg}, {csr}, {uimm}"), "Csrrsi"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrrci) { |
| DriverStr(RepeatCsrrXi(&Riscv64Assembler::Csrrci, "csrrci {reg}, {csr}, {uimm}"), "Csrrci"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FLw) { |
| DriverStr(RepeatFRIb(&Riscv64Assembler::FLw, -12, "flw {reg1}, {imm}({reg2})"), "FLw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FLd) { |
| DriverStr(RepeatFRIb(&Riscv64Assembler::FLd, -12, "fld {reg1}, {imm}({reg2})"), "FLw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSw) { |
| DriverStr(RepeatFRIb(&Riscv64Assembler::FSw, 2, "fsw {reg1}, {imm}({reg2})"), "FSw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSd) { |
| DriverStr(RepeatFRIb(&Riscv64Assembler::FSd, 2, "fsd {reg1}, {imm}({reg2})"), "FSd"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMAddS) { |
| DriverStr(RepeatFFFFRoundingMode(&Riscv64Assembler::FMAddS, |
| "fmadd.s {reg1}, {reg2}, {reg3}, {reg4}, {rm}"), "FMAddS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMAddS_Default) { |
| DriverStr(RepeatFFFF(&Riscv64Assembler::FMAddS, "fmadd.s {reg1}, {reg2}, {reg3}, {reg4}"), |
| "FMAddS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMAddD) { |
| DriverStr(RepeatFFFFRoundingMode(&Riscv64Assembler::FMAddD, |
| "fmadd.d {reg1}, {reg2}, {reg3}, {reg4}, {rm}"), "FMAddD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMAddD_Default) { |
| DriverStr(RepeatFFFF(&Riscv64Assembler::FMAddD, "fmadd.d {reg1}, {reg2}, {reg3}, {reg4}"), |
| "FMAddD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMSubS) { |
| DriverStr(RepeatFFFFRoundingMode(&Riscv64Assembler::FMSubS, |
| "fmsub.s {reg1}, {reg2}, {reg3}, {reg4}, {rm}"), "FMSubS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMSubS_Default) { |
| DriverStr(RepeatFFFF(&Riscv64Assembler::FMSubS, "fmsub.s {reg1}, {reg2}, {reg3}, {reg4}"), |
| "FMSubS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMSubD) { |
| DriverStr(RepeatFFFFRoundingMode(&Riscv64Assembler::FMSubD, |
| "fmsub.d {reg1}, {reg2}, {reg3}, {reg4}, {rm}"), "FMSubD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMSubD_Default) { |
| DriverStr(RepeatFFFF(&Riscv64Assembler::FMSubD, "fmsub.d {reg1}, {reg2}, {reg3}, {reg4}"), |
| "FMSubD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FNMSubS) { |
| DriverStr(RepeatFFFFRoundingMode(&Riscv64Assembler::FNMSubS, |
| "fnmsub.s {reg1}, {reg2}, {reg3}, {reg4}, {rm}"), "FNMSubS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FNMSubS_Default) { |
| DriverStr(RepeatFFFF(&Riscv64Assembler::FNMSubS, "fnmsub.s {reg1}, {reg2}, {reg3}, {reg4}"), |
| "FNMSubS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FNMSubD) { |
| DriverStr(RepeatFFFFRoundingMode(&Riscv64Assembler::FNMSubD, |
| "fnmsub.d {reg1}, {reg2}, {reg3}, {reg4}, {rm}"), "FNMSubD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FNMSubD_Default) { |
| DriverStr(RepeatFFFF(&Riscv64Assembler::FNMSubD, "fnmsub.d {reg1}, {reg2}, {reg3}, {reg4}"), |
| "FNMSubD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FNMAddS) { |
| DriverStr(RepeatFFFFRoundingMode(&Riscv64Assembler::FNMAddS, |
| "fnmadd.s {reg1}, {reg2}, {reg3}, {reg4}, {rm}"), "FNMAddS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FNMAddS_Default) { |
| DriverStr(RepeatFFFF(&Riscv64Assembler::FNMAddS, "fnmadd.s {reg1}, {reg2}, {reg3}, {reg4}"), |
| "FNMAddS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FNMAddD) { |
| DriverStr(RepeatFFFFRoundingMode(&Riscv64Assembler::FNMAddD, |
| "fnmadd.d {reg1}, {reg2}, {reg3}, {reg4}, {rm}"), "FNMAddD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FNMAddD_Default) { |
| DriverStr(RepeatFFFF(&Riscv64Assembler::FNMAddD, "fnmadd.d {reg1}, {reg2}, {reg3}, {reg4}"), |
| "FNMAddD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FAddS) { |
| DriverStr(RepeatFFFRoundingMode(&Riscv64Assembler::FAddS, "fadd.s {reg1}, {reg2}, {reg3}, {rm}"), |
| "FAddS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FAddS_Default) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FAddS, "fadd.s {reg1}, {reg2}, {reg3}"), "FAddS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FAddD) { |
| DriverStr(RepeatFFFRoundingMode(&Riscv64Assembler::FAddD, "fadd.d {reg1}, {reg2}, {reg3}, {rm}"), |
| "FAddD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FAddD_Default) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FAddD, "fadd.d {reg1}, {reg2}, {reg3}"), "FAddD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSubS) { |
| DriverStr(RepeatFFFRoundingMode(&Riscv64Assembler::FSubS, "fsub.s {reg1}, {reg2}, {reg3}, {rm}"), |
| "FSubS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSubS_Default) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FSubS, "fsub.s {reg1}, {reg2}, {reg3}"), "FSubS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSubD) { |
| DriverStr(RepeatFFFRoundingMode(&Riscv64Assembler::FSubD, "fsub.d {reg1}, {reg2}, {reg3}, {rm}"), |
| "FSubD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSubD_Default) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FSubD, "fsub.d {reg1}, {reg2}, {reg3}"), "FSubD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMulS) { |
| DriverStr(RepeatFFFRoundingMode(&Riscv64Assembler::FMulS, "fmul.s {reg1}, {reg2}, {reg3}, {rm}"), |
| "FMulS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMulS_Default) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FMulS, "fmul.s {reg1}, {reg2}, {reg3}"), "FMulS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMulD) { |
| DriverStr(RepeatFFFRoundingMode(&Riscv64Assembler::FMulD, "fmul.d {reg1}, {reg2}, {reg3}, {rm}"), |
| "FMulD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMulD_Default) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FMulD, "fmul.d {reg1}, {reg2}, {reg3}"), "FMulD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FDivS) { |
| DriverStr(RepeatFFFRoundingMode(&Riscv64Assembler::FDivS, "fdiv.s {reg1}, {reg2}, {reg3}, {rm}"), |
| "FDivS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FDivS_Default) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FDivS, "fdiv.s {reg1}, {reg2}, {reg3}"), "FDivS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FDivD) { |
| DriverStr(RepeatFFFRoundingMode(&Riscv64Assembler::FDivD, "fdiv.d {reg1}, {reg2}, {reg3}, {rm}"), |
| "FDivD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FDivD_Default) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FDivD, "fdiv.d {reg1}, {reg2}, {reg3}"), "FDivD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSqrtS) { |
| DriverStr(RepeatFFRoundingMode(&Riscv64Assembler::FSqrtS, "fsqrt.s {reg1}, {reg2}, {rm}"), |
| "FSqrtS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSqrtS_Default) { |
| DriverStr(RepeatFF(&Riscv64Assembler::FSqrtS, "fsqrt.s {reg1}, {reg2}"), "FSqrtS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSqrtD) { |
| DriverStr(RepeatFFRoundingMode(&Riscv64Assembler::FSqrtD, "fsqrt.d {reg1}, {reg2}, {rm}"), |
| "FSqrtD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSqrtD_Default) { |
| DriverStr(RepeatFF(&Riscv64Assembler::FSqrtD, "fsqrt.d {reg1}, {reg2}"), "FSqrtD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSgnjS) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FSgnjS, "fsgnj.s {reg1}, {reg2}, {reg3}"), "FSgnjS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSgnjD) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FSgnjD, "fsgnj.d {reg1}, {reg2}, {reg3}"), "FSgnjD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSgnjnS) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FSgnjnS, "fsgnjn.s {reg1}, {reg2}, {reg3}"), "FSgnjnS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSgnjnD) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FSgnjnD, "fsgnjn.d {reg1}, {reg2}, {reg3}"), "FSgnjnD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSgnjxS) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FSgnjxS, "fsgnjx.s {reg1}, {reg2}, {reg3}"), "FSgnjxS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FSgnjxD) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FSgnjxD, "fsgnjx.d {reg1}, {reg2}, {reg3}"), "FSgnjxD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMinS) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FMinS, "fmin.s {reg1}, {reg2}, {reg3}"), "FMinS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMinD) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FMinD, "fmin.d {reg1}, {reg2}, {reg3}"), "FMinD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMaxS) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FMaxS, "fmax.s {reg1}, {reg2}, {reg3}"), "FMaxS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMaxD) { |
| DriverStr(RepeatFFF(&Riscv64Assembler::FMaxD, "fmax.d {reg1}, {reg2}, {reg3}"), "FMaxD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtSD) { |
| DriverStr(RepeatFFRoundingMode(&Riscv64Assembler::FCvtSD, "fcvt.s.d {reg1}, {reg2}, {rm}"), |
| "FCvtSD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtSD_Default) { |
| DriverStr(RepeatFF(&Riscv64Assembler::FCvtSD, "fcvt.s.d {reg1}, {reg2}"), "FCvtSD_Default"); |
| } |
| |
| // This conversion is lossless, so the rounding mode is meaningless and the assembler we're |
| // testing against does not even accept the rounding mode argument, so this test is disabled. |
| TEST_F(AssemblerRISCV64Test, DISABLED_FCvtDS) { |
| DriverStr(RepeatFFRoundingMode(&Riscv64Assembler::FCvtDS, "fcvt.d.s {reg1}, {reg2}, {rm}"), |
| "FCvtDS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtDS_Default) { |
| DriverStr(RepeatFF(&Riscv64Assembler::FCvtDS, "fcvt.d.s {reg1}, {reg2}"), "FCvtDS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FEqS) { |
| DriverStr(RepeatRFF(&Riscv64Assembler::FEqS, "feq.s {reg1}, {reg2}, {reg3}"), "FEqS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FEqD) { |
| DriverStr(RepeatRFF(&Riscv64Assembler::FEqD, "feq.d {reg1}, {reg2}, {reg3}"), "FEqD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FLtS) { |
| DriverStr(RepeatRFF(&Riscv64Assembler::FLtS, "flt.s {reg1}, {reg2}, {reg3}"), "FLtS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FLtD) { |
| DriverStr(RepeatRFF(&Riscv64Assembler::FLtD, "flt.d {reg1}, {reg2}, {reg3}"), "FLtD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FLeS) { |
| DriverStr(RepeatRFF(&Riscv64Assembler::FLeS, "fle.s {reg1}, {reg2}, {reg3}"), "FLeS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FLeD) { |
| DriverStr(RepeatRFF(&Riscv64Assembler::FLeD, "fle.d {reg1}, {reg2}, {reg3}"), "FLeD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtWS) { |
| DriverStr(RepeatrFRoundingMode(&Riscv64Assembler::FCvtWS, "fcvt.w.s {reg1}, {reg2}, {rm}"), |
| "FCvtWS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtWS_Default) { |
| DriverStr(RepeatrF(&Riscv64Assembler::FCvtWS, "fcvt.w.s {reg1}, {reg2}"), "FCvtWS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtWD) { |
| DriverStr(RepeatrFRoundingMode(&Riscv64Assembler::FCvtWD, "fcvt.w.d {reg1}, {reg2}, {rm}"), |
| "FCvtWD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtWD_Default) { |
| DriverStr(RepeatrF(&Riscv64Assembler::FCvtWD, "fcvt.w.d {reg1}, {reg2}"), "FCvtWD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtWuS) { |
| DriverStr(RepeatrFRoundingMode(&Riscv64Assembler::FCvtWuS, "fcvt.wu.s {reg1}, {reg2}, {rm}"), |
| "FCvtWuS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtWuS_Default) { |
| DriverStr(RepeatrF(&Riscv64Assembler::FCvtWuS, "fcvt.wu.s {reg1}, {reg2}"), "FCvtWuS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtWuD) { |
| DriverStr(RepeatrFRoundingMode(&Riscv64Assembler::FCvtWuD, "fcvt.wu.d {reg1}, {reg2}, {rm}"), |
| "FCvtWuD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtWuD_Default) { |
| DriverStr(RepeatrF(&Riscv64Assembler::FCvtWuD, "fcvt.wu.d {reg1}, {reg2}"), "FCvtWuD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtLS) { |
| DriverStr(RepeatrFRoundingMode(&Riscv64Assembler::FCvtLS, "fcvt.l.s {reg1}, {reg2}, {rm}"), |
| "FCvtLS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtLS_Default) { |
| DriverStr(RepeatrF(&Riscv64Assembler::FCvtLS, "fcvt.l.s {reg1}, {reg2}"), "FCvtLS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtLD) { |
| DriverStr(RepeatrFRoundingMode(&Riscv64Assembler::FCvtLD, "fcvt.l.d {reg1}, {reg2}, {rm}"), |
| "FCvtLD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtLD_Default) { |
| DriverStr(RepeatrF(&Riscv64Assembler::FCvtLD, "fcvt.l.d {reg1}, {reg2}"), "FCvtLD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtLuS) { |
| DriverStr(RepeatrFRoundingMode(&Riscv64Assembler::FCvtLuS, "fcvt.lu.s {reg1}, {reg2}, {rm}"), |
| "FCvtLuS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtLuS_Default) { |
| DriverStr(RepeatrF(&Riscv64Assembler::FCvtLuS, "fcvt.lu.s {reg1}, {reg2}"), "FCvtLuS_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtLuD) { |
| DriverStr(RepeatrFRoundingMode(&Riscv64Assembler::FCvtLuD, "fcvt.lu.d {reg1}, {reg2}, {rm}"), |
| "FCvtLuD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtLuD_Default) { |
| DriverStr(RepeatrF(&Riscv64Assembler::FCvtLuD, "fcvt.lu.d {reg1}, {reg2}"), "FCvtLuD_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtSW) { |
| DriverStr(RepeatFrRoundingMode(&Riscv64Assembler::FCvtSW, "fcvt.s.w {reg1}, {reg2}, {rm}"), |
| "FCvtSW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtSW_Default) { |
| DriverStr(RepeatFr(&Riscv64Assembler::FCvtSW, "fcvt.s.w {reg1}, {reg2}"), "FCvtSW_Default"); |
| } |
| |
| // This conversion is lossless, so the rounding mode is meaningless and the assembler we're |
| // testing against does not even accept the rounding mode argument, so this test is disabled. |
| TEST_F(AssemblerRISCV64Test, DISABLED_FCvtDW) { |
| DriverStr(RepeatFrRoundingMode(&Riscv64Assembler::FCvtDW, "fcvt.d.w {reg1}, {reg2}, {rm}"), |
| "FCvtDW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtDW_Default) { |
| DriverStr(RepeatFr(&Riscv64Assembler::FCvtDW, "fcvt.d.w {reg1}, {reg2}"), "FCvtDW_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtSWu) { |
| DriverStr(RepeatFrRoundingMode(&Riscv64Assembler::FCvtSWu, "fcvt.s.wu {reg1}, {reg2}, {rm}"), |
| "FCvtSWu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtSWu_Default) { |
| DriverStr(RepeatFr(&Riscv64Assembler::FCvtSWu, "fcvt.s.wu {reg1}, {reg2}"), "FCvtSWu_Default"); |
| } |
| |
| // This conversion is lossless, so the rounding mode is meaningless and the assembler we're |
| // testing against does not even accept the rounding mode argument, so this test is disabled. |
| TEST_F(AssemblerRISCV64Test, DISABLED_FCvtDWu) { |
| DriverStr(RepeatFrRoundingMode(&Riscv64Assembler::FCvtDWu, "fcvt.d.wu {reg1}, {reg2}, {rm}"), |
| "FCvtDWu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtDWu_Default) { |
| DriverStr(RepeatFr(&Riscv64Assembler::FCvtDWu, "fcvt.d.wu {reg1}, {reg2}"), "FCvtDWu_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtSL) { |
| DriverStr(RepeatFrRoundingMode(&Riscv64Assembler::FCvtSL, "fcvt.s.l {reg1}, {reg2}, {rm}"), |
| "FCvtSL"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtSL_Default) { |
| DriverStr(RepeatFr(&Riscv64Assembler::FCvtSL, "fcvt.s.l {reg1}, {reg2}"), "FCvtSL_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtDL) { |
| DriverStr(RepeatFrRoundingMode(&Riscv64Assembler::FCvtDL, "fcvt.d.l {reg1}, {reg2}, {rm}"), |
| "FCvtDL"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtDL_Default) { |
| DriverStr(RepeatFr(&Riscv64Assembler::FCvtDL, "fcvt.d.l {reg1}, {reg2}"), "FCvtDL_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtSLu) { |
| DriverStr(RepeatFrRoundingMode(&Riscv64Assembler::FCvtSLu, "fcvt.s.lu {reg1}, {reg2}, {rm}"), |
| "FCvtSLu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtSLu_Default) { |
| DriverStr(RepeatFr(&Riscv64Assembler::FCvtSLu, "fcvt.s.lu {reg1}, {reg2}"), "FCvtSLu_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtDLu) { |
| DriverStr(RepeatFrRoundingMode(&Riscv64Assembler::FCvtDLu, "fcvt.d.lu {reg1}, {reg2}, {rm}"), |
| "FCvtDLu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FCvtDLu_Default) { |
| DriverStr(RepeatFr(&Riscv64Assembler::FCvtDLu, "fcvt.d.lu {reg1}, {reg2}"), "FCvtDLu_Default"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMvXW) { |
| DriverStr(RepeatRF(&Riscv64Assembler::FMvXW, "fmv.x.w {reg1}, {reg2}"), "FMvXW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMvXD) { |
| DriverStr(RepeatRF(&Riscv64Assembler::FMvXD, "fmv.x.d {reg1}, {reg2}"), "FMvXD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMvWX) { |
| DriverStr(RepeatFR(&Riscv64Assembler::FMvWX, "fmv.w.x {reg1}, {reg2}"), "FMvWX"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMvDX) { |
| DriverStr(RepeatFR(&Riscv64Assembler::FMvDX, "fmv.d.x {reg1}, {reg2}"), "FMvDX"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FClassS) { |
| DriverStr(RepeatRF(&Riscv64Assembler::FClassS, "fclass.s {reg1}, {reg2}"), "FClassS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FClassD) { |
| DriverStr(RepeatrF(&Riscv64Assembler::FClassD, "fclass.d {reg1}, {reg2}"), "FClassD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AddUw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::AddUw, "add.uw {reg1}, {reg2}, {reg3}"), "AddUw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sh1Add) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Sh1Add, "sh1add {reg1}, {reg2}, {reg3}"), "Sh1Add"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sh1AddUw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Sh1AddUw, "sh1add.uw {reg1}, {reg2}, {reg3}"), "Sh1AddUw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sh2Add) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Sh2Add, "sh2add {reg1}, {reg2}, {reg3}"), "Sh2Add"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sh2AddUw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Sh2AddUw, "sh2add.uw {reg1}, {reg2}, {reg3}"), "Sh2AddUw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sh3Add) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Sh3Add, "sh3add {reg1}, {reg2}, {reg3}"), "Sh3Add"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sh3AddUw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Sh3AddUw, "sh3add.uw {reg1}, {reg2}, {reg3}"), "Sh3AddUw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, SlliUw) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::SlliUw, 6, "slli.uw {reg1}, {reg2}, {imm}"), "SlliUw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Andn) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Andn, "andn {reg1}, {reg2}, {reg3}"), "Andn"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Orn) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Orn, "orn {reg1}, {reg2}, {reg3}"), "Orn"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Xnor) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Xnor, "xnor {reg1}, {reg2}, {reg3}"), "Xnor"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Clz) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Clz, "clz {reg1}, {reg2}"), "Clz"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Clzw) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Clzw, "clzw {reg1}, {reg2}"), "Clzw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Ctz) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Ctz, "ctz {reg1}, {reg2}"), "Ctz"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Ctzw) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Ctzw, "ctzw {reg1}, {reg2}"), "Ctzw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Cpop) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Cpop, "cpop {reg1}, {reg2}"), "Cpop"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Cpopw) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Cpopw, "cpopw {reg1}, {reg2}"), "Cpopw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Min) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Min, "min {reg1}, {reg2}, {reg3}"), "Min"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Minu) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Minu, "minu {reg1}, {reg2}, {reg3}"), "Minu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Max) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Max, "max {reg1}, {reg2}, {reg3}"), "Max"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Maxu) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Maxu, "maxu {reg1}, {reg2}, {reg3}"), "Maxu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Rol) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Rol, "rol {reg1}, {reg2}, {reg3}"), "Rol"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Rolw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Rolw, "rolw {reg1}, {reg2}, {reg3}"), "Rolw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Ror) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Ror, "ror {reg1}, {reg2}, {reg3}"), "Ror"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Rorw) { |
| DriverStr(RepeatRRR(&Riscv64Assembler::Rorw, "rorw {reg1}, {reg2}, {reg3}"), "Rorw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Rori) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Rori, 6, "rori {reg1}, {reg2}, {imm}"), "Rori"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Roriw) { |
| DriverStr(RepeatRRIb(&Riscv64Assembler::Roriw, 5, "roriw {reg1}, {reg2}, {imm}"), "Roriw"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, OrcB) { |
| DriverStr(RepeatRR(&Riscv64Assembler::OrcB, "orc.b {reg1}, {reg2}"), "OrcB"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Rev8) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Rev8, "rev8 {reg1}, {reg2}"), "Rev8"); |
| } |
| |
| // Pseudo instructions. |
| TEST_F(AssemblerRISCV64Test, Nop) { |
| __ Nop(); |
| DriverStr("addi zero,zero,0", "Nop"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Li) { |
| SetUseSimpleMarch(true); |
| TestLoadConst64("Li", |
| /*can_use_tmp=*/ false, |
| [&](XRegister rd, int64_t value) { __ Li(rd, value); }); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Mv) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Mv, "addi {reg1}, {reg2}, 0"), "Mv"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Not) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Not, "xori {reg1}, {reg2}, -1"), "Not"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Neg) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Neg, "sub {reg1}, x0, {reg2}"), "Neg"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, NegW) { |
| DriverStr(RepeatRR(&Riscv64Assembler::NegW, "subw {reg1}, x0, {reg2}"), "Neg"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, SextB) { |
| // Note: SEXT.B from the Zbb extension is not supported. |
| DriverStr(RepeatRR(&Riscv64Assembler::SextB, |
| "slli {reg1}, {reg2}, 56\n" |
| "srai {reg1}, {reg1}, 56"), |
| "SextB"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, SextH) { |
| // Note: SEXT.H from the Zbb extension is not supported. |
| DriverStr(RepeatRR(&Riscv64Assembler::SextH, |
| "slli {reg1}, {reg2}, 48\n" |
| "srai {reg1}, {reg1}, 48"), |
| "SextH"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, SextW) { |
| DriverStr(RepeatRR(&Riscv64Assembler::SextW, "addiw {reg1}, {reg2}, 0\n"), "SextW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, ZextB) { |
| DriverStr(RepeatRR(&Riscv64Assembler::ZextB, "andi {reg1}, {reg2}, 255"), "ZextB"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, ZextH) { |
| // Note: ZEXT.H from the Zbb extension is not supported. |
| DriverStr(RepeatRR(&Riscv64Assembler::ZextH, |
| "slli {reg1}, {reg2}, 48\n" |
| "srli {reg1}, {reg1}, 48"), |
| "SextH"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, ZextW) { |
| DriverStr(RepeatRR(&Riscv64Assembler::ZextW, |
| "slli {reg1}, {reg2}, 32\n" |
| "srli {reg1}, {reg1}, 32"), |
| "ZextW"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Seqz) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Seqz, "sltiu {reg1}, {reg2}, 1\n"), "Seqz"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Snez) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Snez, "sltu {reg1}, zero, {reg2}\n"), "Snez"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sltz) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Sltz, "slt {reg1}, {reg2}, zero\n"), "Sltz"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Sgtz) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Sgtz, "slt {reg1}, zero, {reg2}\n"), "Sgtz"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMvS) { |
| DriverStr(RepeatFF(&Riscv64Assembler::FMvS, "fsgnj.s {reg1}, {reg2}, {reg2}\n"), "FMvS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FAbsS) { |
| DriverStr(RepeatFF(&Riscv64Assembler::FAbsS, "fsgnjx.s {reg1}, {reg2}, {reg2}\n"), "FAbsS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FNegS) { |
| DriverStr(RepeatFF(&Riscv64Assembler::FNegS, "fsgnjn.s {reg1}, {reg2}, {reg2}\n"), "FNegS"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FMvD) { |
| DriverStr(RepeatFF(&Riscv64Assembler::FMvD, "fsgnj.d {reg1}, {reg2}, {reg2}\n"), "FMvD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FAbsD) { |
| DriverStr(RepeatFF(&Riscv64Assembler::FAbsD, "fsgnjx.d {reg1}, {reg2}, {reg2}\n"), "FAbsD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FNegD) { |
| DriverStr(RepeatFF(&Riscv64Assembler::FNegD, "fsgnjn.d {reg1}, {reg2}, {reg2}\n"), "FNegD"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Beqz) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRIbS(&Riscv64Assembler::Beqz, -11, 2, "beq {reg}, zero, {imm}\n"), "Beqz"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Bnez) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRIbS(&Riscv64Assembler::Bnez, -11, 2, "bne {reg}, zero, {imm}\n"), "Bnez"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Blez) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRIbS(&Riscv64Assembler::Blez, -11, 2, "bge zero, {reg}, {imm}\n"), "Blez"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Bgez) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRIbS(&Riscv64Assembler::Bgez, -11, 2, "bge {reg}, zero, {imm}\n"), "Bgez"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Bltz) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRIbS(&Riscv64Assembler::Bltz, -11, 2, "blt {reg}, zero, {imm}\n"), "Bltz"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Bgtz) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRIbS(&Riscv64Assembler::Bgtz, -11, 2, "blt zero, {reg}, {imm}\n"), "Bgtz"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Bgt) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRRIbS(&Riscv64Assembler::Bgt, -11, 2, "blt {reg2}, {reg1}, {imm}\n"), "Bgt"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Ble) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRRIbS(&Riscv64Assembler::Ble, -11, 2, "bge {reg2}, {reg1}, {imm}\n"), "Bge"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Bgtu) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRRIbS(&Riscv64Assembler::Bgtu, -11, 2, "bltu {reg2}, {reg1}, {imm}\n"), "Bgtu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Bleu) { |
| // TODO(riscv64): Change "-11, 2" to "-12, 1" for "C" Standard Extension. |
| DriverStr(RepeatRRIbS(&Riscv64Assembler::Bleu, -11, 2, "bgeu {reg2}, {reg1}, {imm}\n"), "Bgeu"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, J) { |
| // TODO(riscv64): Change "-19, 2" to "-20, 1" for "C" Standard Extension. |
| DriverStr(RepeatIbS<int32_t>(&Riscv64Assembler::J, -19, 2, "j {imm}\n"), "J"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JalRA) { |
| // TODO(riscv64): Change "-19, 2" to "-20, 1" for "C" Standard Extension. |
| DriverStr(RepeatIbS<int32_t>(&Riscv64Assembler::Jal, -19, 2, "jal {imm}\n"), "JalRA"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Jr) { |
| DriverStr(RepeatR(&Riscv64Assembler::Jr, "jr {reg}\n"), "Jr"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JalrRA) { |
| DriverStr(RepeatR(&Riscv64Assembler::Jalr, "jalr {reg}\n"), "JalrRA"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Jalr0) { |
| DriverStr(RepeatRR(&Riscv64Assembler::Jalr, "jalr {reg1}, {reg2}\n"), "Jalr0"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Ret) { |
| __ Ret(); |
| DriverStr("ret\n", "Ret"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, RdCycle) { |
| DriverStr(RepeatR(&Riscv64Assembler::RdCycle, "rdcycle {reg}\n"), "RdCycle"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, RdTime) { |
| DriverStr(RepeatR(&Riscv64Assembler::RdTime, "rdtime {reg}\n"), "RdTime"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, RdInstret) { |
| DriverStr(RepeatR(&Riscv64Assembler::RdInstret, "rdinstret {reg}\n"), "RdInstret"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrr) { |
| TestCsrrXMacro( |
| "Csrr", "csrr {reg}, {csr}", [&](uint32_t csr, XRegister rd) { __ Csrr(rd, csr); }); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrw) { |
| TestCsrrXMacro( |
| "Csrw", "csrw {csr}, {reg}", [&](uint32_t csr, XRegister rs) { __ Csrw(csr, rs); }); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrs) { |
| TestCsrrXMacro( |
| "Csrs", "csrs {csr}, {reg}", [&](uint32_t csr, XRegister rs) { __ Csrs(csr, rs); }); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrc) { |
| TestCsrrXMacro( |
| "Csrc", "csrc {csr}, {reg}", [&](uint32_t csr, XRegister rs) { __ Csrc(csr, rs); }); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrwi) { |
| TestCsrrXiMacro( |
| "Csrwi", "csrwi {csr}, {uimm}", [&](uint32_t csr, uint32_t uimm) { __ Csrwi(csr, uimm); }); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrsi) { |
| TestCsrrXiMacro( |
| "Csrsi", "csrsi {csr}, {uimm}", [&](uint32_t csr, uint32_t uimm) { __ Csrsi(csr, uimm); }); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Csrci) { |
| TestCsrrXiMacro( |
| "Csrci", "csrci {csr}, {uimm}", [&](uint32_t csr, uint32_t uimm) { __ Csrci(csr, uimm); }); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, LoadConst32) { |
| // `LoadConst32()` emits the same code sequences as `Li()` for 32-bit values. |
| ScratchRegisterScope srs(GetAssembler()); |
| srs.ExcludeXRegister(TMP); |
| srs.ExcludeXRegister(TMP2); |
| DriverStr(RepeatRIb(&Riscv64Assembler::LoadConst32, -32, "li {reg}, {imm}"), "LoadConst32"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, LoadConst64) { |
| SetUseSimpleMarch(true); |
| TestLoadConst64("LoadConst64", |
| /*can_use_tmp=*/ true, |
| [&](XRegister rd, int64_t value) { __ LoadConst64(rd, value); }); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AddConst32) { |
| auto emit_op = [&](XRegister rd, XRegister rs1, int64_t value) { |
| __ AddConst32(rd, rs1, dchecked_integral_cast<int32_t>(value)); |
| }; |
| TestAddConst("AddConst32", 32, /*suffix=*/ "w", emit_op); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, AddConst64) { |
| SetUseSimpleMarch(true); |
| auto emit_op = [&](XRegister rd, XRegister rs1, int64_t value) { |
| __ AddConst64(rd, rs1, value); |
| }; |
| TestAddConst("AddConst64", 64, /*suffix=*/ "", emit_op); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BcondForward3KiB) { |
| TestBcondForward("BcondForward3KiB", 3 * KB, "1", GetPrintBcond()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BcondForward3KiBBare) { |
| TestBcondForward("BcondForward3KiB", 3 * KB, "1", GetPrintBcond(), /*is_bare=*/ true); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BcondBackward3KiB) { |
| TestBcondBackward("BcondBackward3KiB", 3 * KB, "1", GetPrintBcond()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BcondBackward3KiBBare) { |
| TestBcondBackward("BcondBackward3KiB", 3 * KB, "1", GetPrintBcond(), /*is_bare=*/ true); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BcondForward5KiB) { |
| TestBcondForward("BcondForward5KiB", 5 * KB, "1", GetPrintBcondOppositeAndJ("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BcondBackward5KiB) { |
| TestBcondBackward("BcondBackward5KiB", 5 * KB, "1", GetPrintBcondOppositeAndJ("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BcondForward2MiB) { |
| TestBcondForward("BcondForward2MiB", 2 * MB, "1", GetPrintBcondOppositeAndTail("2", "3")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BcondBackward2MiB) { |
| TestBcondBackward("BcondBackward2MiB", 2 * MB, "1", GetPrintBcondOppositeAndTail("2", "3")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BeqA0A1MaxOffset13Forward) { |
| TestBeqA0A1Forward("BeqA0A1MaxOffset13Forward", |
| MaxOffset13ForwardDistance() - /*BEQ*/ 4u, |
| "1", |
| GetPrintBcond()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BeqA0A1MaxOffset13ForwardBare) { |
| TestBeqA0A1Forward("BeqA0A1MaxOffset13ForwardBare", |
| MaxOffset13ForwardDistance() - /*BEQ*/ 4u, |
| "1", |
| GetPrintBcond(), |
| /*is_bare=*/ true); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BeqA0A1MaxOffset13Backward) { |
| TestBeqA0A1Backward("BeqA0A1MaxOffset13Forward", |
| MaxOffset13BackwardDistance(), |
| "1", |
| GetPrintBcond()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BeqA0A1MaxOffset13BackwardBare) { |
| TestBeqA0A1Backward("BeqA0A1MaxOffset13ForwardBare", |
| MaxOffset13BackwardDistance(), |
| "1", |
| GetPrintBcond(), |
| /*is_bare=*/ true); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BeqA0A1OverMaxOffset13Forward) { |
| TestBeqA0A1Forward("BeqA0A1OverMaxOffset13Forward", |
| MaxOffset13ForwardDistance() - /*BEQ*/ 4u + /*Exceed max*/ 4u, |
| "1", |
| GetPrintBcondOppositeAndJ("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BeqA0A1OverMaxOffset13Backward) { |
| TestBeqA0A1Backward("BeqA0A1OverMaxOffset13Forward", |
| MaxOffset13BackwardDistance() + /*Exceed max*/ 4u, |
| "1", |
| GetPrintBcondOppositeAndJ("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BeqA0A1MaxOffset21Forward) { |
| TestBeqA0A1Forward("BeqA0A1MaxOffset21Forward", |
| MaxOffset21ForwardDistance() - /*J*/ 4u, |
| "1", |
| GetPrintBcondOppositeAndJ("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BeqA0A1MaxOffset21Backward) { |
| TestBeqA0A1Backward("BeqA0A1MaxOffset21Backward", |
| MaxOffset21BackwardDistance() - /*BNE*/ 4u, |
| "1", |
| GetPrintBcondOppositeAndJ("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BeqA0A1OverMaxOffset21Forward) { |
| TestBeqA0A1Forward("BeqA0A1OverMaxOffset21Forward", |
| MaxOffset21ForwardDistance() - /*J*/ 4u + /*Exceed max*/ 4u, |
| "1", |
| GetPrintBcondOppositeAndTail("2", "3")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BeqA0A1OverMaxOffset21Backward) { |
| TestBeqA0A1Backward("BeqA0A1OverMaxOffset21Backward", |
| MaxOffset21BackwardDistance() - /*BNE*/ 4u + /*Exceed max*/ 4u, |
| "1", |
| GetPrintBcondOppositeAndTail("2", "3")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BeqA0A1AlmostCascade) { |
| TestBeqA0A1MaybeCascade("BeqA0A1AlmostCascade", /*cascade=*/ false, GetPrintBcond()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BeqA0A1Cascade) { |
| TestBeqA0A1MaybeCascade( |
| "BeqA0A1AlmostCascade", /*cascade=*/ true, GetPrintBcondOppositeAndJ("1")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BcondElimination) { |
| Riscv64Label label; |
| __ Bind(&label); |
| __ Nop(); |
| for (XRegister reg : GetRegisters()) { |
| __ Bne(reg, reg, &label); |
| __ Blt(reg, reg, &label); |
| __ Bgt(reg, reg, &label); |
| __ Bltu(reg, reg, &label); |
| __ Bgtu(reg, reg, &label); |
| } |
| DriverStr("nop\n", "BcondElimination"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, BcondUnconditional) { |
| Riscv64Label label; |
| __ Bind(&label); |
| __ Nop(); |
| for (XRegister reg : GetRegisters()) { |
| __ Beq(reg, reg, &label); |
| __ Bge(reg, reg, &label); |
| __ Ble(reg, reg, &label); |
| __ Bleu(reg, reg, &label); |
| __ Bgeu(reg, reg, &label); |
| } |
| std::string expected = |
| "1:\n" |
| "nop\n" + |
| RepeatInsn(5u * GetRegisters().size(), "j 1b\n", []() {}); |
| DriverStr(expected, "BcondUnconditional"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JalRdForward3KiB) { |
| TestJalRdForward("JalRdForward3KiB", 3 * KB, "1", GetPrintJalRd()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JalRdForward3KiBBare) { |
| TestJalRdForward("JalRdForward3KiB", 3 * KB, "1", GetPrintJalRd(), /*is_bare=*/ true); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JalRdBackward3KiB) { |
| TestJalRdBackward("JalRdBackward3KiB", 3 * KB, "1", GetPrintJalRd()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JalRdBackward3KiBBare) { |
| TestJalRdBackward("JalRdBackward3KiB", 3 * KB, "1", GetPrintJalRd(), /*is_bare=*/ true); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JalRdForward2MiB) { |
| TestJalRdForward("JalRdForward2MiB", 2 * MB, "1", GetPrintCallRd("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JalRdBackward2MiB) { |
| TestJalRdBackward("JalRdBackward2MiB", 2 * MB, "1", GetPrintCallRd("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JForward3KiB) { |
| TestBuncondForward("JForward3KiB", 3 * KB, "1", GetEmitJ(), GetPrintJ()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JForward3KiBBare) { |
| TestBuncondForward("JForward3KiB", 3 * KB, "1", GetEmitJ(/*is_bare=*/ true), GetPrintJ()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JBackward3KiB) { |
| TestBuncondBackward("JBackward3KiB", 3 * KB, "1", GetEmitJ(), GetPrintJ()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JBackward3KiBBare) { |
| TestBuncondBackward("JBackward3KiB", 3 * KB, "1", GetEmitJ(/*is_bare=*/ true), GetPrintJ()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JForward2MiB) { |
| TestBuncondForward("JForward2MiB", 2 * MB, "1", GetEmitJ(), GetPrintTail("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JBackward2MiB) { |
| TestBuncondBackward("JBackward2MiB", 2 * MB, "1", GetEmitJ(), GetPrintTail("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JMaxOffset21Forward) { |
| TestBuncondForward("JMaxOffset21Forward", |
| MaxOffset21ForwardDistance() - /*J*/ 4u, |
| "1", |
| GetEmitJ(), |
| GetPrintJ()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JMaxOffset21ForwardBare) { |
| TestBuncondForward("JMaxOffset21Forward", |
| MaxOffset21ForwardDistance() - /*J*/ 4u, |
| "1", |
| GetEmitJ(/*is_bare=*/ true), |
| GetPrintJ()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JMaxOffset21Backward) { |
| TestBuncondBackward("JMaxOffset21Backward", |
| MaxOffset21BackwardDistance(), |
| "1", |
| GetEmitJ(), |
| GetPrintJ()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JMaxOffset21BackwardBare) { |
| TestBuncondBackward("JMaxOffset21Backward", |
| MaxOffset21BackwardDistance(), |
| "1", |
| GetEmitJ(/*is_bare=*/ true), |
| GetPrintJ()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JOverMaxOffset21Forward) { |
| TestBuncondForward("JOverMaxOffset21Forward", |
| MaxOffset21ForwardDistance() - /*J*/ 4u + /*Exceed max*/ 4u, |
| "1", |
| GetEmitJ(), |
| GetPrintTail("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JOverMaxOffset21Backward) { |
| TestBuncondBackward("JMaxOffset21Backward", |
| MaxOffset21BackwardDistance() + /*Exceed max*/ 4u, |
| "1", |
| GetEmitJ(), |
| GetPrintTail("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, CallForward3KiB) { |
| TestBuncondForward("CallForward3KiB", 3 * KB, "1", GetEmitJal(), GetPrintJal()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, CallBackward3KiB) { |
| TestBuncondBackward("CallBackward3KiB", 3 * KB, "1", GetEmitJal(), GetPrintJal()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, CallForward2MiB) { |
| TestBuncondForward("CallForward2MiB", 2 * MB, "1", GetEmitJal(), GetPrintCall("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, CallBackward2MiB) { |
| TestBuncondBackward("CallBackward2MiB", 2 * MB, "1", GetEmitJal(), GetPrintCall("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, CallMaxOffset21Forward) { |
| TestBuncondForward("CallMaxOffset21Forward", |
| MaxOffset21ForwardDistance() - /*J*/ 4u, |
| "1", |
| GetEmitJal(), |
| GetPrintJal()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, CallMaxOffset21Backward) { |
| TestBuncondBackward("CallMaxOffset21Backward", |
| MaxOffset21BackwardDistance(), |
| "1", |
| GetEmitJal(), |
| GetPrintJal()); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, CallOverMaxOffset21Forward) { |
| TestBuncondForward("CallOverMaxOffset21Forward", |
| MaxOffset21ForwardDistance() - /*J*/ 4u + /*Exceed max*/ 4u, |
| "1", |
| GetEmitJal(), |
| GetPrintCall("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, CallOverMaxOffset21Backward) { |
| TestBuncondBackward("CallMaxOffset21Backward", |
| MaxOffset21BackwardDistance() + /*Exceed max*/ 4u, |
| "1", |
| GetEmitJal(), |
| GetPrintCall("2")); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Loadb) { |
| TestLoadStoreArbitraryOffset("Loadb", "lb", &Riscv64Assembler::Loadb, /*is_store=*/ false); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Loadh) { |
| TestLoadStoreArbitraryOffset("Loadh", "lh", &Riscv64Assembler::Loadh, /*is_store=*/ false); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Loadw) { |
| TestLoadStoreArbitraryOffset("Loadw", "lw", &Riscv64Assembler::Loadw, /*is_store=*/ false); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Loadd) { |
| TestLoadStoreArbitraryOffset("Loadd", "ld", &Riscv64Assembler::Loadd, /*is_store=*/ false); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Loadbu) { |
| TestLoadStoreArbitraryOffset("Loadbu", "lbu", &Riscv64Assembler::Loadbu, /*is_store=*/ false); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Loadhu) { |
| TestLoadStoreArbitraryOffset("Loadhu", "lhu", &Riscv64Assembler::Loadhu, /*is_store=*/ false); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Loadwu) { |
| TestLoadStoreArbitraryOffset("Loadwu", "lwu", &Riscv64Assembler::Loadwu, /*is_store=*/ false); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Storeb) { |
| TestLoadStoreArbitraryOffset("Storeb", "sb", &Riscv64Assembler::Storeb, /*is_store=*/ true); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Storeh) { |
| TestLoadStoreArbitraryOffset("Storeh", "sh", &Riscv64Assembler::Storeh, /*is_store=*/ true); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Storew) { |
| TestLoadStoreArbitraryOffset("Storew", "sw", &Riscv64Assembler::Storew, /*is_store=*/ true); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Stored) { |
| TestLoadStoreArbitraryOffset("Stored", "sd", &Riscv64Assembler::Stored, /*is_store=*/ true); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FLoadw) { |
| TestFPLoadStoreArbitraryOffset("FLoadw", "flw", &Riscv64Assembler::FLoadw); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FLoadd) { |
| TestFPLoadStoreArbitraryOffset("FLoadd", "fld", &Riscv64Assembler::FLoadd); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FStorew) { |
| TestFPLoadStoreArbitraryOffset("FStorew", "fsw", &Riscv64Assembler::FStorew); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, FStored) { |
| TestFPLoadStoreArbitraryOffset("FStored", "fsd", &Riscv64Assembler::FStored); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, Unimp) { |
| __ Unimp(); |
| DriverStr("unimp\n", "Unimp"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, LoadLabelAddress) { |
| std::string expected; |
| constexpr size_t kNumLoadsForward = 4 * KB; |
| constexpr size_t kNumLoadsBackward = 4 * KB; |
| Riscv64Label label; |
| auto emit_batch = [&](size_t num_loads, const std::string& target_label) { |
| for (size_t i = 0; i != num_loads; ++i) { |
| // Cycle through non-Zero registers. |
| XRegister rd = enum_cast<XRegister>((i % (kNumberOfXRegisters - 1)) + 1); |
| DCHECK_NE(rd, Zero); |
| std::string rd_name = GetRegisterName(rd); |
| __ LoadLabelAddress(rd, &label); |
| expected += "1:\n"; |
| expected += ART_FORMAT("auipc {}, %pcrel_hi({})\n", rd_name, target_label); |
| expected += ART_FORMAT("addi {}, {}, %pcrel_lo(1b)\n", rd_name, rd_name); |
| } |
| }; |
| emit_batch(kNumLoadsForward, "2f"); |
| __ Bind(&label); |
| expected += "2:\n"; |
| emit_batch(kNumLoadsBackward, "2b"); |
| DriverStr(expected, "LoadLabelAddress"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, LoadLiteralWithPaddingForLong) { |
| TestLoadLiteral("LoadLiteralWithPaddingForLong", /*with_padding_for_long=*/ true); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, LoadLiteralWithoutPaddingForLong) { |
| TestLoadLiteral("LoadLiteralWithoutPaddingForLong", /*with_padding_for_long=*/ false); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, JumpTable) { |
| std::string expected; |
| expected += EmitNops(sizeof(uint32_t)); |
| Riscv64Label targets[4]; |
| uint32_t target_locations[4]; |
| JumpTable* jump_table = __ CreateJumpTable(ArenaVector<Riscv64Label*>( |
| {&targets[0], &targets[1], &targets[2], &targets[3]}, __ GetAllocator()->Adapter())); |
| for (size_t i : {0, 1, 2, 3}) { |
| target_locations[i] = __ CodeSize(); |
| __ Bind(&targets[i]); |
| expected += std::to_string(i) + ":\n"; |
| expected += EmitNops(sizeof(uint32_t)); |
| } |
| __ LoadLabelAddress(A0, jump_table->GetLabel()); |
| expected += "4:\n" |
| "auipc a0, %pcrel_hi(5f)\n" |
| "addi a0, a0, %pcrel_lo(4b)\n"; |
| expected += EmitNops(sizeof(uint32_t)); |
| uint32_t label5_location = __ CodeSize(); |
| auto target_offset = [&](size_t i) { |
| // Even with `-mno-relax`, clang assembler does not fully resolve `.4byte 0b - 5b` |
| // and emits a relocation, so we need to calculate target offsets ourselves. |
| return std::to_string(static_cast<int64_t>(target_locations[i] - label5_location)); |
| }; |
| expected += "5:\n" |
| ".4byte " + target_offset(0) + "\n" |
| ".4byte " + target_offset(1) + "\n" |
| ".4byte " + target_offset(2) + "\n" |
| ".4byte " + target_offset(3) + "\n"; |
| DriverStr(expected, "JumpTable"); |
| } |
| |
| TEST_F(AssemblerRISCV64Test, ScratchRegisters) { |
| ScratchRegisterScope srs(GetAssembler()); |
| ASSERT_EQ(2u, srs.AvailableXRegisters()); // Default: TMP(T6) and TMP2(T5). |
| ASSERT_EQ(1u, srs.AvailableFRegisters()); // Default: FTMP(FT11). |
| |
| XRegister tmp = srs.AllocateXRegister(); |
| EXPECT_EQ(TMP, tmp); |
| XRegister tmp2 = srs.AllocateXRegister(); |
| EXPECT_EQ(TMP2, tmp2); |
| ASSERT_EQ(0u, srs.AvailableXRegisters()); |
| |
| FRegister ftmp = srs.AllocateFRegister(); |
| EXPECT_EQ(FTMP, ftmp); |
| ASSERT_EQ(0u, srs.AvailableFRegisters()); |
| |
| // Test nesting. |
| srs.FreeXRegister(A0); |
| srs.FreeXRegister(A1); |
| srs.FreeFRegister(FA0); |
| srs.FreeFRegister(FA1); |
| ASSERT_EQ(2u, srs.AvailableXRegisters()); |
| ASSERT_EQ(2u, srs.AvailableFRegisters()); |
| { |
| ScratchRegisterScope srs2(GetAssembler()); |
| ASSERT_EQ(2u, srs2.AvailableXRegisters()); |
| ASSERT_EQ(2u, srs2.AvailableFRegisters()); |
| XRegister a1 = srs2.AllocateXRegister(); |
| EXPECT_EQ(A1, a1); |
| XRegister a0 = srs2.AllocateXRegister(); |
| EXPECT_EQ(A0, a0); |
| ASSERT_EQ(0u, srs2.AvailableXRegisters()); |
| FRegister fa1 = srs2.AllocateFRegister(); |
| EXPECT_EQ(FA1, fa1); |
| FRegister fa0 = srs2.AllocateFRegister(); |
| EXPECT_EQ(FA0, fa0); |
| ASSERT_EQ(0u, srs2.AvailableFRegisters()); |
| } |
| ASSERT_EQ(2u, srs.AvailableXRegisters()); |
| ASSERT_EQ(2u, srs.AvailableFRegisters()); |
| |
| srs.IncludeXRegister(A0); // No-op as the register was already available. |
| ASSERT_EQ(2u, srs.AvailableXRegisters()); |
| srs.IncludeFRegister(FA0); // No-op as the register was already available. |
| ASSERT_EQ(2u, srs.AvailableFRegisters()); |
| srs.IncludeXRegister(S0); |
| ASSERT_EQ(3u, srs.AvailableXRegisters()); |
| srs.IncludeFRegister(FS0); |
| ASSERT_EQ(3u, srs.AvailableFRegisters()); |
| |
| srs.ExcludeXRegister(S1); // No-op as the register was not available. |
| ASSERT_EQ(3u, srs.AvailableXRegisters()); |
| srs.ExcludeFRegister(FS1); // No-op as the register was not available. |
| ASSERT_EQ(3u, srs.AvailableFRegisters()); |
| srs.ExcludeXRegister(A0); |
| ASSERT_EQ(2u, srs.AvailableXRegisters()); |
| srs.ExcludeFRegister(FA0); |
| ASSERT_EQ(2u, srs.AvailableFRegisters()); |
| } |
| |
| #undef __ |
| |
| } // namespace riscv64 |
| } // namespace art |