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 #ifndef COMPILER_TESTS_VIXL_EXEC_MODULE_H
17 #define COMPILER_TESTS_VIXL_EXEC_MODULE_H
18
19 #ifndef USE_VIXL_ARM64
20 #error "Unsupported!"
21 #endif
22
23 #include "aarch64/simulator-aarch64.h"
24 #include "aarch64/macro-assembler-aarch64.h"
25 #include "optimizer/code_generator/operands.h"
26 #include "optimizer/ir/constants.h"
27 #include "optimizer/ir/runtime_interface.h"
28 #include "unit_test.h"
29
30 #include <cstring>
31 #include <cstdlib>
32
33 namespace panda::compiler {
34 using namespace vixl::aarch64;
35
36 template <class T>
CutValue(uint64_t data,DataType::Type type)37 T CutValue(uint64_t data, DataType::Type type)
38 {
39 switch (type) {
40 default:
41 case (DataType::VOID):
42 case (DataType::NO_TYPE):
43 ASSERT(false);
44 return -1;
45 case (DataType::BOOL):
46 return static_cast<T>(static_cast<bool>(data));
47 case (DataType::UINT8):
48 return static_cast<T>(static_cast<uint8_t>(data));
49 case (DataType::INT8):
50 return static_cast<T>(static_cast<int8_t>(data));
51 case (DataType::UINT16):
52 return static_cast<T>(static_cast<uint16_t>(data));
53 case (DataType::INT16):
54 return static_cast<T>(static_cast<int16_t>(data));
55 case (DataType::UINT32):
56 return static_cast<T>(static_cast<uint32_t>(data));
57 case (DataType::INT32):
58 return static_cast<T>(static_cast<int32_t>(data));
59 case (DataType::UINT64):
60 return static_cast<T>(static_cast<uint64_t>(data));
61 case (DataType::INT64):
62 return static_cast<T>(static_cast<int64_t>(data));
63 case (DataType::FLOAT32):
64 return static_cast<T>(static_cast<float>(data));
65 case (DataType::FLOAT64):
66 return static_cast<T>(static_cast<double>(data));
67 }
68 return 0;
69 }
70
71 // Class for emulate generated arm64 code
72 class VixlExecModule {
73 public:
VixlExecModule(panda::ArenaAllocator * allocator,RuntimeInterface * runtime_info)74 VixlExecModule(panda::ArenaAllocator *allocator, RuntimeInterface *runtime_info)
75 : decoder(allocator),
76 simulator(allocator, &decoder, SimStack().Allocate()),
77 runtime_info_(runtime_info),
78 allocator_(allocator),
79 params_(allocator->Adapter()) {};
80
SetInstructions(const char * start,const char * end)81 void SetInstructions(const char *start, const char *end)
82 {
83 start_pointer = reinterpret_cast<const Instruction *>(start);
84 end_pointer = reinterpret_cast<const Instruction *>(end);
85 }
86
87 // Original type
88 template <typename T>
GetType()89 static constexpr DataType::Type GetType()
90 {
91 if constexpr (std::is_same_v<T, bool>) {
92 return DataType::BOOL;
93 } else if constexpr (std::is_same_v<T, uint8_t>) {
94 return DataType::UINT8;
95 } else if constexpr (std::is_same_v<T, int8_t>) {
96 return DataType::INT8;
97 } else if constexpr (std::is_same_v<T, uint16_t>) {
98 return DataType::UINT16;
99 } else if constexpr (std::is_same_v<T, int16_t>) {
100 return DataType::INT16;
101 } else if constexpr (std::is_same_v<T, uint32_t>) {
102 return DataType::UINT32;
103 } else if constexpr (std::is_same_v<T, int32_t>) {
104 return DataType::INT32;
105 } else if constexpr (std::is_same_v<T, uint64_t>) {
106 return DataType::UINT64;
107 } else if constexpr (std::is_same_v<T, int64_t>) {
108 return DataType::INT64;
109 } else if constexpr (std::is_same_v<T, float>) {
110 return DataType::FLOAT32;
111 } else if constexpr (std::is_same_v<T, double>) {
112 return DataType::FLOAT64;
113 }
114 return DataType::NO_TYPE;
115 }
116
117 // Set/Get one parameter - will updated during emulation
118 template <class T>
SetParameter(uint32_t idx,T imm)119 void SetParameter(uint32_t idx, T imm)
120 {
121 constexpr DataType::Type type = GetType<T>();
122 if (params_.size() < idx + 1) {
123 params_.resize(idx + 1);
124 }
125 params_[idx] = {imm, type};
126 }
127
128 template <typename T>
CreateArray(T * array,int size,ArenaAllocator * object_allocator)129 void *CreateArray(T *array, int size, ArenaAllocator *object_allocator)
130 {
131 void *arr_data = object_allocator->Alloc(size * sizeof(T) + runtime_info_->GetArrayDataOffset(Arch::AARCH64));
132 ASSERT(IsInObjectsAddressSpace(static_cast<uintptr_t>arr_data));
133 int *lenarr = reinterpret_cast<int *>(reinterpret_cast<char *>(arr_data) +
134 runtime_info_->GetArrayLengthOffset(Arch::AARCH64));
135 lenarr[0] = size;
136 T *arr = reinterpret_cast<T *>(reinterpret_cast<char *>(arr_data) +
137 runtime_info_->GetArrayDataOffset(Arch::AARCH64));
138
139 memcpy_s(arr, size * sizeof(T), array, size * sizeof(T));
140 return arr_data;
141 }
142
143 template <typename T>
CopyArray(void * arr_data,T * array)144 void CopyArray(void *arr_data, T *array)
145 {
146 int *lenarr = reinterpret_cast<int *>(reinterpret_cast<char *>(arr_data) +
147 runtime_info_->GetArrayLengthOffset(Arch::AARCH64));
148 auto size = lenarr[0];
149 T *arr = reinterpret_cast<T *>(reinterpret_cast<char *>(arr_data) +
150 runtime_info_->GetArrayDataOffset(Arch::AARCH64));
151 memcpy_s(array, size * sizeof(T), arr, size * sizeof(T));
152 }
153
FreeArray(void * array)154 void FreeArray([[maybe_unused]] void *array)
155 {
156 // std::free(array)
157 }
158
159 // Return value
GetRetValue()160 int64_t GetRetValue()
161 {
162 return simulator.ReadXRegister(0);
163 }
164
165 template <typename T>
GetRetValue()166 T GetRetValue()
167 {
168 if constexpr (std::is_same_v<T, float>) {
169 return simulator.ReadVRegister<T>(0);
170 } else if constexpr (std::is_same_v<T, double>) {
171 return simulator.ReadVRegister<T>(0);
172 }
173 return static_cast<T>(simulator.ReadXRegister(0));
174 }
175
WriteParameters()176 void WriteParameters()
177 {
178 // we can put only 7 parameters to registers
179 ASSERT(params_.size() <= 7);
180 // 0 reg reserve to method
181 uint32_t curr_reg_num = 1;
182 uint32_t curr_vreg_num = 0;
183 for (auto [imm, type] : params_) {
184 if (type == DataType::BOOL) {
185 simulator.WriteXRegister(curr_reg_num++, std::get<bool>(imm));
186 } else if (type == DataType::INT8) {
187 simulator.WriteXRegister(curr_reg_num++, std::get<int8_t>(imm));
188 } else if (type == DataType::UINT8) {
189 simulator.WriteXRegister(curr_reg_num++, std::get<uint8_t>(imm));
190 } else if (type == DataType::INT16) {
191 simulator.WriteXRegister(curr_reg_num++, std::get<int16_t>(imm));
192 } else if (type == DataType::UINT16) {
193 simulator.WriteXRegister(curr_reg_num++, std::get<uint16_t>(imm));
194 } else if (type == DataType::INT32) {
195 simulator.WriteXRegister(curr_reg_num++, std::get<int32_t>(imm));
196 } else if (type == DataType::UINT32) {
197 simulator.WriteXRegister(curr_reg_num++, std::get<uint32_t>(imm));
198 } else if (type == DataType::INT64) {
199 simulator.WriteXRegister(curr_reg_num++, std::get<int64_t>(imm));
200 } else if (type == DataType::UINT64) {
201 simulator.WriteXRegister(curr_reg_num++, std::get<uint64_t>(imm));
202 } else if (type == DataType::FLOAT32) {
203 simulator.WriteVRegister(curr_vreg_num++, std::get<float>(imm));
204 } else if (type == DataType::FLOAT64) {
205 simulator.WriteVRegister(curr_vreg_num++, std::get<double>(imm));
206 } else {
207 UNREACHABLE();
208 }
209 }
210 ClearParameters();
211 }
212
213 // Run emulator on current code with current parameters
Execute()214 void Execute()
215 {
216 simulator.ResetState();
217 // method
218 simulator.WriteXRegister(0, 0xDEADC0DE);
219 // Set x28 to the dummy thread, since prolog of the compiled method writes to it.
220 static std::array<uint8_t, sizeof(ManagedThread)> dummy;
221 simulator.WriteCPURegister(XRegister(28), dummy.data());
222
223 WriteParameters();
224 simulator.RunFrom(reinterpret_cast<const Instruction *>(start_pointer));
225 }
226
227 // Change debbuging level
SetDump(bool dump)228 void SetDump(bool dump)
229 {
230 if (dump) {
231 simulator.SetTraceParameters(simulator.GetTraceParameters() | LOG_ALL);
232 } else {
233 simulator.SetTraceParameters(LOG_NONE);
234 }
235 }
236
237 // Print encoded instructions
PrintInstructions()238 void PrintInstructions()
239 {
240 Decoder decoder(allocator_);
241 Disassembler disasm(allocator_);
242 decoder.AppendVisitor(&disasm);
243 for (auto instr = start_pointer; instr < end_pointer; instr += kInstructionSize) {
244 decoder.Decode(instr);
245 std::cout << "VIXL disasm " << reinterpret_cast<uintptr_t>(instr) << " : 0x" << std::hex;
246 std::cout << std::setfill('0') << std::right << std::setw(sizeof(int64_t));
247 std::cout << *(reinterpret_cast<const uint32_t *>(instr)) << ":\t" << disasm.GetOutput() << "\n";
248 std::cout << std::setfill(' ');
249 }
250 }
251
GetSimulator()252 Simulator *GetSimulator()
253 {
254 return &simulator;
255 }
256
257 private:
ClearParameters()258 void ClearParameters()
259 {
260 // clear size and capacity
261 params_ = Params(allocator_->Adapter());
262 }
263
264 using Params = ArenaVector<std::pair<
265 std::variant<bool, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, float, double>,
266 DataType::Type>>;
267
268 // VIXL Instruction decoder
269 Decoder decoder;
270 // VIXL Simulator
271 Simulator simulator;
272 // Begin of executed code
273 const Instruction *start_pointer {nullptr};
274 // End of executed code
275 const Instruction *end_pointer {nullptr};
276
277 RuntimeInterface *runtime_info_;
278
279 // Dummy allocated data for parameters:
280 panda::ArenaAllocator *allocator_;
281
282 Params params_;
283 };
284 } // namespace panda::compiler
285
286 #endif // COMPILER_TESTS_VIXL_EXEC_MODULE_H
287