Home | History | Annotate | Download | only in optimizing
      1 /*
      2  * Copyright (C) 2016 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_OPTIMIZING_CODEGEN_TEST_UTILS_H_
     18 #define ART_COMPILER_OPTIMIZING_CODEGEN_TEST_UTILS_H_
     19 
     20 #include "arch/arm/registers_arm.h"
     21 #include "arch/instruction_set.h"
     22 #include "arch/mips/registers_mips.h"
     23 #include "arch/mips64/registers_mips64.h"
     24 #include "arch/x86/registers_x86.h"
     25 #include "code_simulator.h"
     26 #include "code_simulator_container.h"
     27 #include "common_compiler_test.h"
     28 #include "graph_checker.h"
     29 #include "prepare_for_register_allocation.h"
     30 #include "ssa_liveness_analysis.h"
     31 
     32 #ifdef ART_ENABLE_CODEGEN_arm
     33 #include "code_generator_arm_vixl.h"
     34 #endif
     35 
     36 #ifdef ART_ENABLE_CODEGEN_arm64
     37 #include "code_generator_arm64.h"
     38 #endif
     39 
     40 #ifdef ART_ENABLE_CODEGEN_x86
     41 #include "code_generator_x86.h"
     42 #endif
     43 
     44 #ifdef ART_ENABLE_CODEGEN_x86_64
     45 #include "code_generator_x86_64.h"
     46 #endif
     47 
     48 #ifdef ART_ENABLE_CODEGEN_mips
     49 #include "code_generator_mips.h"
     50 #endif
     51 
     52 #ifdef ART_ENABLE_CODEGEN_mips64
     53 #include "code_generator_mips64.h"
     54 #endif
     55 
     56 namespace art {
     57 
     58 typedef CodeGenerator* (*CreateCodegenFn)(HGraph*, const CompilerOptions&);
     59 
     60 class CodegenTargetConfig {
     61  public:
     62   CodegenTargetConfig(InstructionSet isa, CreateCodegenFn create_codegen)
     63       : isa_(isa), create_codegen_(create_codegen) {
     64   }
     65   InstructionSet GetInstructionSet() const { return isa_; }
     66   CodeGenerator* CreateCodeGenerator(HGraph* graph, const CompilerOptions& compiler_options) {
     67     return create_codegen_(graph, compiler_options);
     68   }
     69 
     70  private:
     71   InstructionSet isa_;
     72   CreateCodegenFn create_codegen_;
     73 };
     74 
     75 #ifdef ART_ENABLE_CODEGEN_arm
     76 // Special ARM code generator for codegen testing in a limited code
     77 // generation environment (i.e. with no runtime support).
     78 //
     79 // Note: If we want to exercise certains HIR constructions
     80 // (e.g. reference field load in Baker read barrier configuration) in
     81 // codegen tests in the future, we should also:
     82 // - save the Thread Register (R9) and possibly the Marking Register
     83 //   (R8) before entering the generated function (both registers are
     84 //   callee-save in AAPCS);
     85 // - set these registers to meaningful values before or upon entering
     86 //   the generated function (so that generated code using them is
     87 //   correct);
     88 // - restore their original values before leaving the generated
     89 //   function.
     90 
     91 // Provide our own codegen, that ensures the C calling conventions
     92 // are preserved. Currently, ART and C do not match as R4 is caller-save
     93 // in ART, and callee-save in C. Alternatively, we could use or write
     94 // the stub that saves and restores all registers, but it is easier
     95 // to just overwrite the code generator.
     96 class TestCodeGeneratorARMVIXL : public arm::CodeGeneratorARMVIXL {
     97  public:
     98   TestCodeGeneratorARMVIXL(HGraph* graph, const CompilerOptions& compiler_options)
     99       : arm::CodeGeneratorARMVIXL(graph, compiler_options) {
    100     AddAllocatedRegister(Location::RegisterLocation(arm::R6));
    101     AddAllocatedRegister(Location::RegisterLocation(arm::R7));
    102   }
    103 
    104   void SetupBlockedRegisters() const override {
    105     arm::CodeGeneratorARMVIXL::SetupBlockedRegisters();
    106     blocked_core_registers_[arm::R4] = true;
    107     blocked_core_registers_[arm::R6] = false;
    108     blocked_core_registers_[arm::R7] = false;
    109   }
    110 
    111   void MaybeGenerateMarkingRegisterCheck(int code ATTRIBUTE_UNUSED,
    112                                          Location temp_loc ATTRIBUTE_UNUSED) override {
    113     // When turned on, the marking register checks in
    114     // CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck expects the
    115     // Thread Register and the Marking Register to be set to
    116     // meaningful values. This is not the case in codegen testing, so
    117     // just disable them entirely here (by doing nothing in this
    118     // method).
    119   }
    120 };
    121 #endif
    122 
    123 #ifdef ART_ENABLE_CODEGEN_arm64
    124 // Special ARM64 code generator for codegen testing in a limited code
    125 // generation environment (i.e. with no runtime support).
    126 //
    127 // Note: If we want to exercise certains HIR constructions
    128 // (e.g. reference field load in Baker read barrier configuration) in
    129 // codegen tests in the future, we should also:
    130 // - save the Thread Register (X19) and possibly the Marking Register
    131 //   (X20) before entering the generated function (both registers are
    132 //   callee-save in AAPCS64);
    133 // - set these registers to meaningful values before or upon entering
    134 //   the generated function (so that generated code using them is
    135 //   correct);
    136 // - restore their original values before leaving the generated
    137 //   function.
    138 class TestCodeGeneratorARM64 : public arm64::CodeGeneratorARM64 {
    139  public:
    140   TestCodeGeneratorARM64(HGraph* graph, const CompilerOptions& compiler_options)
    141       : arm64::CodeGeneratorARM64(graph, compiler_options) {}
    142 
    143   void MaybeGenerateMarkingRegisterCheck(int codem ATTRIBUTE_UNUSED,
    144                                          Location temp_loc ATTRIBUTE_UNUSED) override {
    145     // When turned on, the marking register checks in
    146     // CodeGeneratorARM64::MaybeGenerateMarkingRegisterCheck expect the
    147     // Thread Register and the Marking Register to be set to
    148     // meaningful values. This is not the case in codegen testing, so
    149     // just disable them entirely here (by doing nothing in this
    150     // method).
    151   }
    152 };
    153 #endif
    154 
    155 #ifdef ART_ENABLE_CODEGEN_x86
    156 class TestCodeGeneratorX86 : public x86::CodeGeneratorX86 {
    157  public:
    158   TestCodeGeneratorX86(HGraph* graph, const CompilerOptions& compiler_options)
    159       : x86::CodeGeneratorX86(graph, compiler_options) {
    160     // Save edi, we need it for getting enough registers for long multiplication.
    161     AddAllocatedRegister(Location::RegisterLocation(x86::EDI));
    162   }
    163 
    164   void SetupBlockedRegisters() const override {
    165     x86::CodeGeneratorX86::SetupBlockedRegisters();
    166     // ebx is a callee-save register in C, but caller-save for ART.
    167     blocked_core_registers_[x86::EBX] = true;
    168 
    169     // Make edi available.
    170     blocked_core_registers_[x86::EDI] = false;
    171   }
    172 };
    173 #endif
    174 
    175 class InternalCodeAllocator : public CodeAllocator {
    176  public:
    177   InternalCodeAllocator() : size_(0) { }
    178 
    179   uint8_t* Allocate(size_t size) override {
    180     size_ = size;
    181     memory_.reset(new uint8_t[size]);
    182     return memory_.get();
    183   }
    184 
    185   size_t GetSize() const { return size_; }
    186   ArrayRef<const uint8_t> GetMemory() const override {
    187     return ArrayRef<const uint8_t>(memory_.get(), size_);
    188   }
    189 
    190  private:
    191   size_t size_;
    192   std::unique_ptr<uint8_t[]> memory_;
    193 
    194   DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator);
    195 };
    196 
    197 static bool CanExecuteOnHardware(InstructionSet target_isa) {
    198   return (target_isa == kRuntimeISA)
    199       // Handle the special case of ARM, with two instructions sets (ARM32 and Thumb-2).
    200       || (kRuntimeISA == InstructionSet::kArm && target_isa == InstructionSet::kThumb2);
    201 }
    202 
    203 static bool CanExecute(InstructionSet target_isa) {
    204   CodeSimulatorContainer simulator(target_isa);
    205   return CanExecuteOnHardware(target_isa) || simulator.CanSimulate();
    206 }
    207 
    208 template <typename Expected>
    209 inline static Expected SimulatorExecute(CodeSimulator* simulator, Expected (*f)());
    210 
    211 template <>
    212 inline bool SimulatorExecute<bool>(CodeSimulator* simulator, bool (*f)()) {
    213   simulator->RunFrom(reinterpret_cast<intptr_t>(f));
    214   return simulator->GetCReturnBool();
    215 }
    216 
    217 template <>
    218 inline int32_t SimulatorExecute<int32_t>(CodeSimulator* simulator, int32_t (*f)()) {
    219   simulator->RunFrom(reinterpret_cast<intptr_t>(f));
    220   return simulator->GetCReturnInt32();
    221 }
    222 
    223 template <>
    224 inline int64_t SimulatorExecute<int64_t>(CodeSimulator* simulator, int64_t (*f)()) {
    225   simulator->RunFrom(reinterpret_cast<intptr_t>(f));
    226   return simulator->GetCReturnInt64();
    227 }
    228 
    229 template <typename Expected>
    230 static void VerifyGeneratedCode(InstructionSet target_isa,
    231                                 Expected (*f)(),
    232                                 bool has_result,
    233                                 Expected expected) {
    234   ASSERT_TRUE(CanExecute(target_isa)) << "Target isa is not executable.";
    235 
    236   // Verify on simulator.
    237   CodeSimulatorContainer simulator(target_isa);
    238   if (simulator.CanSimulate()) {
    239     Expected result = SimulatorExecute<Expected>(simulator.Get(), f);
    240     if (has_result) {
    241       ASSERT_EQ(expected, result);
    242     }
    243   }
    244 
    245   // Verify on hardware.
    246   if (CanExecuteOnHardware(target_isa)) {
    247     Expected result = f();
    248     if (has_result) {
    249       ASSERT_EQ(expected, result);
    250     }
    251   }
    252 }
    253 
    254 template <typename Expected>
    255 static void Run(const InternalCodeAllocator& allocator,
    256                 const CodeGenerator& codegen,
    257                 bool has_result,
    258                 Expected expected) {
    259   InstructionSet target_isa = codegen.GetInstructionSet();
    260 
    261   typedef Expected (*fptr)();
    262   CommonCompilerTest::MakeExecutable(allocator.GetMemory().data(), allocator.GetMemory().size());
    263   fptr f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(allocator.GetMemory().data()));
    264   if (target_isa == InstructionSet::kThumb2) {
    265     // For thumb we need the bottom bit set.
    266     f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(f) + 1);
    267   }
    268   VerifyGeneratedCode(target_isa, f, has_result, expected);
    269 }
    270 
    271 static void ValidateGraph(HGraph* graph) {
    272   GraphChecker graph_checker(graph);
    273   graph_checker.Run();
    274   if (!graph_checker.IsValid()) {
    275     for (const std::string& error : graph_checker.GetErrors()) {
    276       std::cout << error << std::endl;
    277     }
    278   }
    279   ASSERT_TRUE(graph_checker.IsValid());
    280 }
    281 
    282 template <typename Expected>
    283 static void RunCodeNoCheck(CodeGenerator* codegen,
    284                            HGraph* graph,
    285                            const std::function<void(HGraph*)>& hook_before_codegen,
    286                            bool has_result,
    287                            Expected expected) {
    288   {
    289     ScopedArenaAllocator local_allocator(graph->GetArenaStack());
    290     SsaLivenessAnalysis liveness(graph, codegen, &local_allocator);
    291     PrepareForRegisterAllocation(graph, codegen->GetCompilerOptions()).Run();
    292     liveness.Analyze();
    293     std::unique_ptr<RegisterAllocator> register_allocator =
    294         RegisterAllocator::Create(&local_allocator, codegen, liveness);
    295     register_allocator->AllocateRegisters();
    296   }
    297   hook_before_codegen(graph);
    298   InternalCodeAllocator allocator;
    299   codegen->Compile(&allocator);
    300   Run(allocator, *codegen, has_result, expected);
    301 }
    302 
    303 template <typename Expected>
    304 static void RunCode(CodeGenerator* codegen,
    305                     HGraph* graph,
    306                     std::function<void(HGraph*)> hook_before_codegen,
    307                     bool has_result,
    308                     Expected expected) {
    309   ValidateGraph(graph);
    310   RunCodeNoCheck(codegen, graph, hook_before_codegen, has_result, expected);
    311 }
    312 
    313 template <typename Expected>
    314 static void RunCode(CodegenTargetConfig target_config,
    315                     const CompilerOptions& compiler_options,
    316                     HGraph* graph,
    317                     std::function<void(HGraph*)> hook_before_codegen,
    318                     bool has_result,
    319                     Expected expected) {
    320   std::unique_ptr<CodeGenerator> codegen(target_config.CreateCodeGenerator(graph,
    321                                                                            compiler_options));
    322   RunCode(codegen.get(), graph, hook_before_codegen, has_result, expected);
    323 }
    324 
    325 #ifdef ART_ENABLE_CODEGEN_arm
    326 CodeGenerator* create_codegen_arm_vixl32(HGraph* graph, const CompilerOptions& compiler_options) {
    327   return new (graph->GetAllocator()) TestCodeGeneratorARMVIXL(graph, compiler_options);
    328 }
    329 #endif
    330 
    331 #ifdef ART_ENABLE_CODEGEN_arm64
    332 CodeGenerator* create_codegen_arm64(HGraph* graph, const CompilerOptions& compiler_options) {
    333   return new (graph->GetAllocator()) TestCodeGeneratorARM64(graph, compiler_options);
    334 }
    335 #endif
    336 
    337 #ifdef ART_ENABLE_CODEGEN_x86
    338 CodeGenerator* create_codegen_x86(HGraph* graph, const CompilerOptions& compiler_options) {
    339   return new (graph->GetAllocator()) TestCodeGeneratorX86(graph, compiler_options);
    340 }
    341 #endif
    342 
    343 #ifdef ART_ENABLE_CODEGEN_x86_64
    344 CodeGenerator* create_codegen_x86_64(HGraph* graph, const CompilerOptions& compiler_options) {
    345   return new (graph->GetAllocator()) x86_64::CodeGeneratorX86_64(graph, compiler_options);
    346 }
    347 #endif
    348 
    349 #ifdef ART_ENABLE_CODEGEN_mips
    350 CodeGenerator* create_codegen_mips(HGraph* graph, const CompilerOptions& compiler_options) {
    351   return new (graph->GetAllocator()) mips::CodeGeneratorMIPS(graph, compiler_options);
    352 }
    353 #endif
    354 
    355 #ifdef ART_ENABLE_CODEGEN_mips64
    356 CodeGenerator* create_codegen_mips64(HGraph* graph, const CompilerOptions& compiler_options) {
    357   return new (graph->GetAllocator()) mips64::CodeGeneratorMIPS64(graph, compiler_options);
    358 }
    359 #endif
    360 
    361 }  // namespace art
    362 
    363 #endif  // ART_COMPILER_OPTIMIZING_CODEGEN_TEST_UTILS_H_
    364