blob: f873e7514edfd5b7d1fc53ff53d135bd88a3a17e [file] [log] [blame]
Alexandre Rames22aa54b2016-10-18 09:32:29 +01001/*
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
Alexandre Rames22aa54b2016-10-18 09:32:29 +010020#include "arch/arm/registers_arm.h"
Alexandre Rames22aa54b2016-10-18 09:32:29 +010021#include "arch/instruction_set.h"
Alexandre Rames22aa54b2016-10-18 09:32:29 +010022#include "arch/x86/registers_x86.h"
Andreas Gampe71da4872017-07-26 10:02:07 -070023#include "code_simulator.h"
Alexandre Rames22aa54b2016-10-18 09:32:29 +010024#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
Alexandre Rames22aa54b2016-10-18 09:32:29 +010031#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
Vladimir Marko0a516052019-10-14 13:00:44 +000046namespace art {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010047
48typedef CodeGenerator* (*CreateCodegenFn)(HGraph*, const CompilerOptions&);
49
50class CodegenTargetConfig {
51 public:
52 CodegenTargetConfig(InstructionSet isa, CreateCodegenFn create_codegen)
53 : isa_(isa), create_codegen_(create_codegen) {
54 }
55 InstructionSet GetInstructionSet() const { return isa_; }
56 CodeGenerator* CreateCodeGenerator(HGraph* graph, const CompilerOptions& compiler_options) {
57 return create_codegen_(graph, compiler_options);
58 }
59
60 private:
Alexandre Rames22aa54b2016-10-18 09:32:29 +010061 InstructionSet isa_;
62 CreateCodegenFn create_codegen_;
63};
64
65#ifdef ART_ENABLE_CODEGEN_arm
Roland Levillain5daa4952017-07-03 17:23:56 +010066// 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
Alexandre Rames22aa54b2016-10-18 09:32:29 +010081// 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.
Alexandre Rames22aa54b2016-10-18 09:32:29 +010086class TestCodeGeneratorARMVIXL : public arm::CodeGeneratorARMVIXL {
87 public:
Vladimir Markoa0431112018-06-25 09:32:54 +010088 TestCodeGeneratorARMVIXL(HGraph* graph, const CompilerOptions& compiler_options)
89 : arm::CodeGeneratorARMVIXL(graph, compiler_options) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010090 AddAllocatedRegister(Location::RegisterLocation(arm::R6));
91 AddAllocatedRegister(Location::RegisterLocation(arm::R7));
92 }
93
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010094 void SetupBlockedRegisters() const override {
Alexandre Rames22aa54b2016-10-18 09:32:29 +010095 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 }
Roland Levillain5daa4952017-07-03 17:23:56 +0100100
101 void MaybeGenerateMarkingRegisterCheck(int code ATTRIBUTE_UNUSED,
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100102 Location temp_loc ATTRIBUTE_UNUSED) override {
Roland Levillain5daa4952017-07-03 17:23:56 +0100103 // 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 }
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100110};
111#endif
112
Roland Levillain2b03a1f2017-06-06 16:09:59 +0100113#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.
128class TestCodeGeneratorARM64 : public arm64::CodeGeneratorARM64 {
129 public:
Vladimir Markoa0431112018-06-25 09:32:54 +0100130 TestCodeGeneratorARM64(HGraph* graph, const CompilerOptions& compiler_options)
131 : arm64::CodeGeneratorARM64(graph, compiler_options) {}
Roland Levillain2b03a1f2017-06-06 16:09:59 +0100132
133 void MaybeGenerateMarkingRegisterCheck(int codem ATTRIBUTE_UNUSED,
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100134 Location temp_loc ATTRIBUTE_UNUSED) override {
Roland Levillain2b03a1f2017-06-06 16:09:59 +0100135 // 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
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100145#ifdef ART_ENABLE_CODEGEN_x86
146class TestCodeGeneratorX86 : public x86::CodeGeneratorX86 {
147 public:
Vladimir Markoa0431112018-06-25 09:32:54 +0100148 TestCodeGeneratorX86(HGraph* graph, const CompilerOptions& compiler_options)
149 : x86::CodeGeneratorX86(graph, compiler_options) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100150 // Save edi, we need it for getting enough registers for long multiplication.
151 AddAllocatedRegister(Location::RegisterLocation(x86::EDI));
152 }
153
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100154 void SetupBlockedRegisters() const override {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100155 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
165class InternalCodeAllocator : public CodeAllocator {
166 public:
167 InternalCodeAllocator() : size_(0) { }
168
Yi Kong39402542019-03-24 02:47:16 -0700169 uint8_t* Allocate(size_t size) override {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100170 size_ = size;
171 memory_.reset(new uint8_t[size]);
172 return memory_.get();
173 }
174
175 size_t GetSize() const { return size_; }
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100176 ArrayRef<const uint8_t> GetMemory() const override {
Vladimir Markoca1e0382018-04-11 09:58:41 +0000177 return ArrayRef<const uint8_t>(memory_.get(), size_);
178 }
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100179
180 private:
181 size_t size_;
182 std::unique_ptr<uint8_t[]> memory_;
183
184 DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator);
185};
186
187static 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).
Vladimir Marko33bff252017-11-01 14:35:42 +0000190 || (kRuntimeISA == InstructionSet::kArm && target_isa == InstructionSet::kThumb2);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100191}
192
193static bool CanExecute(InstructionSet target_isa) {
194 CodeSimulatorContainer simulator(target_isa);
195 return CanExecuteOnHardware(target_isa) || simulator.CanSimulate();
196}
197
198template <typename Expected>
199inline static Expected SimulatorExecute(CodeSimulator* simulator, Expected (*f)());
200
201template <>
202inline bool SimulatorExecute<bool>(CodeSimulator* simulator, bool (*f)()) {
203 simulator->RunFrom(reinterpret_cast<intptr_t>(f));
204 return simulator->GetCReturnBool();
205}
206
207template <>
208inline int32_t SimulatorExecute<int32_t>(CodeSimulator* simulator, int32_t (*f)()) {
209 simulator->RunFrom(reinterpret_cast<intptr_t>(f));
210 return simulator->GetCReturnInt32();
211}
212
213template <>
214inline int64_t SimulatorExecute<int64_t>(CodeSimulator* simulator, int64_t (*f)()) {
215 simulator->RunFrom(reinterpret_cast<intptr_t>(f));
216 return simulator->GetCReturnInt64();
217}
218
219template <typename Expected>
220static 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
Ulyana Trafimoviche886d682020-07-16 15:09:38 +0000226 // Simulator cannot run without runtime, because it needs quick entrypoints.
227 if (Runtime::Current() != nullptr) {
228 // Verify on simulator.
229 CodeSimulatorContainer simulator(target_isa);
230 if (simulator.CanSimulate()) {
231 Expected result = SimulatorExecute<Expected>(simulator.Get(), f);
232 if (has_result) {
233 ASSERT_EQ(expected, result);
234 }
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100235 }
236 }
237
238 // Verify on hardware.
239 if (CanExecuteOnHardware(target_isa)) {
240 Expected result = f();
241 if (has_result) {
242 ASSERT_EQ(expected, result);
243 }
244 }
245}
246
247template <typename Expected>
248static void Run(const InternalCodeAllocator& allocator,
249 const CodeGenerator& codegen,
250 bool has_result,
251 Expected expected) {
252 InstructionSet target_isa = codegen.GetInstructionSet();
253
254 typedef Expected (*fptr)();
Vladimir Markoca1e0382018-04-11 09:58:41 +0000255 CommonCompilerTest::MakeExecutable(allocator.GetMemory().data(), allocator.GetMemory().size());
256 fptr f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(allocator.GetMemory().data()));
Vladimir Marko33bff252017-11-01 14:35:42 +0000257 if (target_isa == InstructionSet::kThumb2) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100258 // For thumb we need the bottom bit set.
259 f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(f) + 1);
260 }
261 VerifyGeneratedCode(target_isa, f, has_result, expected);
262}
263
264static void ValidateGraph(HGraph* graph) {
265 GraphChecker graph_checker(graph);
266 graph_checker.Run();
267 if (!graph_checker.IsValid()) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +0100268 for (const std::string& error : graph_checker.GetErrors()) {
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100269 std::cout << error << std::endl;
270 }
271 }
272 ASSERT_TRUE(graph_checker.IsValid());
273}
274
275template <typename Expected>
276static void RunCodeNoCheck(CodeGenerator* codegen,
277 HGraph* graph,
278 const std::function<void(HGraph*)>& hook_before_codegen,
279 bool has_result,
280 Expected expected) {
Vladimir Markoe764d2e2017-10-05 14:35:55 +0100281 {
282 ScopedArenaAllocator local_allocator(graph->GetArenaStack());
283 SsaLivenessAnalysis liveness(graph, codegen, &local_allocator);
Nicolas Geoffray61ba8d22018-08-07 09:55:57 +0100284 PrepareForRegisterAllocation(graph, codegen->GetCompilerOptions()).Run();
Vladimir Markoe764d2e2017-10-05 14:35:55 +0100285 liveness.Analyze();
286 std::unique_ptr<RegisterAllocator> register_allocator =
287 RegisterAllocator::Create(&local_allocator, codegen, liveness);
288 register_allocator->AllocateRegisters();
289 }
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100290 hook_before_codegen(graph);
291 InternalCodeAllocator allocator;
292 codegen->Compile(&allocator);
293 Run(allocator, *codegen, has_result, expected);
294}
295
296template <typename Expected>
297static void RunCode(CodeGenerator* codegen,
298 HGraph* graph,
299 std::function<void(HGraph*)> hook_before_codegen,
300 bool has_result,
301 Expected expected) {
302 ValidateGraph(graph);
303 RunCodeNoCheck(codegen, graph, hook_before_codegen, has_result, expected);
304}
305
306template <typename Expected>
307static void RunCode(CodegenTargetConfig target_config,
Vladimir Markoa0431112018-06-25 09:32:54 +0100308 const CompilerOptions& compiler_options,
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100309 HGraph* graph,
310 std::function<void(HGraph*)> hook_before_codegen,
311 bool has_result,
312 Expected expected) {
Roland Levillain2b03a1f2017-06-06 16:09:59 +0100313 std::unique_ptr<CodeGenerator> codegen(target_config.CreateCodeGenerator(graph,
314 compiler_options));
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100315 RunCode(codegen.get(), graph, hook_before_codegen, has_result, expected);
316}
317
318#ifdef ART_ENABLE_CODEGEN_arm
David Srbecky883c1342020-05-11 23:30:29 +0000319inline CodeGenerator* create_codegen_arm_vixl32(HGraph* graph, const CompilerOptions& compiler_options) {
Vladimir Markoa0431112018-06-25 09:32:54 +0100320 return new (graph->GetAllocator()) TestCodeGeneratorARMVIXL(graph, compiler_options);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100321}
322#endif
323
324#ifdef ART_ENABLE_CODEGEN_arm64
David Srbecky883c1342020-05-11 23:30:29 +0000325inline CodeGenerator* create_codegen_arm64(HGraph* graph, const CompilerOptions& compiler_options) {
Vladimir Markoa0431112018-06-25 09:32:54 +0100326 return new (graph->GetAllocator()) TestCodeGeneratorARM64(graph, compiler_options);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100327}
328#endif
329
330#ifdef ART_ENABLE_CODEGEN_x86
David Srbecky883c1342020-05-11 23:30:29 +0000331inline CodeGenerator* create_codegen_x86(HGraph* graph, const CompilerOptions& compiler_options) {
Vladimir Markoa0431112018-06-25 09:32:54 +0100332 return new (graph->GetAllocator()) TestCodeGeneratorX86(graph, compiler_options);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100333}
334#endif
335
336#ifdef ART_ENABLE_CODEGEN_x86_64
David Srbecky883c1342020-05-11 23:30:29 +0000337inline CodeGenerator* create_codegen_x86_64(HGraph* graph, const CompilerOptions& compiler_options) {
Vladimir Markoa0431112018-06-25 09:32:54 +0100338 return new (graph->GetAllocator()) x86_64::CodeGeneratorX86_64(graph, compiler_options);
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100339}
340#endif
341
Alexandre Rames22aa54b2016-10-18 09:32:29 +0100342} // namespace art
343
344#endif // ART_COMPILER_OPTIMIZING_CODEGEN_TEST_UTILS_H_