| 1 | //===-- DynamicRegisterInfoTest.cpp ---------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "gmock/gmock.h" |
| 10 | #include "gtest/gtest.h" |
| 11 | |
| 12 | #include "lldb/Target/DynamicRegisterInfo.h" |
| 13 | #include "lldb/Utility/ArchSpec.h" |
| 14 | |
| 15 | #include <functional> |
| 16 | |
| 17 | using namespace lldb_private; |
| 18 | |
| 19 | static std::vector<uint32_t> regs_to_vector(uint32_t *regs) { |
| 20 | std::vector<uint32_t> ret; |
| 21 | if (regs) { |
| 22 | while (*regs != LLDB_INVALID_REGNUM) |
| 23 | ret.push_back(x: *regs++); |
| 24 | } |
| 25 | return ret; |
| 26 | } |
| 27 | |
| 28 | class DynamicRegisterInfoRegisterTest : public ::testing::Test { |
| 29 | protected: |
| 30 | std::vector<DynamicRegisterInfo::Register> m_regs; |
| 31 | DynamicRegisterInfo m_dyninfo; |
| 32 | |
| 33 | uint32_t AddTestRegister( |
| 34 | const char *name, const char *group, uint32_t byte_size, |
| 35 | std::function<void(const DynamicRegisterInfo::Register &)> adder, |
| 36 | std::vector<uint32_t> value_regs = {}, |
| 37 | std::vector<uint32_t> invalidate_regs = {}) { |
| 38 | DynamicRegisterInfo::Register new_reg{.name: ConstString(name), |
| 39 | .alt_name: ConstString(), |
| 40 | .set_name: ConstString(group), |
| 41 | .byte_size: byte_size, |
| 42 | LLDB_INVALID_INDEX32, |
| 43 | .encoding: lldb::eEncodingUint, |
| 44 | .format: lldb::eFormatUnsigned, |
| 45 | LLDB_INVALID_REGNUM, |
| 46 | LLDB_INVALID_REGNUM, |
| 47 | LLDB_INVALID_REGNUM, |
| 48 | .regnum_remote: static_cast<uint32_t>(m_regs.size()), |
| 49 | .value_regs: value_regs, |
| 50 | .invalidate_regs: invalidate_regs}; |
| 51 | adder(new_reg); |
| 52 | return m_regs.size() - 1; |
| 53 | } |
| 54 | |
| 55 | void ExpectInRegs(uint32_t reg_num, const char *reg_name, |
| 56 | std::vector<uint32_t> value_regs, |
| 57 | std::vector<uint32_t> invalidate_regs) { |
| 58 | ASSERT_GT(m_regs.size(), reg_num); |
| 59 | |
| 60 | const DynamicRegisterInfo::Register ® = m_regs[reg_num]; |
| 61 | ConstString expected_reg_name{reg_name}; |
| 62 | EXPECT_EQ(reg.name, expected_reg_name); |
| 63 | EXPECT_EQ(reg.value_regs, value_regs); |
| 64 | EXPECT_EQ(reg.invalidate_regs, invalidate_regs); |
| 65 | } |
| 66 | |
| 67 | void ExpectInDynInfo(uint32_t reg_num, const char *reg_name, |
| 68 | uint32_t byte_offset, |
| 69 | std::vector<uint32_t> value_regs = {}, |
| 70 | std::vector<uint32_t> invalidate_regs = {}) { |
| 71 | const RegisterInfo *reg = m_dyninfo.GetRegisterInfoAtIndex(i: reg_num); |
| 72 | ASSERT_NE(reg, nullptr); |
| 73 | EXPECT_STREQ(reg->name, reg_name); |
| 74 | EXPECT_EQ(reg->byte_offset, byte_offset); |
| 75 | EXPECT_THAT(regs_to_vector(reg->value_regs), value_regs); |
| 76 | EXPECT_THAT(regs_to_vector(reg->invalidate_regs), invalidate_regs); |
| 77 | } |
| 78 | }; |
| 79 | |
| 80 | #define EXPECT_IN_REGS(reg, ...) \ |
| 81 | { \ |
| 82 | SCOPED_TRACE("at register " #reg); \ |
| 83 | ExpectInRegs(reg, #reg, __VA_ARGS__); \ |
| 84 | } |
| 85 | |
| 86 | #define EXPECT_IN_DYNINFO(reg, ...) \ |
| 87 | { \ |
| 88 | SCOPED_TRACE("at register " #reg); \ |
| 89 | ExpectInDynInfo(reg, #reg, __VA_ARGS__); \ |
| 90 | } |
| 91 | |
| 92 | TEST_F(DynamicRegisterInfoRegisterTest, addSupplementaryRegister) { |
| 93 | // Add a base register |
| 94 | uint32_t rax = AddTestRegister( |
| 95 | name: "rax" , group: "group" , byte_size: 8, |
| 96 | adder: [this](const DynamicRegisterInfo::Register &r) { m_regs.push_back(x: r); }); |
| 97 | |
| 98 | // Add supplementary registers |
| 99 | auto suppl_adder = [this](const DynamicRegisterInfo::Register &r) { |
| 100 | addSupplementaryRegister(regs&: m_regs, new_reg_info: r); |
| 101 | }; |
| 102 | uint32_t eax = AddTestRegister(name: "eax" , group: "supplementary" , byte_size: 4, adder: suppl_adder, value_regs: {rax}); |
| 103 | uint32_t ax = AddTestRegister(name: "ax" , group: "supplementary" , byte_size: 2, adder: suppl_adder, value_regs: {rax}); |
| 104 | uint32_t ah = AddTestRegister(name: "ah" , group: "supplementary" , byte_size: 1, adder: suppl_adder, value_regs: {rax}); |
| 105 | uint32_t al = AddTestRegister(name: "al" , group: "supplementary" , byte_size: 1, adder: suppl_adder, value_regs: {rax}); |
| 106 | m_regs[ah].value_reg_offset = 1; |
| 107 | |
| 108 | EXPECT_IN_REGS(rax, {}, {eax, ax, ah, al}); |
| 109 | EXPECT_IN_REGS(eax, {rax}, {rax, ax, ah, al}); |
| 110 | EXPECT_IN_REGS(ax, {rax}, {rax, eax, ah, al}); |
| 111 | EXPECT_IN_REGS(ah, {rax}, {rax, eax, ax, al}); |
| 112 | EXPECT_IN_REGS(al, {rax}, {rax, eax, ax, ah}); |
| 113 | |
| 114 | EXPECT_EQ(m_dyninfo.SetRegisterInfo(std::move(m_regs), ArchSpec()), |
| 115 | m_regs.size()); |
| 116 | EXPECT_IN_DYNINFO(rax, 0, {}, {eax, ax, ah, al}); |
| 117 | EXPECT_IN_DYNINFO(eax, 0, {rax}, {rax, ax, ah, al}); |
| 118 | EXPECT_IN_DYNINFO(ax, 0, {rax}, {rax, eax, ah, al}); |
| 119 | EXPECT_IN_DYNINFO(ah, 1, {rax}, {rax, eax, ax, al}); |
| 120 | EXPECT_IN_DYNINFO(al, 0, {rax}, {rax, eax, ax, ah}); |
| 121 | } |
| 122 | |
| 123 | TEST_F(DynamicRegisterInfoRegisterTest, SetRegisterInfo) { |
| 124 | auto adder = [this](const DynamicRegisterInfo::Register &r) { |
| 125 | m_regs.push_back(x: r); |
| 126 | }; |
| 127 | // Add regular registers |
| 128 | uint32_t b1 = AddTestRegister(name: "b1" , group: "base" , byte_size: 8, adder); |
| 129 | uint32_t b2 = AddTestRegister(name: "b2" , group: "other" , byte_size: 8, adder); |
| 130 | |
| 131 | // Add a few sub-registers |
| 132 | uint32_t s1 = AddTestRegister(name: "s1" , group: "base" , byte_size: 4, adder, value_regs: {b1}); |
| 133 | uint32_t s2 = AddTestRegister(name: "s2" , group: "other" , byte_size: 4, adder, value_regs: {b2}); |
| 134 | |
| 135 | // Add a register with invalidate_regs |
| 136 | uint32_t i1 = AddTestRegister(name: "i1" , group: "third" , byte_size: 8, adder, value_regs: {}, invalidate_regs: {b1}); |
| 137 | |
| 138 | // Add a register with indirect invalidate regs to be expanded |
| 139 | // TODO: why is it done conditionally to value_regs? |
| 140 | uint32_t i2 = AddTestRegister(name: "i2" , group: "third" , byte_size: 4, adder, value_regs: {b2}, invalidate_regs: {i1}); |
| 141 | |
| 142 | EXPECT_EQ(m_dyninfo.SetRegisterInfo(std::move(m_regs), ArchSpec()), |
| 143 | m_regs.size()); |
| 144 | EXPECT_IN_DYNINFO(b1, 0, {}, {}); |
| 145 | EXPECT_IN_DYNINFO(b2, 8, {}, {}); |
| 146 | EXPECT_IN_DYNINFO(s1, 0, {b1}, {}); |
| 147 | EXPECT_IN_DYNINFO(s2, 8, {b2}, {}); |
| 148 | EXPECT_IN_DYNINFO(i1, 16, {}, {b1}); |
| 149 | EXPECT_IN_DYNINFO(i2, 8, {b2}, {b1, i1}); |
| 150 | } |
| 151 | |