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