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