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