1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ART_COMPILER_UTILS_ASSEMBLER_TEST_H_ 18 #define ART_COMPILER_UTILS_ASSEMBLER_TEST_H_ 19 20 #include "assembler.h" 21 22 #include "assembler_test_base.h" 23 #include "common_runtime_test.h" // For ScratchFile 24 25 #include <cstdio> 26 #include <cstdlib> 27 #include <fstream> 28 #include <iterator> 29 #include <sys/stat.h> 30 31 namespace art { 32 33 // Helper for a constexpr string length. 34 constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) { 35 return ('\0' == str[0]) ? count : ConstexprStrLen(str+1, count+1); 36 } 37 38 enum class RegisterView { // private 39 kUsePrimaryName, 40 kUseSecondaryName, 41 kUseTertiaryName, 42 kUseQuaternaryName, 43 }; 44 45 template<typename Ass, typename Reg, typename FPReg, typename Imm> 46 class AssemblerTest : public testing::Test { 47 public: 48 Ass* GetAssembler() { 49 return assembler_.get(); 50 } 51 52 typedef std::string (*TestFn)(AssemblerTest* assembler_test, Ass* assembler); 53 54 void DriverFn(TestFn f, std::string test_name) { 55 DriverWrapper(f(this, assembler_.get()), test_name); 56 } 57 58 // This driver assumes the assembler has already been called. 59 void DriverStr(std::string assembly_string, std::string test_name) { 60 DriverWrapper(assembly_string, test_name); 61 } 62 63 std::string RepeatR(void (Ass::*f)(Reg), std::string fmt) { 64 return RepeatTemplatedRegister<Reg>(f, 65 GetRegisters(), 66 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, 67 fmt); 68 } 69 70 std::string Repeatr(void (Ass::*f)(Reg), std::string fmt) { 71 return RepeatTemplatedRegister<Reg>(f, 72 GetRegisters(), 73 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, 74 fmt); 75 } 76 77 std::string RepeatRR(void (Ass::*f)(Reg, Reg), std::string fmt) { 78 return RepeatTemplatedRegisters<Reg, Reg>(f, 79 GetRegisters(), 80 GetRegisters(), 81 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, 82 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, 83 fmt); 84 } 85 86 std::string Repeatrr(void (Ass::*f)(Reg, Reg), std::string fmt) { 87 return RepeatTemplatedRegisters<Reg, Reg>(f, 88 GetRegisters(), 89 GetRegisters(), 90 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, 91 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, 92 fmt); 93 } 94 95 std::string Repeatrb(void (Ass::*f)(Reg, Reg), std::string fmt) { 96 return RepeatTemplatedRegisters<Reg, Reg>(f, 97 GetRegisters(), 98 GetRegisters(), 99 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, 100 &AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>, 101 fmt); 102 } 103 104 std::string RepeatRr(void (Ass::*f)(Reg, Reg), std::string fmt) { 105 return RepeatTemplatedRegisters<Reg, Reg>(f, 106 GetRegisters(), 107 GetRegisters(), 108 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, 109 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, 110 fmt); 111 } 112 113 std::string RepeatRI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) { 114 return RepeatRegisterImm<RegisterView::kUsePrimaryName>(f, imm_bytes, fmt); 115 } 116 117 std::string Repeatri(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) { 118 return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt); 119 } 120 121 std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), std::string fmt) { 122 return RepeatTemplatedRegisters<FPReg, FPReg>(f, 123 GetFPRegisters(), 124 GetFPRegisters(), 125 &AssemblerTest::GetFPRegName, 126 &AssemblerTest::GetFPRegName, 127 fmt); 128 } 129 130 std::string RepeatFFI(void (Ass::*f)(FPReg, FPReg, const Imm&), size_t imm_bytes, std::string fmt) { 131 return RepeatTemplatedRegistersImm<FPReg, FPReg>(f, 132 GetFPRegisters(), 133 GetFPRegisters(), 134 &AssemblerTest::GetFPRegName, 135 &AssemblerTest::GetFPRegName, 136 imm_bytes, 137 fmt); 138 } 139 140 std::string RepeatFR(void (Ass::*f)(FPReg, Reg), std::string fmt) { 141 return RepeatTemplatedRegisters<FPReg, Reg>(f, 142 GetFPRegisters(), 143 GetRegisters(), 144 &AssemblerTest::GetFPRegName, 145 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, 146 fmt); 147 } 148 149 std::string RepeatFr(void (Ass::*f)(FPReg, Reg), std::string fmt) { 150 return RepeatTemplatedRegisters<FPReg, Reg>(f, 151 GetFPRegisters(), 152 GetRegisters(), 153 &AssemblerTest::GetFPRegName, 154 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, 155 fmt); 156 } 157 158 std::string RepeatRF(void (Ass::*f)(Reg, FPReg), std::string fmt) { 159 return RepeatTemplatedRegisters<Reg, FPReg>(f, 160 GetRegisters(), 161 GetFPRegisters(), 162 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, 163 &AssemblerTest::GetFPRegName, 164 fmt); 165 } 166 167 std::string RepeatrF(void (Ass::*f)(Reg, FPReg), std::string fmt) { 168 return RepeatTemplatedRegisters<Reg, FPReg>(f, 169 GetRegisters(), 170 GetFPRegisters(), 171 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, 172 &AssemblerTest::GetFPRegName, 173 fmt); 174 } 175 176 std::string RepeatI(void (Ass::*f)(const Imm&), size_t imm_bytes, std::string fmt, 177 bool as_uint = false) { 178 std::string str; 179 std::vector<int64_t> imms = CreateImmediateValues(imm_bytes, as_uint); 180 181 WarnOnCombinations(imms.size()); 182 183 for (int64_t imm : imms) { 184 Imm new_imm = CreateImmediate(imm); 185 (assembler_.get()->*f)(new_imm); 186 std::string base = fmt; 187 188 size_t imm_index = base.find(IMM_TOKEN); 189 if (imm_index != std::string::npos) { 190 std::ostringstream sreg; 191 sreg << imm; 192 std::string imm_string = sreg.str(); 193 base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string); 194 } 195 196 if (str.size() > 0) { 197 str += "\n"; 198 } 199 str += base; 200 } 201 // Add a newline at the end. 202 str += "\n"; 203 return str; 204 } 205 206 // This is intended to be run as a test. 207 bool CheckTools() { 208 return test_helper_->CheckTools(); 209 } 210 211 // The following functions are public so that TestFn can use them... 212 213 virtual std::vector<Reg*> GetRegisters() = 0; 214 215 virtual std::vector<FPReg*> GetFPRegisters() { 216 UNIMPLEMENTED(FATAL) << "Architecture does not support floating-point registers"; 217 UNREACHABLE(); 218 } 219 220 // Secondary register names are the secondary view on registers, e.g., 32b on 64b systems. 221 virtual std::string GetSecondaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) { 222 UNIMPLEMENTED(FATAL) << "Architecture does not support secondary registers"; 223 UNREACHABLE(); 224 } 225 226 // Tertiary register names are the tertiary view on registers, e.g., 16b on 64b systems. 227 virtual std::string GetTertiaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) { 228 UNIMPLEMENTED(FATAL) << "Architecture does not support tertiary registers"; 229 UNREACHABLE(); 230 } 231 232 // Quaternary register names are the quaternary view on registers, e.g., 8b on 64b systems. 233 virtual std::string GetQuaternaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) { 234 UNIMPLEMENTED(FATAL) << "Architecture does not support quaternary registers"; 235 UNREACHABLE(); 236 } 237 238 std::string GetRegisterName(const Reg& reg) { 239 return GetRegName<RegisterView::kUsePrimaryName>(reg); 240 } 241 242 protected: 243 explicit AssemblerTest() {} 244 245 void SetUp() OVERRIDE { 246 assembler_.reset(new Ass()); 247 test_helper_.reset( 248 new AssemblerTestInfrastructure(GetArchitectureString(), 249 GetAssemblerCmdName(), 250 GetAssemblerParameters(), 251 GetObjdumpCmdName(), 252 GetObjdumpParameters(), 253 GetDisassembleCmdName(), 254 GetDisassembleParameters(), 255 GetAssemblyHeader())); 256 257 SetUpHelpers(); 258 } 259 260 void TearDown() OVERRIDE { 261 test_helper_.reset(); // Clean up the helper. 262 } 263 264 // Override this to set up any architecture-specific things, e.g., register vectors. 265 virtual void SetUpHelpers() {} 266 267 // Get the typically used name for this architecture, e.g., aarch64, x86_64, ... 268 virtual std::string GetArchitectureString() = 0; 269 270 // Get the name of the assembler, e.g., "as" by default. 271 virtual std::string GetAssemblerCmdName() { 272 return "as"; 273 } 274 275 // Switches to the assembler command. Default none. 276 virtual std::string GetAssemblerParameters() { 277 return ""; 278 } 279 280 // Get the name of the objdump, e.g., "objdump" by default. 281 virtual std::string GetObjdumpCmdName() { 282 return "objdump"; 283 } 284 285 // Switches to the objdump command. Default is " -h". 286 virtual std::string GetObjdumpParameters() { 287 return " -h"; 288 } 289 290 // Get the name of the objdump, e.g., "objdump" by default. 291 virtual std::string GetDisassembleCmdName() { 292 return "objdump"; 293 } 294 295 // Switches to the objdump command. As it's a binary, one needs to push the architecture and 296 // such to objdump, so it's architecture-specific and there is no default. 297 virtual std::string GetDisassembleParameters() = 0; 298 299 // Create a couple of immediate values up to the number of bytes given. 300 virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes, bool as_uint = false) { 301 std::vector<int64_t> res; 302 res.push_back(0); 303 if (!as_uint) { 304 res.push_back(-1); 305 } else { 306 res.push_back(0xFF); 307 } 308 res.push_back(0x12); 309 if (imm_bytes >= 2) { 310 res.push_back(0x1234); 311 if (!as_uint) { 312 res.push_back(-0x1234); 313 } else { 314 res.push_back(0xFFFF); 315 } 316 if (imm_bytes >= 4) { 317 res.push_back(0x12345678); 318 if (!as_uint) { 319 res.push_back(-0x12345678); 320 } else { 321 res.push_back(0xFFFFFFFF); 322 } 323 if (imm_bytes >= 6) { 324 res.push_back(0x123456789ABC); 325 if (!as_uint) { 326 res.push_back(-0x123456789ABC); 327 } 328 if (imm_bytes >= 8) { 329 res.push_back(0x123456789ABCDEF0); 330 if (!as_uint) { 331 res.push_back(-0x123456789ABCDEF0); 332 } else { 333 res.push_back(0xFFFFFFFFFFFFFFFF); 334 } 335 } 336 } 337 } 338 } 339 return res; 340 } 341 342 // Create an immediate from the specific value. 343 virtual Imm CreateImmediate(int64_t imm_value) = 0; 344 345 template <typename RegType> 346 std::string RepeatTemplatedRegister(void (Ass::*f)(RegType), 347 const std::vector<RegType*> registers, 348 std::string (AssemblerTest::*GetName)(const RegType&), 349 std::string fmt) { 350 std::string str; 351 for (auto reg : registers) { 352 (assembler_.get()->*f)(*reg); 353 std::string base = fmt; 354 355 std::string reg_string = (this->*GetName)(*reg); 356 size_t reg_index; 357 if ((reg_index = base.find(REG_TOKEN)) != std::string::npos) { 358 base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string); 359 } 360 361 if (str.size() > 0) { 362 str += "\n"; 363 } 364 str += base; 365 } 366 // Add a newline at the end. 367 str += "\n"; 368 return str; 369 } 370 371 template <typename Reg1, typename Reg2> 372 std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2), 373 const std::vector<Reg1*> reg1_registers, 374 const std::vector<Reg2*> reg2_registers, 375 std::string (AssemblerTest::*GetName1)(const Reg1&), 376 std::string (AssemblerTest::*GetName2)(const Reg2&), 377 std::string fmt) { 378 WarnOnCombinations(reg1_registers.size() * reg2_registers.size()); 379 380 std::string str; 381 for (auto reg1 : reg1_registers) { 382 for (auto reg2 : reg2_registers) { 383 (assembler_.get()->*f)(*reg1, *reg2); 384 std::string base = fmt; 385 386 std::string reg1_string = (this->*GetName1)(*reg1); 387 size_t reg1_index; 388 while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) { 389 base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string); 390 } 391 392 std::string reg2_string = (this->*GetName2)(*reg2); 393 size_t reg2_index; 394 while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) { 395 base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string); 396 } 397 398 if (str.size() > 0) { 399 str += "\n"; 400 } 401 str += base; 402 } 403 } 404 // Add a newline at the end. 405 str += "\n"; 406 return str; 407 } 408 409 template <typename Reg1, typename Reg2> 410 std::string RepeatTemplatedRegistersImm(void (Ass::*f)(Reg1, Reg2, const Imm&), 411 const std::vector<Reg1*> reg1_registers, 412 const std::vector<Reg2*> reg2_registers, 413 std::string (AssemblerTest::*GetName1)(const Reg1&), 414 std::string (AssemblerTest::*GetName2)(const Reg2&), 415 size_t imm_bytes, 416 std::string fmt) { 417 std::vector<int64_t> imms = CreateImmediateValues(imm_bytes); 418 WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * imms.size()); 419 420 std::string str; 421 for (auto reg1 : reg1_registers) { 422 for (auto reg2 : reg2_registers) { 423 for (int64_t imm : imms) { 424 Imm new_imm = CreateImmediate(imm); 425 (assembler_.get()->*f)(*reg1, *reg2, new_imm); 426 std::string base = fmt; 427 428 std::string reg1_string = (this->*GetName1)(*reg1); 429 size_t reg1_index; 430 while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) { 431 base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string); 432 } 433 434 std::string reg2_string = (this->*GetName2)(*reg2); 435 size_t reg2_index; 436 while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) { 437 base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string); 438 } 439 440 size_t imm_index = base.find(IMM_TOKEN); 441 if (imm_index != std::string::npos) { 442 std::ostringstream sreg; 443 sreg << imm; 444 std::string imm_string = sreg.str(); 445 base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string); 446 } 447 448 if (str.size() > 0) { 449 str += "\n"; 450 } 451 str += base; 452 } 453 } 454 } 455 // Add a newline at the end. 456 str += "\n"; 457 return str; 458 } 459 460 template <RegisterView kRegView> 461 std::string GetRegName(const Reg& reg) { 462 std::ostringstream sreg; 463 switch (kRegView) { 464 case RegisterView::kUsePrimaryName: 465 sreg << reg; 466 break; 467 468 case RegisterView::kUseSecondaryName: 469 sreg << GetSecondaryRegisterName(reg); 470 break; 471 472 case RegisterView::kUseTertiaryName: 473 sreg << GetTertiaryRegisterName(reg); 474 break; 475 476 case RegisterView::kUseQuaternaryName: 477 sreg << GetQuaternaryRegisterName(reg); 478 break; 479 } 480 return sreg.str(); 481 } 482 483 std::string GetFPRegName(const FPReg& reg) { 484 std::ostringstream sreg; 485 sreg << reg; 486 return sreg.str(); 487 } 488 489 // If the assembly file needs a header, return it in a sub-class. 490 virtual const char* GetAssemblyHeader() { 491 return nullptr; 492 } 493 494 void WarnOnCombinations(size_t count) { 495 if (count > kWarnManyCombinationsThreshold) { 496 GTEST_LOG_(WARNING) << "Many combinations (" << count << "), test generation might be slow."; 497 } 498 } 499 500 static constexpr const char* REG_TOKEN = "{reg}"; 501 static constexpr const char* REG1_TOKEN = "{reg1}"; 502 static constexpr const char* REG2_TOKEN = "{reg2}"; 503 static constexpr const char* IMM_TOKEN = "{imm}"; 504 505 private: 506 template <RegisterView kRegView> 507 std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, 508 std::string fmt) { 509 const std::vector<Reg*> registers = GetRegisters(); 510 std::string str; 511 std::vector<int64_t> imms = CreateImmediateValues(imm_bytes); 512 513 WarnOnCombinations(registers.size() * imms.size()); 514 515 for (auto reg : registers) { 516 for (int64_t imm : imms) { 517 Imm new_imm = CreateImmediate(imm); 518 (assembler_.get()->*f)(*reg, new_imm); 519 std::string base = fmt; 520 521 std::string reg_string = GetRegName<kRegView>(*reg); 522 size_t reg_index; 523 while ((reg_index = base.find(REG_TOKEN)) != std::string::npos) { 524 base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string); 525 } 526 527 size_t imm_index = base.find(IMM_TOKEN); 528 if (imm_index != std::string::npos) { 529 std::ostringstream sreg; 530 sreg << imm; 531 std::string imm_string = sreg.str(); 532 base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string); 533 } 534 535 if (str.size() > 0) { 536 str += "\n"; 537 } 538 str += base; 539 } 540 } 541 // Add a newline at the end. 542 str += "\n"; 543 return str; 544 } 545 546 void DriverWrapper(std::string assembly_text, std::string test_name) { 547 size_t cs = assembler_->CodeSize(); 548 std::unique_ptr<std::vector<uint8_t>> data(new std::vector<uint8_t>(cs)); 549 MemoryRegion code(&(*data)[0], data->size()); 550 assembler_->FinalizeInstructions(code); 551 test_helper_->Driver(*data, assembly_text, test_name); 552 } 553 554 static constexpr size_t kWarnManyCombinationsThreshold = 500; 555 556 std::unique_ptr<Ass> assembler_; 557 std::unique_ptr<AssemblerTestInfrastructure> test_helper_; 558 559 DISALLOW_COPY_AND_ASSIGN(AssemblerTest); 560 }; 561 562 } // namespace art 563 564 #endif // ART_COMPILER_UTILS_ASSEMBLER_TEST_H_ 565