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