• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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