You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
12 KiB
350 lines
12 KiB
/*
|
|
* 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/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
|
|
|
|
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<const uint8_t> GetMemory() const override {
|
|
return ArrayRef<const uint8_t>(memory_.get(), size_);
|
|
}
|
|
|
|
private:
|
|
size_t size_;
|
|
std::unique_ptr<uint8_t[]> 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 <typename Expected>
|
|
inline static Expected SimulatorExecute(CodeSimulator* simulator, Expected (*f)());
|
|
|
|
template <>
|
|
inline bool SimulatorExecute<bool>(CodeSimulator* simulator, bool (*f)()) {
|
|
simulator->RunFrom(reinterpret_cast<intptr_t>(f));
|
|
return simulator->GetCReturnBool();
|
|
}
|
|
|
|
template <>
|
|
inline int32_t SimulatorExecute<int32_t>(CodeSimulator* simulator, int32_t (*f)()) {
|
|
simulator->RunFrom(reinterpret_cast<intptr_t>(f));
|
|
return simulator->GetCReturnInt32();
|
|
}
|
|
|
|
template <>
|
|
inline int64_t SimulatorExecute<int64_t>(CodeSimulator* simulator, int64_t (*f)()) {
|
|
simulator->RunFrom(reinterpret_cast<intptr_t>(f));
|
|
return simulator->GetCReturnInt64();
|
|
}
|
|
|
|
template <typename Expected>
|
|
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<Expected>(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 <typename Expected>
|
|
static void Run(const InternalCodeAllocator& allocator,
|
|
const CodeGenerator& codegen,
|
|
bool has_result,
|
|
Expected expected) {
|
|
InstructionSet target_isa = codegen.GetInstructionSet();
|
|
|
|
struct CodeHolder : CommonCompilerTestImpl {
|
|
protected:
|
|
ClassLinker* GetClassLinker() override { return nullptr; }
|
|
Runtime* GetRuntime() override { return nullptr; }
|
|
};
|
|
CodeHolder code_holder;
|
|
const void* code_ptr =
|
|
code_holder.MakeExecutable(allocator.GetMemory(), ArrayRef<const uint8_t>(), target_isa);
|
|
|
|
typedef Expected (*fptr)();
|
|
fptr f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(code_ptr));
|
|
if (target_isa == InstructionSet::kThumb2) {
|
|
// For thumb we need the bottom bit set.
|
|
f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(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 <typename Expected>
|
|
static void RunCodeNoCheck(CodeGenerator* codegen,
|
|
HGraph* graph,
|
|
const std::function<void(HGraph*)>& 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<RegisterAllocator> 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 <typename Expected>
|
|
static void RunCode(CodeGenerator* codegen,
|
|
HGraph* graph,
|
|
std::function<void(HGraph*)> hook_before_codegen,
|
|
bool has_result,
|
|
Expected expected) {
|
|
ValidateGraph(graph);
|
|
RunCodeNoCheck(codegen, graph, hook_before_codegen, has_result, expected);
|
|
}
|
|
|
|
template <typename Expected>
|
|
static void RunCode(CodegenTargetConfig target_config,
|
|
const CompilerOptions& compiler_options,
|
|
HGraph* graph,
|
|
std::function<void(HGraph*)> hook_before_codegen,
|
|
bool has_result,
|
|
Expected expected) {
|
|
std::unique_ptr<CodeGenerator> codegen(target_config.CreateCodeGenerator(graph,
|
|
compiler_options));
|
|
RunCode(codegen.get(), graph, hook_before_codegen, has_result, expected);
|
|
}
|
|
|
|
#ifdef ART_ENABLE_CODEGEN_arm
|
|
inline 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
|
|
inline CodeGenerator* create_codegen_arm64(HGraph* graph, const CompilerOptions& compiler_options) {
|
|
return new (graph->GetAllocator()) TestCodeGeneratorARM64(graph, compiler_options);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ART_ENABLE_CODEGEN_x86
|
|
inline 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
|
|
inline CodeGenerator* create_codegen_x86_64(HGraph* graph, const CompilerOptions& compiler_options) {
|
|
return new (graph->GetAllocator()) x86_64::CodeGeneratorX86_64(graph, compiler_options);
|
|
}
|
|
#endif
|
|
|
|
} // namespace art
|
|
|
|
#endif // ART_COMPILER_OPTIMIZING_CODEGEN_TEST_UTILS_H_
|