• 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 <climits>
17 
18 #include <gtest/gtest.h>
19 #include <random>
20 #include <sstream>
21 
22 #include "libpandabase/os/filesystem.h"
23 #include "mem/base_mem_stats.h"
24 #include "mem/code_allocator.h"
25 #include "mem/pool_manager.h"
26 #include "os/filesystem.h"
27 #include "target/asm_printer.h"
28 
29 static const std::string output_dir = "asm_output";
30 
31 namespace panda::compiler {
32 
33 template <Arch arch>
34 class PrinterTest : public ::testing::Test {
35 public:
PrinterTest()36     PrinterTest()
37     {
38         panda::mem::MemConfig::Initialize(64_MB, 64_MB, 64_MB, 32_MB);
39 #ifdef STDOUT_PRINT
40         curr_stream_ = &std::cout;
41 #else
42         curr_stream_ = new std::stringstream();
43 #endif
44         PoolManager::Initialize();
45         allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER);
46         // Create printer
47         encoder_ = Encoder::Create(allocator_, arch, true);
48         encoder_->InitMasm();
49         regfile_ = RegistersDescription::Create(allocator_, arch);
50         bool pabi = false;
51         bool osr = false;
52         bool dyn = false;
53         bool print = true;
54 #ifdef PANDA_COMPILER_TARGET_AARCH32
55         if constexpr (arch == Arch::AARCH32) {
56             dir_suffix_ = "aarch32";
57             auto enc = reinterpret_cast<aarch32::Aarch32Assembly *>(encoder_);
58             enc->SetStream(curr_stream_);
59             callconv_ = CallingConvention::Create(allocator_, enc, regfile_, arch, pabi, osr, dyn, print);
60             enc->GetEncoder()->SetRegfile(regfile_);
61         }
62 #endif
63 #ifdef PANDA_COMPILER_TARGET_AARCH64
64         if constexpr (arch == Arch::AARCH64) {
65             dir_suffix_ = "aarch64";
66             auto enc = reinterpret_cast<aarch64::Aarch64Assembly *>(encoder_);
67             enc->SetStream(curr_stream_);
68             callconv_ = CallingConvention::Create(allocator_, enc, regfile_, arch, pabi, osr, dyn, print);
69             enc->GetEncoder()->SetRegfile(regfile_);
70         }
71 #endif
72 #ifdef PANDA_COMPILER_TARGET_X86_64
73         if constexpr (arch == Arch::X86_64) {
74             dir_suffix_ = "amd64";
75             auto enc = reinterpret_cast<amd64::Amd64Assembly *>(encoder_);
76             enc->SetStream(curr_stream_);
77             callconv_ = CallingConvention::Create(allocator_, enc, regfile_, arch, pabi, osr, dyn, print);
78             enc->GetEncoder()->SetRegfile(regfile_);
79         }
80 #endif
81         mem_stats_ = new BaseMemStats();
82         code_alloc_ = new (std::nothrow) CodeAllocator(mem_stats_);
83         // Create dir if it is not exist
84         auto exec_path = panda::os::file::File::GetExecutablePath();
85         ASSERT(exec_path);
86         exec_path_ = exec_path.Value();
87         os::CreateDirectories(exec_path_ + "/" + output_dir);
88         os::CreateDirectories(GetDir());
89     }
~PrinterTest()90     ~PrinterTest()
91     {
92         Logger::Destroy();
93         encoder_->~Encoder();
94         delete allocator_;
95         delete code_alloc_;
96         delete mem_stats_;
97         PoolManager::Finalize();
98         panda::mem::MemConfig::Finalize();
99         delete curr_stream_;
100     }
101 
GetCodeAllocator()102     CodeAllocator *GetCodeAllocator()
103     {
104         return code_alloc_;
105     }
106 
GetDir()107     std::string GetDir()
108     {
109         return exec_path_ + "/" + output_dir + "/" + dir_suffix_ + "/";
110     }
111 
ResetCodeAllocator(void * ptr,size_t size)112     void ResetCodeAllocator(void *ptr, size_t size)
113     {
114         os::mem::MapRange<std::byte> mem_range(static_cast<std::byte *>(ptr), size);
115         mem_range.MakeReadWrite();
116         delete code_alloc_;
117         code_alloc_ = new (std::nothrow) CodeAllocator(mem_stats_);
118     }
119 
GetAllocator()120     ArenaAllocator *GetAllocator()
121     {
122         return allocator_;
123     }
124 
GetEncoder()125     Encoder *GetEncoder()
126     {
127         return encoder_;
128     }
129 
GetRegfile()130     RegistersDescription *GetRegfile()
131     {
132         return regfile_;
133     }
134 
GetCallconv()135     CallingConvention *GetCallconv()
136     {
137         return callconv_;
138     }
139 
GetCursor()140     size_t GetCursor()
141     {
142         return curr_cursor_;
143     }
144 
145     // Warning! Do not use multiply times with different types!
GetParameter(TypeInfo type,int id=0)146     Reg GetParameter(TypeInfo type, int id = 0)
147     {
148         ASSERT(id < 4);
149         if (type.IsFloat()) {
150             return Reg(id, type);
151         }
152         if constexpr (arch == Arch::AARCH32) {
153             // special offset for double-reg param
154             if (id == 1 && type.GetSize() == DOUBLE_WORD_SIZE) {
155                 return Target::Current().GetParamReg(2, type);
156             }
157         }
158         return Target::Current().GetParamReg(id, type);
159     }
160 
PreWork()161     void PreWork()
162     {
163         // Curor need to encode multiply tests due one execution
164         curr_cursor_ = 0;
165         encoder_->SetCursorOffset(0);
166 
167         [[maybe_unused]] std::string func_name = "test_" + GetTestName();
168 #ifdef PANDA_COMPILER_TARGET_AARCH32
169         if constexpr (arch == Arch::AARCH32) {
170             auto enc = reinterpret_cast<aarch32::Aarch32Assembly *>(encoder_);
171             enc->EmitFunctionName(reinterpret_cast<const void *>(func_name.c_str()));
172         }
173 #endif
174 #ifdef PANDA_COMPILER_TARGET_AARCH64
175         if constexpr (arch == Arch::AARCH64) {
176             auto enc = reinterpret_cast<aarch64::Aarch64Assembly *>(encoder_);
177             enc->EmitFunctionName(reinterpret_cast<const void *>(func_name.c_str()));
178         }
179 #endif
180 #ifdef PANDA_COMPILER_TARGET_X86_64
181         if constexpr (arch == Arch::X86_64) {
182             auto enc = reinterpret_cast<amd64::Amd64Assembly *>(encoder_);
183             enc->EmitFunctionName(reinterpret_cast<const void *>(func_name.c_str()));
184         }
185 #endif
186         callconv_->GeneratePrologue(FrameInfo::FullPrologue());
187     }
188 
PostWork()189     void PostWork()
190     {
191         auto param = Target::Current().GetParamReg(0);
192         auto return_reg = Target::Current().GetReturnReg();
193         if (param.GetId() != return_reg.GetId()) {
194             GetEncoder()->EncodeMov(return_reg, param);
195         }
196         callconv_->GenerateEpilogue(FrameInfo::FullPrologue(), []() {});
197         encoder_->Finalize();
198     }
199 
200 #ifdef STDOUT_PRINT
GetStream()201     std::ostream *GetStream()
202 #else
203     std::stringstream *GetStream()
204 #endif
205     {
206         return curr_stream_;
207     }
208 
SetTestName(std::string name)209     void SetTestName(std::string name)
210     {
211         test_name_ = name;
212     }
213 
GetTestName()214     const std::string GetTestName()
215     {
216         return test_name_;
217     }
218 
FinalizeTest()219     void FinalizeTest()
220     {
221         // Make them separate!
222         std::string filename = GetTestName() + ".S";
223         std::ofstream asm_file;
224         asm_file.open(GetDir() + "/" + filename.c_str());
225         // Test must generate asembly-flie
226         ASSERT(asm_file.is_open());
227         // Compile by assembler
228 #ifndef STDOUT_PRINT
229         asm_file << GetStream()->str();
230 #endif
231         asm_file.close();
232     }
233 
234 private:
235     ArenaAllocator *allocator_ {nullptr};
236     Encoder *encoder_ {nullptr};
237     RegistersDescription *regfile_ {nullptr};
238     // Callconv for printing
239     CallingConvention *cc_ {nullptr};
240     // Callconv for masm initialization
241     CallingConvention *callconv_ {nullptr};
242     CodeAllocator *code_alloc_ {nullptr};
243     BaseMemStats *mem_stats_ {nullptr};
244     size_t curr_cursor_ {0};
245 #ifdef STDOUT_PRINT
246     std::ostream *curr_stream_;
247 #else
248     std::stringstream *curr_stream_;
249 #endif
250     std::string test_name_;
251     std::string exec_path_;
252     std::string dir_suffix_;
253 };
254 
255 #ifdef PANDA_COMPILER_TARGET_AARCH32
256 using PrinterAarch32Test = PrinterTest<Arch::AARCH32>;
257 #endif
258 
259 #ifdef PANDA_COMPILER_TARGET_AARCH64
260 using PrinterAarch64Test = PrinterTest<Arch::AARCH64>;
261 #endif
262 
263 #ifdef PANDA_COMPILER_TARGET_X86_64
264 using PrinterAmd64Test = PrinterTest<Arch::X86_64>;
265 #endif
266 
267 #define SINGLE_PARAM_TEST_TEMPLATE(test_name, encode_func) \
268     template <typename T, Arch arch>                       \
269     bool test_name(PrinterTest<arch> *test)                \
270     {                                                      \
271         test->PreWork();                                   \
272         auto param = test->GetParameter(TypeInfo(T(0)));   \
273         test->GetEncoder()->encode_func(param, param);     \
274         test->PostWork();                                  \
275         if (!test->GetEncoder()->GetResult()) {            \
276             return false;                                  \
277         }                                                  \
278         return true;                                       \
279     }
280 
281 SINGLE_PARAM_TEST_TEMPLATE(TestMov, EncodeMov)
282 SINGLE_PARAM_TEST_TEMPLATE(TestNeg, EncodeNeg)
283 SINGLE_PARAM_TEST_TEMPLATE(TestAbs, EncodeAbs)
284 SINGLE_PARAM_TEST_TEMPLATE(TestNot, EncodeNot)
285 // SINGLE_PARAM_TEST_TEMPLATE(TestSqrt, EncodeSqrt)
286 
287 #undef SINGLE_PARAM_TEST_TEMPLATE
288 
289 #define DOUBLE_PARAM_TEST_TEMPLATE(test_name, encode_func)       \
290     template <typename T, Arch arch>                             \
291     bool test_name(PrinterTest<arch> *test)                      \
292     {                                                            \
293         test->PreWork();                                         \
294         auto param1 = test->GetParameter(TypeInfo(T(0)), 0);     \
295         auto param2 = test->GetParameter(TypeInfo(T(0)), 1);     \
296         test->GetEncoder()->encode_func(param1, param1, param2); \
297         test->PostWork();                                        \
298         if (!test->GetEncoder()->GetResult()) {                  \
299             return false;                                        \
300         }                                                        \
301         return true;                                             \
302     }
303 
304 DOUBLE_PARAM_TEST_TEMPLATE(TestAdd, EncodeAdd)
305 DOUBLE_PARAM_TEST_TEMPLATE(TestSub, EncodeSub)
306 DOUBLE_PARAM_TEST_TEMPLATE(TestMul, EncodeMul)
307 DOUBLE_PARAM_TEST_TEMPLATE(TestShl, EncodeShl)
308 DOUBLE_PARAM_TEST_TEMPLATE(TestShr, EncodeShr)
309 DOUBLE_PARAM_TEST_TEMPLATE(TestAShr, EncodeAShr)
310 DOUBLE_PARAM_TEST_TEMPLATE(TestAnd, EncodeAnd)
311 DOUBLE_PARAM_TEST_TEMPLATE(TestOr, EncodeOr)
312 DOUBLE_PARAM_TEST_TEMPLATE(TestXor, EncodeXor)
313 
314 #undef DOUBLE_PARAM_TEST_TEMPLATE
315 
316 #define ONE_TEST_BODY(test_class, test_method, test_name, arch) \
317     TEST_F(test_class, test_method)                             \
318     {                                                           \
319         bool success = true;                                    \
320         SetTestName(#test_name "_8");                           \
321         success &= test_method<uint8_t, Arch::arch>(this);      \
322         SetTestName(#test_name "_16");                          \
323         success &= test_method<uint16_t, Arch::arch>(this);     \
324         SetTestName(#test_name "_32");                          \
325         success &= test_method<uint32_t, Arch::arch>(this);     \
326         SetTestName(#test_name "_64");                          \
327         success &= test_method<uint64_t, Arch::arch>(this);     \
328         SetTestName(#test_name);                                \
329         FinalizeTest();                                         \
330         EXPECT_TRUE(success);                                   \
331     }
332 
333 #ifdef PANDA_COMPILER_TARGET_AARCH64
334 ONE_TEST_BODY(PrinterAarch64Test, TestMov, mov, AARCH64)
335 ONE_TEST_BODY(PrinterAarch64Test, TestNeg, neg, AARCH64)
336 ONE_TEST_BODY(PrinterAarch64Test, TestAbs, abs, AARCH64)
337 ONE_TEST_BODY(PrinterAarch64Test, TestNot, not, AARCH64)
338 ONE_TEST_BODY(PrinterAarch64Test, TestAdd, add, AARCH64)
339 ONE_TEST_BODY(PrinterAarch64Test, TestSub, sub, AARCH64)
340 ONE_TEST_BODY(PrinterAarch64Test, TestMul, mul, AARCH64)
341 ONE_TEST_BODY(PrinterAarch64Test, TestShl, shl, AARCH64)
342 ONE_TEST_BODY(PrinterAarch64Test, TestShr, shr, AARCH64)
343 ONE_TEST_BODY(PrinterAarch64Test, TestAShr, ashr, AARCH64)
344 ONE_TEST_BODY(PrinterAarch64Test, TestAnd, and, AARCH64)
345 ONE_TEST_BODY(PrinterAarch64Test, TestOr, or, AARCH64)
346 ONE_TEST_BODY(PrinterAarch64Test, TestXor, xor, AARCH64)
347 #endif
348 
349 #ifdef PANDA_COMPILER_TARGET_AARCH32
350 ONE_TEST_BODY(PrinterAarch32Test, TestMov, mov, AARCH32)
351 ONE_TEST_BODY(PrinterAarch32Test, TestNeg, neg, AARCH32)
352 ONE_TEST_BODY(PrinterAarch32Test, TestAbs, abs, AARCH32)
353 ONE_TEST_BODY(PrinterAarch32Test, TestNot, not, AARCH32)
354 ONE_TEST_BODY(PrinterAarch32Test, TestAdd, add, AARCH32)
355 ONE_TEST_BODY(PrinterAarch32Test, TestSub, sub, AARCH32)
356 ONE_TEST_BODY(PrinterAarch32Test, TestMul, mul, AARCH32)
357 ONE_TEST_BODY(PrinterAarch32Test, TestShl, shl, AARCH32)
358 ONE_TEST_BODY(PrinterAarch32Test, TestShr, shr, AARCH32)
359 ONE_TEST_BODY(PrinterAarch32Test, TestAShr, ashr, AARCH32)
360 ONE_TEST_BODY(PrinterAarch32Test, TestAnd, and, AARCH32)
361 ONE_TEST_BODY(PrinterAarch32Test, TestOr, or, AARCH32)
362 ONE_TEST_BODY(PrinterAarch32Test, TestXor, xor, AARCH32)
363 #endif
364 
365 #ifdef PANDA_COMPILER_TARGET_X86_64
366 ONE_TEST_BODY(PrinterAmd64Test, TestMov, mov, X86_64)
367 ONE_TEST_BODY(PrinterAmd64Test, TestNeg, neg, X86_64)
368 ONE_TEST_BODY(PrinterAmd64Test, TestAbs, abs, X86_64)
369 ONE_TEST_BODY(PrinterAmd64Test, TestNot, not, X86_64)
370 ONE_TEST_BODY(PrinterAmd64Test, TestAdd, add, X86_64)
371 ONE_TEST_BODY(PrinterAmd64Test, TestSub, sub, X86_64)
372 ONE_TEST_BODY(PrinterAmd64Test, TestMul, mul, X86_64)
373 ONE_TEST_BODY(PrinterAmd64Test, TestShl, shl, X86_64)
374 ONE_TEST_BODY(PrinterAmd64Test, TestShr, shr, X86_64)
375 ONE_TEST_BODY(PrinterAmd64Test, TestAShr, ashr, X86_64)
376 ONE_TEST_BODY(PrinterAmd64Test, TestAnd, and, X86_64)
377 ONE_TEST_BODY(PrinterAmd64Test, TestOr, or, X86_64)
378 ONE_TEST_BODY(PrinterAmd64Test, TestXor, xor, X86_64)
379 #endif
380 
381 }  // namespace panda::compiler
382