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:
CodegenTargetConfig(InstructionSet isa,CreateCodegenFn create_codegen)62   CodegenTargetConfig(InstructionSet isa, CreateCodegenFn create_codegen)
63       : isa_(isa), create_codegen_(create_codegen) {
64   }
GetInstructionSet()65   InstructionSet GetInstructionSet() const { return isa_; }
CreateCodeGenerator(HGraph * graph,const CompilerOptions & compiler_options)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:
TestCodeGeneratorARMVIXL(HGraph * graph,const CompilerOptions & compiler_options)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 
SetupBlockedRegisters()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 
MaybeGenerateMarkingRegisterCheck(int code ATTRIBUTE_UNUSED,Location temp_loc ATTRIBUTE_UNUSED)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:
TestCodeGeneratorARM64(HGraph * graph,const CompilerOptions & compiler_options)140   TestCodeGeneratorARM64(HGraph* graph, const CompilerOptions& compiler_options)
141       : arm64::CodeGeneratorARM64(graph, compiler_options) {}
142 
MaybeGenerateMarkingRegisterCheck(int codem ATTRIBUTE_UNUSED,Location temp_loc ATTRIBUTE_UNUSED)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:
TestCodeGeneratorX86(HGraph * graph,const CompilerOptions & compiler_options)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 
SetupBlockedRegisters()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:
InternalCodeAllocator()177   InternalCodeAllocator() : size_(0) { }
178 
Allocate(size_t size)179   uint8_t* Allocate(size_t size) override {
180     size_ = size;
181     memory_.reset(new uint8_t[size]);
182     return memory_.get();
183   }
184 
GetSize()185   size_t GetSize() const { return size_; }
GetMemory()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 
CanExecuteOnHardware(InstructionSet target_isa)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 
CanExecute(InstructionSet target_isa)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>
VerifyGeneratedCode(InstructionSet target_isa,Expected (* f)(),bool has_result,Expected 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>
Run(const InternalCodeAllocator & allocator,const CodeGenerator & codegen,bool has_result,Expected 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 
ValidateGraph(HGraph * graph)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>
RunCodeNoCheck(CodeGenerator * codegen,HGraph * graph,const std::function<void (HGraph *)> & hook_before_codegen,bool has_result,Expected 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>
RunCode(CodeGenerator * codegen,HGraph * graph,std::function<void (HGraph *)> hook_before_codegen,bool has_result,Expected 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>
RunCode(CodegenTargetConfig target_config,const CompilerOptions & compiler_options,HGraph * graph,std::function<void (HGraph *)> hook_before_codegen,bool has_result,Expected 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
create_codegen_arm_vixl32(HGraph * graph,const CompilerOptions & compiler_options)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
create_codegen_arm64(HGraph * graph,const CompilerOptions & compiler_options)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
create_codegen_x86(HGraph * graph,const CompilerOptions & compiler_options)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
create_codegen_x86_64(HGraph * graph,const CompilerOptions & compiler_options)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
create_codegen_mips(HGraph * graph,const CompilerOptions & compiler_options)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
create_codegen_mips64(HGraph * graph,const CompilerOptions & compiler_options)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