Home | History | Annotate | Download | only in utils
      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