1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <gtest/gtest.h>
17 #include "mem/code_allocator.h"
18 #include "mem/pool_manager.h"
19 #include "target/aarch64/target.h"
20
21 #include "vixl_exec_module.h"
22
23 namespace panda::compiler::tests {
24
25 class EncoderArm64VixlTest : public ::testing::Test {
26 public:
EncoderArm64VixlTest()27 EncoderArm64VixlTest()
28 {
29 panda::mem::MemConfig::Initialize(64_MB, 64_MB, 64_MB, 32_MB);
30 PoolManager::Initialize();
31 allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER);
32 encoder_ = static_cast<aarch64::Aarch64Encoder *>(Encoder::Create(allocator_, Arch::AARCH64, false));
33 encoder_->InitMasm();
34 encoder_->SetRegfile(RegistersDescription::Create(allocator_, Arch::AARCH64));
35
36 exec_module_ = allocator_->New<VixlExecModule>(allocator_, nullptr);
37 }
38
~EncoderArm64VixlTest()39 ~EncoderArm64VixlTest()
40 {
41 exec_module_->~VixlExecModule();
42 Logger::Destroy();
43 encoder_->~Aarch64Encoder();
44 delete allocator_;
45 PoolManager::Finalize();
46 panda::mem::MemConfig::Finalize();
47 }
48
GetEncoder()49 auto GetEncoder()
50 {
51 return encoder_;
52 }
53
GetAllocator()54 auto GetAllocator()
55 {
56 return allocator_;
57 }
58
Dump(bool enabled)59 void Dump(bool enabled)
60 {
61 if (enabled) {
62 auto size = GetEncoder()->GetCursorOffset();
63 for (uint32_t i = 0; i < size;) {
64 i = encoder_->DisasmInstr(std::cout, i, 0);
65 std::cout << std::endl;
66 }
67 }
68 }
69
70 template <typename T>
TestPcRelativeLoad(size_t data_size,ssize_t offset,bool get_address,std::initializer_list<const char * > insts)71 void TestPcRelativeLoad(size_t data_size, ssize_t offset, bool get_address,
72 std::initializer_list<const char *> insts)
73 {
74 auto buffer = GetEncoder()->GetMasm()->GetBuffer();
75 T *data = reinterpret_cast<T *>(buffer->template GetOffsetAddress<uint8_t *>(data_size) + offset);
76 T good_value = GetGoodValue<T>();
77 T bad_value = GetBadValue<T>();
78
79 {
80 T tmp_value;
81 memcpy_s(&tmp_value, sizeof(T), data, sizeof(T));
82 ASSERT_EQ(tmp_value, bad_value);
83 }
84
85 // We use memcpy here (instead of just assigning), because the data can be aligned by 4 for 64-bit target, and
86 // asan will complain about this.
87 memcpy_s(data, sizeof(T), &good_value, sizeof(T));
88
89 GetEncoder()->SetCodeOffset(0);
90
91 buffer->Rewind(data_size);
92 Reg reg(0, TypeInfo(T(0)));
93 if (get_address) {
94 Reg addr(1, INT64_TYPE);
95 GetEncoder()->LoadPcRelative(reg, offset, addr);
96 } else {
97 GetEncoder()->LoadPcRelative(reg, offset);
98 }
99 GetEncoder()->EncodeReturn();
100
101 auto start_addr = buffer->GetOffsetAddress<const char *>(data_size);
102 exec_module_->SetInstructions(start_addr, buffer->GetEndAddress<const char *>());
103 exec_module_->Execute();
104
105 {
106 T tmp_value;
107 memcpy_s(&tmp_value, sizeof(T), data, sizeof(T));
108 ASSERT_EQ(tmp_value, good_value);
109 }
110
111 {
112 auto inst = insts.begin();
113 for (uint32_t i = data_size; i < buffer->GetCursorOffset() && inst != insts.end(); ++inst) {
114 std::stringstream ss;
115 i = encoder_->DisasmInstr(ss, i, 0);
116 auto pos = ss.str().find(*inst);
117 EXPECT_NE(pos, std::string::npos) << *inst << " not found";
118 if (pos == std::string::npos) {
119 return;
120 }
121 }
122 }
123
124 memcpy_s(data, sizeof(T), &bad_value, sizeof(T));
125
126 if (get_address) {
127 EXPECT_EQ(exec_module_->GetSimulator()->ReadXRegister(1), reinterpret_cast<int64_t>(data));
128 }
129 EXPECT_EQ(exec_module_->GetRetValue(), GetGoodValue<T>());
130 }
131
TestOffset(size_t data_size,ssize_t offset)132 void TestOffset(size_t data_size, ssize_t offset)
133 {
134 ASSERT((offset & 3) == 0);
135 if (vixl::IsInt21(offset)) {
136 TestPcRelativeLoad<uint64_t>(data_size, offset, false, {"adr", "ldr"});
137 TestPcRelativeLoad<uint64_t>(data_size, offset, true, {"adr", "ldr"});
138 TestPcRelativeLoad<uint64_t>(data_size, -offset, false, {"adr", "ldr"});
139 TestPcRelativeLoad<uint64_t>(data_size, -offset, true, {"adr", "ldr"});
140 TestPcRelativeLoad<uint32_t>(data_size, offset, false, {"adr", "ldr"});
141 TestPcRelativeLoad<uint32_t>(data_size, offset, true, {"adr", "ldr"});
142 TestPcRelativeLoad<uint32_t>(data_size, -offset, false, {"adr", "ldr"});
143 TestPcRelativeLoad<uint32_t>(data_size, -offset, true, {"adr", "ldr"});
144 } else {
145 if ((offset & 7) == 0) {
146 TestPcRelativeLoad<uint64_t>(data_size, offset, false, {"adrp", "ldr"});
147 TestPcRelativeLoad<uint64_t>(data_size, offset, true, {"adrp", "add", "ldr"});
148 TestPcRelativeLoad<uint64_t>(data_size, -offset, false, {"adrp", "ldr"});
149 TestPcRelativeLoad<uint64_t>(data_size, -offset, true, {"adrp", "add", "ldr"});
150 } else {
151 TestPcRelativeLoad<uint64_t>(data_size, offset, false, {"adrp", "mov", "ldr"});
152 TestPcRelativeLoad<uint64_t>(data_size, offset, true, {"adrp", "add", "ldr"});
153 TestPcRelativeLoad<uint64_t>(data_size, -offset, false, {"adrp", "mov", "ldr"});
154 TestPcRelativeLoad<uint64_t>(data_size, -offset, true, {"adrp", "add", "ldr"});
155 }
156 TestPcRelativeLoad<uint32_t>(data_size, offset, false, {"adrp", "ldr"});
157 TestPcRelativeLoad<uint32_t>(data_size, offset, true, {"adrp", "add", "ldr"});
158 TestPcRelativeLoad<uint32_t>(data_size, -offset, false, {"adrp", "ldr"});
159 TestPcRelativeLoad<uint32_t>(data_size, -offset, true, {"adrp", "add", "ldr"});
160 }
161 }
162
163 template <typename T>
GetBadValue()164 static constexpr T GetBadValue()
165 {
166 if constexpr (sizeof(T) == sizeof(uint64_t)) {
167 return BAD_VALUE64;
168 }
169 return BAD_VALUE;
170 }
171
172 template <typename T>
GetGoodValue()173 static constexpr T GetGoodValue()
174 {
175 if constexpr (sizeof(T) == sizeof(uint64_t)) {
176 return GOOD_VALUE64;
177 }
178 return GOOD_VALUE;
179 }
180
181 protected:
182 static constexpr uint32_t BAD_VALUE = 0xdeadbaad;
183 static constexpr uint32_t GOOD_VALUE = 0xcafebabe;
184 static constexpr uint64_t BAD_VALUE64 = 0xdeadbaaddeadbaad;
185 static constexpr uint64_t GOOD_VALUE64 = 0x1234cafebabe4321;
186 static constexpr uint64_t MAX_INT21_VALUE = 0x100000;
187
188 private:
189 ArenaAllocator *allocator_ {nullptr};
190 aarch64::Aarch64Encoder *encoder_ {nullptr};
191 VixlExecModule *exec_module_ {nullptr};
192 };
193
TEST_F(EncoderArm64VixlTest,LoadPcRelative)194 TEST_F(EncoderArm64VixlTest, LoadPcRelative)
195 {
196 auto encoder = GetEncoder();
197 auto masm = encoder->GetMasm();
198 auto buffer = masm->GetBuffer();
199 static constexpr size_t code_offset = 16;
200 static constexpr size_t buf_size = (MAX_INT21_VALUE - vixl::aarch64::kPageSize) * 2 + code_offset;
201
202 for (size_t i = 0; i < buf_size / sizeof(uint32_t); i++) {
203 buffer->Emit32(GetBadValue<uint32_t>());
204 }
205 // Pre-allocate space for the code
206 for (size_t i = 0; i < 4; i++) {
207 buffer->Emit32(0);
208 }
209 for (size_t i = 0; i < buf_size / sizeof(uint32_t); i++) {
210 buffer->Emit32(GetBadValue<uint32_t>());
211 }
212
213 TestOffset(buf_size, 40);
214 TestOffset(buf_size, 44);
215 TestOffset(buf_size, 0x1374b8);
216 // Check for two pages addrp
217 TestOffset(buf_size, MAX_INT21_VALUE + vixl::aarch64::kPageSize + code_offset + 8);
218 // Check for one page addrp
219 TestOffset(buf_size, MAX_INT21_VALUE + vixl::aarch64::kPageSize + code_offset - 8);
220 TestOffset(buf_size, 0x100404);
221 }
222
223 } // namespace panda::compiler::tests
224