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,Location temp_loc)106 void MaybeGenerateMarkingRegisterCheck([[maybe_unused]] int code,
107 [[maybe_unused]] Location temp_loc) 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,Location temp_loc)138 void MaybeGenerateMarkingRegisterCheck([[maybe_unused]] int codem,
139 [[maybe_unused]] Location temp_loc) 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 // Check that the current runtime ISA matches the target ISA.
DoesHardwareSupportISA(InstructionSet target_isa)171 static bool DoesHardwareSupportISA(InstructionSet target_isa) {
172 return (target_isa == kRuntimeISA)
173 // Handle the special case of ARM, with two instructions sets (ARM32 and Thumb-2).
174 || (kRuntimeISA == InstructionSet::kArm && target_isa == InstructionSet::kThumb2);
175 }
176
177 // Check that the current runtime ISA matches the target ISA and the ISA features requested are
178 // available on the hardware.
CanExecuteOnHardware(const CodeGenerator & codegen)179 static bool CanExecuteOnHardware(const CodeGenerator& codegen) {
180 const InstructionSetFeatures* isa_features =
181 codegen.GetCompilerOptions().GetInstructionSetFeatures();
182 return DoesHardwareSupportISA(codegen.GetInstructionSet())
183 && InstructionSetFeatures::FromHwcap()->HasAtLeast(isa_features);
184 }
185
CanExecuteISA(InstructionSet target_isa)186 static bool CanExecuteISA(InstructionSet target_isa) {
187 CodeSimulatorContainer simulator(target_isa);
188 return DoesHardwareSupportISA(target_isa) || simulator.CanSimulate();
189 }
190
CanExecute(const CodeGenerator & codegen)191 static bool CanExecute(const CodeGenerator& codegen) {
192 CodeSimulatorContainer simulator(codegen.GetInstructionSet());
193 return CanExecuteOnHardware(codegen) || simulator.CanSimulate();
194 }
195
196 template <typename Expected>
197 inline static Expected SimulatorExecute(CodeSimulator* simulator, Expected (*f)());
198
199 template <>
200 inline bool SimulatorExecute<bool>(CodeSimulator* simulator, bool (*f)()) {
201 simulator->RunFrom(reinterpret_cast<intptr_t>(f));
202 return simulator->GetCReturnBool();
203 }
204
205 template <>
206 inline int32_t SimulatorExecute<int32_t>(CodeSimulator* simulator, int32_t (*f)()) {
207 simulator->RunFrom(reinterpret_cast<intptr_t>(f));
208 return simulator->GetCReturnInt32();
209 }
210
211 template <>
212 inline int64_t SimulatorExecute<int64_t>(CodeSimulator* simulator, int64_t (*f)()) {
213 simulator->RunFrom(reinterpret_cast<intptr_t>(f));
214 return simulator->GetCReturnInt64();
215 }
216
217 template <typename Expected>
VerifyGeneratedCode(const CodeGenerator & codegen,Expected (* f)(),bool has_result,Expected expected)218 static void VerifyGeneratedCode(const CodeGenerator& codegen,
219 Expected (*f)(),
220 bool has_result,
221 Expected expected) {
222 ASSERT_TRUE(CanExecute(codegen)) << "Target isa is not executable.";
223
224 // Verify on simulator.
225 CodeSimulatorContainer simulator(codegen.GetInstructionSet());
226 if (simulator.CanSimulate()) {
227 Expected result = SimulatorExecute<Expected>(simulator.Get(), f);
228 if (has_result) {
229 ASSERT_EQ(expected, result);
230 }
231 }
232
233 // Verify on hardware.
234 if (CanExecuteOnHardware(codegen)) {
235 Expected result = f();
236 if (has_result) {
237 ASSERT_EQ(expected, result);
238 }
239 }
240 }
241
242 template <typename Expected>
Run(const CodeGenerator & codegen,bool has_result,Expected expected)243 static void Run(const CodeGenerator& codegen,
244 bool has_result,
245 Expected expected) {
246 InstructionSet target_isa = codegen.GetInstructionSet();
247
248 struct CodeHolder : CommonCompilerTestImpl {
249 protected:
250 ClassLinker* GetClassLinker() override { return nullptr; }
251 Runtime* GetRuntime() override { return nullptr; }
252 };
253 CodeHolder code_holder;
254 const void* method_code =
255 code_holder.MakeExecutable(codegen.GetCode(), ArrayRef<const uint8_t>(), target_isa);
256
257 using fptr = Expected (*)();
258 fptr f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(method_code));
259 VerifyGeneratedCode(codegen, f, has_result, expected);
260 }
261
ValidateGraph(HGraph * graph)262 static void ValidateGraph(HGraph* graph) {
263 GraphChecker graph_checker(graph);
264 graph_checker.Run();
265 if (!graph_checker.IsValid()) {
266 for (const std::string& error : graph_checker.GetErrors()) {
267 std::cout << error << std::endl;
268 }
269 }
270 ASSERT_TRUE(graph_checker.IsValid());
271 }
272
273 template <typename Expected>
RunCodeNoCheck(CodeGenerator * codegen,HGraph * graph,const std::function<void (HGraph *)> & hook_before_codegen,bool has_result,Expected expected)274 static void RunCodeNoCheck(CodeGenerator* codegen,
275 HGraph* graph,
276 const std::function<void(HGraph*)>& hook_before_codegen,
277 bool has_result,
278 Expected expected) {
279 {
280 ScopedArenaAllocator local_allocator(graph->GetArenaStack());
281 SsaLivenessAnalysis liveness(graph, codegen, &local_allocator);
282 PrepareForRegisterAllocation(graph, codegen->GetCompilerOptions()).Run();
283 liveness.Analyze();
284 std::unique_ptr<RegisterAllocator> register_allocator =
285 RegisterAllocator::Create(&local_allocator, codegen, liveness);
286 register_allocator->AllocateRegisters();
287 }
288 hook_before_codegen(graph);
289 codegen->Compile();
290 Run(*codegen, has_result, expected);
291 }
292
293 template <typename Expected>
RunCode(CodeGenerator * codegen,HGraph * graph,std::function<void (HGraph *)> hook_before_codegen,bool has_result,Expected expected)294 static void RunCode(CodeGenerator* codegen,
295 HGraph* graph,
296 std::function<void(HGraph*)> hook_before_codegen,
297 bool has_result,
298 Expected expected) {
299 ValidateGraph(graph);
300 RunCodeNoCheck(codegen, graph, hook_before_codegen, has_result, expected);
301 }
302
303 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)304 static void RunCode(CodegenTargetConfig target_config,
305 const CompilerOptions& compiler_options,
306 HGraph* graph,
307 std::function<void(HGraph*)> hook_before_codegen,
308 bool has_result,
309 Expected expected) {
310 std::unique_ptr<CodeGenerator> codegen(target_config.CreateCodeGenerator(graph,
311 compiler_options));
312 RunCode(codegen.get(), graph, hook_before_codegen, has_result, expected);
313 }
314
315 #ifdef ART_ENABLE_CODEGEN_arm
create_codegen_arm_vixl32(HGraph * graph,const CompilerOptions & compiler_options)316 inline CodeGenerator* create_codegen_arm_vixl32(HGraph* graph, const CompilerOptions& compiler_options) {
317 return new (graph->GetAllocator()) TestCodeGeneratorARMVIXL(graph, compiler_options);
318 }
319 #endif
320
321 #ifdef ART_ENABLE_CODEGEN_arm64
create_codegen_arm64(HGraph * graph,const CompilerOptions & compiler_options)322 inline CodeGenerator* create_codegen_arm64(HGraph* graph, const CompilerOptions& compiler_options) {
323 return new (graph->GetAllocator()) TestCodeGeneratorARM64(graph, compiler_options);
324 }
325 #endif
326
327 #ifdef ART_ENABLE_CODEGEN_riscv64
create_codegen_riscv64(HGraph *,const CompilerOptions &)328 inline CodeGenerator* create_codegen_riscv64(HGraph*, const CompilerOptions&) { return nullptr; }
329 #endif
330
331 #ifdef ART_ENABLE_CODEGEN_x86
create_codegen_x86(HGraph * graph,const CompilerOptions & compiler_options)332 inline CodeGenerator* create_codegen_x86(HGraph* graph, const CompilerOptions& compiler_options) {
333 return new (graph->GetAllocator()) TestCodeGeneratorX86(graph, compiler_options);
334 }
335 #endif
336
337 #ifdef ART_ENABLE_CODEGEN_x86_64
create_codegen_x86_64(HGraph * graph,const CompilerOptions & compiler_options)338 inline CodeGenerator* create_codegen_x86_64(HGraph* graph, const CompilerOptions& compiler_options) {
339 return new (graph->GetAllocator()) x86_64::CodeGeneratorX86_64(graph, compiler_options);
340 }
341 #endif
342
343 } // namespace art
344
345 #endif // ART_COMPILER_OPTIMIZING_CODEGEN_TEST_UTILS_H_
346