/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ART_COMPILER_OPTIMIZING_CODEGEN_TEST_UTILS_H_ #define ART_COMPILER_OPTIMIZING_CODEGEN_TEST_UTILS_H_ #include "arch/arm/registers_arm.h" #include "arch/instruction_set.h" #include "arch/mips/registers_mips.h" #include "arch/mips64/registers_mips64.h" #include "arch/x86/registers_x86.h" #include "code_simulator.h" #include "code_simulator_container.h" #include "common_compiler_test.h" #include "graph_checker.h" #include "prepare_for_register_allocation.h" #include "ssa_liveness_analysis.h" #ifdef ART_ENABLE_CODEGEN_arm #include "code_generator_arm_vixl.h" #endif #ifdef ART_ENABLE_CODEGEN_arm64 #include "code_generator_arm64.h" #endif #ifdef ART_ENABLE_CODEGEN_x86 #include "code_generator_x86.h" #endif #ifdef ART_ENABLE_CODEGEN_x86_64 #include "code_generator_x86_64.h" #endif #ifdef ART_ENABLE_CODEGEN_mips #include "code_generator_mips.h" #endif #ifdef ART_ENABLE_CODEGEN_mips64 #include "code_generator_mips64.h" #endif namespace art { typedef CodeGenerator* (*CreateCodegenFn)(HGraph*, const CompilerOptions&); class CodegenTargetConfig { public: CodegenTargetConfig(InstructionSet isa, CreateCodegenFn create_codegen) : isa_(isa), create_codegen_(create_codegen) { } InstructionSet GetInstructionSet() const { return isa_; } CodeGenerator* CreateCodeGenerator(HGraph* graph, const CompilerOptions& compiler_options) { return create_codegen_(graph, compiler_options); } private: InstructionSet isa_; CreateCodegenFn create_codegen_; }; #ifdef ART_ENABLE_CODEGEN_arm // Special ARM code generator for codegen testing in a limited code // generation environment (i.e. with no runtime support). // // Note: If we want to exercise certains HIR constructions // (e.g. reference field load in Baker read barrier configuration) in // codegen tests in the future, we should also: // - save the Thread Register (R9) and possibly the Marking Register // (R8) before entering the generated function (both registers are // callee-save in AAPCS); // - set these registers to meaningful values before or upon entering // the generated function (so that generated code using them is // correct); // - restore their original values before leaving the generated // function. // Provide our own codegen, that ensures the C calling conventions // are preserved. Currently, ART and C do not match as R4 is caller-save // in ART, and callee-save in C. Alternatively, we could use or write // the stub that saves and restores all registers, but it is easier // to just overwrite the code generator. class TestCodeGeneratorARMVIXL : public arm::CodeGeneratorARMVIXL { public: TestCodeGeneratorARMVIXL(HGraph* graph, const CompilerOptions& compiler_options) : arm::CodeGeneratorARMVIXL(graph, compiler_options) { AddAllocatedRegister(Location::RegisterLocation(arm::R6)); AddAllocatedRegister(Location::RegisterLocation(arm::R7)); } void SetupBlockedRegisters() const override { arm::CodeGeneratorARMVIXL::SetupBlockedRegisters(); blocked_core_registers_[arm::R4] = true; blocked_core_registers_[arm::R6] = false; blocked_core_registers_[arm::R7] = false; } void MaybeGenerateMarkingRegisterCheck(int code ATTRIBUTE_UNUSED, Location temp_loc ATTRIBUTE_UNUSED) override { // When turned on, the marking register checks in // CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck expects the // Thread Register and the Marking Register to be set to // meaningful values. This is not the case in codegen testing, so // just disable them entirely here (by doing nothing in this // method). } }; #endif #ifdef ART_ENABLE_CODEGEN_arm64 // Special ARM64 code generator for codegen testing in a limited code // generation environment (i.e. with no runtime support). // // Note: If we want to exercise certains HIR constructions // (e.g. reference field load in Baker read barrier configuration) in // codegen tests in the future, we should also: // - save the Thread Register (X19) and possibly the Marking Register // (X20) before entering the generated function (both registers are // callee-save in AAPCS64); // - set these registers to meaningful values before or upon entering // the generated function (so that generated code using them is // correct); // - restore their original values before leaving the generated // function. class TestCodeGeneratorARM64 : public arm64::CodeGeneratorARM64 { public: TestCodeGeneratorARM64(HGraph* graph, const CompilerOptions& compiler_options) : arm64::CodeGeneratorARM64(graph, compiler_options) {} void MaybeGenerateMarkingRegisterCheck(int codem ATTRIBUTE_UNUSED, Location temp_loc ATTRIBUTE_UNUSED) override { // When turned on, the marking register checks in // CodeGeneratorARM64::MaybeGenerateMarkingRegisterCheck expect the // Thread Register and the Marking Register to be set to // meaningful values. This is not the case in codegen testing, so // just disable them entirely here (by doing nothing in this // method). } }; #endif #ifdef ART_ENABLE_CODEGEN_x86 class TestCodeGeneratorX86 : public x86::CodeGeneratorX86 { public: TestCodeGeneratorX86(HGraph* graph, const CompilerOptions& compiler_options) : x86::CodeGeneratorX86(graph, compiler_options) { // Save edi, we need it for getting enough registers for long multiplication. AddAllocatedRegister(Location::RegisterLocation(x86::EDI)); } void SetupBlockedRegisters() const override { x86::CodeGeneratorX86::SetupBlockedRegisters(); // ebx is a callee-save register in C, but caller-save for ART. blocked_core_registers_[x86::EBX] = true; // Make edi available. blocked_core_registers_[x86::EDI] = false; } }; #endif class InternalCodeAllocator : public CodeAllocator { public: InternalCodeAllocator() : size_(0) { } uint8_t* Allocate(size_t size) override { size_ = size; memory_.reset(new uint8_t[size]); return memory_.get(); } size_t GetSize() const { return size_; } ArrayRef GetMemory() const override { return ArrayRef(memory_.get(), size_); } private: size_t size_; std::unique_ptr memory_; DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator); }; static bool CanExecuteOnHardware(InstructionSet target_isa) { return (target_isa == kRuntimeISA) // Handle the special case of ARM, with two instructions sets (ARM32 and Thumb-2). || (kRuntimeISA == InstructionSet::kArm && target_isa == InstructionSet::kThumb2); } static bool CanExecute(InstructionSet target_isa) { CodeSimulatorContainer simulator(target_isa); return CanExecuteOnHardware(target_isa) || simulator.CanSimulate(); } template inline static Expected SimulatorExecute(CodeSimulator* simulator, Expected (*f)()); template <> inline bool SimulatorExecute(CodeSimulator* simulator, bool (*f)()) { simulator->RunFrom(reinterpret_cast(f)); return simulator->GetCReturnBool(); } template <> inline int32_t SimulatorExecute(CodeSimulator* simulator, int32_t (*f)()) { simulator->RunFrom(reinterpret_cast(f)); return simulator->GetCReturnInt32(); } template <> inline int64_t SimulatorExecute(CodeSimulator* simulator, int64_t (*f)()) { simulator->RunFrom(reinterpret_cast(f)); return simulator->GetCReturnInt64(); } template static void VerifyGeneratedCode(InstructionSet target_isa, Expected (*f)(), bool has_result, Expected expected) { ASSERT_TRUE(CanExecute(target_isa)) << "Target isa is not executable."; // Verify on simulator. CodeSimulatorContainer simulator(target_isa); if (simulator.CanSimulate()) { Expected result = SimulatorExecute(simulator.Get(), f); if (has_result) { ASSERT_EQ(expected, result); } } // Verify on hardware. if (CanExecuteOnHardware(target_isa)) { Expected result = f(); if (has_result) { ASSERT_EQ(expected, result); } } } template static void Run(const InternalCodeAllocator& allocator, const CodeGenerator& codegen, bool has_result, Expected expected) { InstructionSet target_isa = codegen.GetInstructionSet(); typedef Expected (*fptr)(); CommonCompilerTest::MakeExecutable(allocator.GetMemory().data(), allocator.GetMemory().size()); fptr f = reinterpret_cast(reinterpret_cast(allocator.GetMemory().data())); if (target_isa == InstructionSet::kThumb2) { // For thumb we need the bottom bit set. f = reinterpret_cast(reinterpret_cast(f) + 1); } VerifyGeneratedCode(target_isa, f, has_result, expected); } static void ValidateGraph(HGraph* graph) { GraphChecker graph_checker(graph); graph_checker.Run(); if (!graph_checker.IsValid()) { for (const std::string& error : graph_checker.GetErrors()) { std::cout << error << std::endl; } } ASSERT_TRUE(graph_checker.IsValid()); } template static void RunCodeNoCheck(CodeGenerator* codegen, HGraph* graph, const std::function& hook_before_codegen, bool has_result, Expected expected) { { ScopedArenaAllocator local_allocator(graph->GetArenaStack()); SsaLivenessAnalysis liveness(graph, codegen, &local_allocator); PrepareForRegisterAllocation(graph, codegen->GetCompilerOptions()).Run(); liveness.Analyze(); std::unique_ptr register_allocator = RegisterAllocator::Create(&local_allocator, codegen, liveness); register_allocator->AllocateRegisters(); } hook_before_codegen(graph); InternalCodeAllocator allocator; codegen->Compile(&allocator); Run(allocator, *codegen, has_result, expected); } template static void RunCode(CodeGenerator* codegen, HGraph* graph, std::function hook_before_codegen, bool has_result, Expected expected) { ValidateGraph(graph); RunCodeNoCheck(codegen, graph, hook_before_codegen, has_result, expected); } template static void RunCode(CodegenTargetConfig target_config, const CompilerOptions& compiler_options, HGraph* graph, std::function hook_before_codegen, bool has_result, Expected expected) { std::unique_ptr codegen(target_config.CreateCodeGenerator(graph, compiler_options)); RunCode(codegen.get(), graph, hook_before_codegen, has_result, expected); } #ifdef ART_ENABLE_CODEGEN_arm CodeGenerator* create_codegen_arm_vixl32(HGraph* graph, const CompilerOptions& compiler_options) { return new (graph->GetAllocator()) TestCodeGeneratorARMVIXL(graph, compiler_options); } #endif #ifdef ART_ENABLE_CODEGEN_arm64 CodeGenerator* create_codegen_arm64(HGraph* graph, const CompilerOptions& compiler_options) { return new (graph->GetAllocator()) TestCodeGeneratorARM64(graph, compiler_options); } #endif #ifdef ART_ENABLE_CODEGEN_x86 CodeGenerator* create_codegen_x86(HGraph* graph, const CompilerOptions& compiler_options) { return new (graph->GetAllocator()) TestCodeGeneratorX86(graph, compiler_options); } #endif #ifdef ART_ENABLE_CODEGEN_x86_64 CodeGenerator* create_codegen_x86_64(HGraph* graph, const CompilerOptions& compiler_options) { return new (graph->GetAllocator()) x86_64::CodeGeneratorX86_64(graph, compiler_options); } #endif #ifdef ART_ENABLE_CODEGEN_mips CodeGenerator* create_codegen_mips(HGraph* graph, const CompilerOptions& compiler_options) { return new (graph->GetAllocator()) mips::CodeGeneratorMIPS(graph, compiler_options); } #endif #ifdef ART_ENABLE_CODEGEN_mips64 CodeGenerator* create_codegen_mips64(HGraph* graph, const CompilerOptions& compiler_options) { return new (graph->GetAllocator()) mips64::CodeGeneratorMIPS64(graph, compiler_options); } #endif } // namespace art #endif // ART_COMPILER_OPTIMIZING_CODEGEN_TEST_UTILS_H_