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