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