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