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