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(const std::string & name)209 void SetTestName(const 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