Improve Register Setup

llvm-svn: 342464
This commit is contained in:
Guillaume Chatelet 2018-09-18 11:26:27 +00:00
parent 4d9d76d800
commit 5ad2909e52
6 changed files with 245 additions and 65 deletions

View File

@ -27,6 +27,30 @@ private:
}; };
class ExegesisAArch64Target : public ExegesisTarget { class ExegesisAArch64Target : public ExegesisTarget {
std::vector<llvm::MCInst> setRegToConstant(const llvm::MCSubtargetInfo &STI,
unsigned Reg) const override {
llvm_unreachable("Not yet implemented");
}
std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI,
const llvm::APInt &Value,
unsigned Reg) const override {
llvm_unreachable("Not yet implemented");
}
unsigned getScratchMemoryRegister(const llvm::Triple &) const override {
llvm_unreachable("Not yet implemented");
}
void fillMemoryOperands(InstructionBuilder &IB, unsigned Reg,
unsigned Offset) const override {
llvm_unreachable("Not yet implemented");
}
unsigned getMaxMemoryAccessSize() const override {
llvm_unreachable("Not yet implemented");
}
bool matchesArch(llvm::Triple::ArchType Arch) const override { bool matchesArch(llvm::Triple::ArchType Arch) const override {
return Arch == llvm::Triple::aarch64 || Arch == llvm::Triple::aarch64_be; return Arch == llvm::Triple::aarch64 || Arch == llvm::Triple::aarch64_be;
} }

View File

@ -89,6 +89,30 @@ namespace {
// Default implementation. // Default implementation.
class ExegesisDefaultTarget : public ExegesisTarget { class ExegesisDefaultTarget : public ExegesisTarget {
private: private:
std::vector<llvm::MCInst> setRegToConstant(const llvm::MCSubtargetInfo &STI,
unsigned Reg) const override {
llvm_unreachable("Not yet implemented");
}
std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI,
const llvm::APInt &Value,
unsigned Reg) const override {
llvm_unreachable("Not yet implemented");
}
unsigned getScratchMemoryRegister(const llvm::Triple &) const override {
llvm_unreachable("Not yet implemented");
}
void fillMemoryOperands(InstructionBuilder &IB, unsigned Reg,
unsigned Offset) const override {
llvm_unreachable("Not yet implemented");
}
unsigned getMaxMemoryAccessSize() const override {
llvm_unreachable("Not yet implemented");
}
bool matchesArch(llvm::Triple::ArchType Arch) const override { bool matchesArch(llvm::Triple::ArchType Arch) const override {
llvm_unreachable("never called"); llvm_unreachable("never called");
return false; return false;

View File

@ -37,30 +37,28 @@ public:
// Generates code to move a constant into a the given register. // Generates code to move a constant into a the given register.
virtual std::vector<llvm::MCInst> virtual std::vector<llvm::MCInst>
setRegToConstant(const llvm::MCSubtargetInfo &STI, unsigned Reg) const { setRegToConstant(const llvm::MCSubtargetInfo &STI, unsigned Reg) const = 0;
return {};
}
// Returns the register pointing to scratch memory, or 0 if this target does // Generates code to move a constant into a the given register.
// not support memory operands. The benchmark function uses the default virtual std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI,
// calling convention. const llvm::APInt &Value,
virtual unsigned getScratchMemoryRegister(const llvm::Triple &) const { unsigned Reg) const = 0;
return 0;
} // Returns the register pointing to scratch memory, or 0 if this target
// does not support memory operands. The benchmark function uses the
// default calling convention.
virtual unsigned getScratchMemoryRegister(const llvm::Triple &) const = 0;
// Fills memory operands with references to the address at [Reg] + Offset. // Fills memory operands with references to the address at [Reg] + Offset.
virtual void fillMemoryOperands(InstructionBuilder &IB, unsigned Reg, virtual void fillMemoryOperands(InstructionBuilder &IB, unsigned Reg,
unsigned Offset) const { unsigned Offset) const = 0;
llvm_unreachable(
"fillMemoryOperands() requires getScratchMemoryRegister() > 0");
}
// Returns the maximum number of bytes a load/store instruction can access at // Returns the maximum number of bytes a load/store instruction can access at
// once. This is typically the size of the largest register available on the // once. This is typically the size of the largest register available on the
// processor. Note that this only used as a hint to generate independant // processor. Note that this only used as a hint to generate independant
// load/stores to/from memory, so the exact returned value does not really // load/stores to/from memory, so the exact returned value does not really
// matter as long as it's large enough. // matter as long as it's large enough.
virtual unsigned getMaxMemoryAccessSize() const { return 0; } virtual unsigned getMaxMemoryAccessSize() const = 0;
// Creates a snippet generator for the given mode. // Creates a snippet generator for the given mode.
std::unique_ptr<SnippetGenerator> std::unique_ptr<SnippetGenerator>

View File

@ -101,6 +101,105 @@ protected:
} }
}; };
static unsigned GetLoadImmediateOpcode(const llvm::APInt &Value) {
switch (Value.getBitWidth()) {
case 8:
return llvm::X86::MOV8ri;
case 16:
return llvm::X86::MOV16ri;
case 32:
return llvm::X86::MOV32ri;
case 64:
return llvm::X86::MOV64ri;
}
llvm_unreachable("Invalid Value Width");
}
static llvm::MCInst loadImmediate(unsigned Reg, const llvm::APInt &Value) {
return llvm::MCInstBuilder(GetLoadImmediateOpcode(Value))
.addReg(Reg)
.addImm(Value.getZExtValue());
}
// Allocates scratch memory on the stack.
static llvm::MCInst allocateStackSpace(unsigned Bytes) {
return llvm::MCInstBuilder(llvm::X86::SUB64ri8)
.addReg(llvm::X86::RSP)
.addReg(llvm::X86::RSP)
.addImm(Bytes);
}
// Fills scratch memory at offset `OffsetBytes` with value `Imm`.
static llvm::MCInst fillStackSpace(unsigned MovOpcode, unsigned OffsetBytes,
uint64_t Imm) {
return llvm::MCInstBuilder(MovOpcode)
// Address = ESP
.addReg(llvm::X86::RSP) // BaseReg
.addImm(1) // ScaleAmt
.addReg(0) // IndexReg
.addImm(OffsetBytes) // Disp
.addReg(0) // Segment
// Immediate.
.addImm(Imm);
}
// Loads scratch memory into register `Reg` using opcode `RMOpcode`.
static llvm::MCInst loadToReg(unsigned Reg, unsigned RMOpcode) {
return llvm::MCInstBuilder(RMOpcode)
.addReg(Reg)
// Address = ESP
.addReg(llvm::X86::RSP) // BaseReg
.addImm(1) // ScaleAmt
.addReg(0) // IndexReg
.addImm(0) // Disp
.addReg(0); // Segment
}
// Releases scratch memory.
static llvm::MCInst releaseStackSpace(unsigned Bytes) {
return llvm::MCInstBuilder(llvm::X86::ADD64ri8)
.addReg(llvm::X86::RSP)
.addReg(llvm::X86::RSP)
.addImm(Bytes);
}
struct ConstantInliner {
explicit ConstantInliner(const llvm::APInt &Constant)
: StackSize(Constant.getBitWidth() / 8) {
assert(Constant.getBitWidth() % 8 == 0 && "Must be a multiple of 8");
Add(allocateStackSpace(StackSize));
size_t ByteOffset = 0;
for (; StackSize - ByteOffset >= 4; ByteOffset += 4)
Add(fillStackSpace(
llvm::X86::MOV32mi, ByteOffset,
Constant.extractBits(32, ByteOffset * 8).getZExtValue()));
if (StackSize - ByteOffset >= 2) {
Add(fillStackSpace(
llvm::X86::MOV16mi, ByteOffset,
Constant.extractBits(16, ByteOffset * 8).getZExtValue()));
ByteOffset += 2;
}
if (StackSize - ByteOffset >= 1)
Add(fillStackSpace(
llvm::X86::MOV8mi, ByteOffset,
Constant.extractBits(8, ByteOffset * 8).getZExtValue()));
}
ConstantInliner &Add(const llvm::MCInst &Inst) {
Instructions.push_back(Inst);
return *this;
}
std::vector<llvm::MCInst> finalize() {
Add(releaseStackSpace(StackSize));
return std::move(Instructions);
}
private:
const size_t StackSize;
std::vector<llvm::MCInst> Instructions;
};
class ExegesisX86Target : public ExegesisTarget { class ExegesisX86Target : public ExegesisTarget {
void addTargetSpecificPasses(llvm::PassManagerBase &PM) const override { void addTargetSpecificPasses(llvm::PassManagerBase &PM) const override {
// Lowers FP pseudo-instructions, e.g. ABS_Fp32 -> ABS_F. // Lowers FP pseudo-instructions, e.g. ABS_Fp32 -> ABS_F.
@ -192,7 +291,21 @@ class ExegesisX86Target : public ExegesisTarget {
Result.push_back(llvm::MCInstBuilder(llvm::X86::POPF64)); // Also pops. Result.push_back(llvm::MCInstBuilder(llvm::X86::POPF64)); // Also pops.
return Result; return Result;
} }
return {}; llvm_unreachable("Not yet implemented");
}
std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI,
const llvm::APInt &Value,
unsigned Reg) const override {
if (llvm::X86::GR8RegClass.contains(Reg) ||
llvm::X86::GR16RegClass.contains(Reg) ||
llvm::X86::GR32RegClass.contains(Reg) ||
llvm::X86::GR64RegClass.contains(Reg))
return {loadImmediate(Reg, Value)};
ConstantInliner CI(Value);
if (llvm::X86::VR64RegClass.contains(Reg))
return CI.Add(loadToReg(Reg, llvm::X86::MMX_MOVQ64rm)).finalize();
llvm_unreachable("Not yet implemented");
} }
std::unique_ptr<SnippetGenerator> std::unique_ptr<SnippetGenerator>
@ -233,48 +346,6 @@ private:
Result.push_back(releaseStackSpace(RegSizeBytes)); Result.push_back(releaseStackSpace(RegSizeBytes));
return Result; return Result;
} }
// Allocates scratch memory on the stack.
static llvm::MCInst allocateStackSpace(unsigned Bytes) {
return llvm::MCInstBuilder(llvm::X86::SUB64ri8)
.addReg(llvm::X86::RSP)
.addReg(llvm::X86::RSP)
.addImm(Bytes);
}
// Fills scratch memory at offset `OffsetBytes` with value `Imm`.
static llvm::MCInst fillStackSpace(unsigned MovOpcode, unsigned OffsetBytes,
uint64_t Imm) {
return llvm::MCInstBuilder(MovOpcode)
// Address = ESP
.addReg(llvm::X86::RSP) // BaseReg
.addImm(1) // ScaleAmt
.addReg(0) // IndexReg
.addImm(OffsetBytes) // Disp
.addReg(0) // Segment
// Immediate.
.addImm(Imm);
}
// Loads scratch memory into register `Reg` using opcode `RMOpcode`.
static llvm::MCInst loadToReg(unsigned Reg, unsigned RMOpcode) {
return llvm::MCInstBuilder(RMOpcode)
.addReg(Reg)
// Address = ESP
.addReg(llvm::X86::RSP) // BaseReg
.addImm(1) // ScaleAmt
.addReg(0) // IndexReg
.addImm(0) // Disp
.addReg(0); // Segment
}
// Releases scratch memory.
static llvm::MCInst releaseStackSpace(unsigned Bytes) {
return llvm::MCInstBuilder(llvm::X86::ADD64ri8)
.addReg(llvm::X86::RSP)
.addReg(llvm::X86::RSP)
.addImm(Bytes);
}
}; };
} // namespace } // namespace

View File

@ -41,13 +41,5 @@ protected:
const ExegesisTarget *const ExegesisTarget_; const ExegesisTarget *const ExegesisTarget_;
}; };
TEST_F(AArch64TargetTest, SetRegToConstant) {
const std::unique_ptr<llvm::MCSubtargetInfo> STI(
Target_->createMCSubtargetInfo(kTriple, "generic", ""));
// The AArch64 target currently doesn't know how to set register values
const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::AArch64::X0);
EXPECT_THAT(Insts, SizeIs(0));
}
} // namespace } // namespace
} // namespace exegesis } // namespace exegesis

View File

@ -9,16 +9,47 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "llvm/MC/MCInstPrinter.h"
namespace llvm {
bool operator==(const llvm::MCOperand &a, const llvm::MCOperand &b) {
if (a.isImm() && b.isImm())
return a.getImm() == b.getImm();
if (a.isReg() && b.isReg())
return a.getReg() == b.getReg();
return false;
}
bool operator==(const llvm::MCInst &a, const llvm::MCInst &b) {
if (a.getOpcode() != b.getOpcode())
return false;
if (a.getNumOperands() != b.getNumOperands())
return false;
for (unsigned I = 0; I < a.getNumOperands(); ++I) {
if (!(a.getOperand(I) == b.getOperand(I)))
return false;
}
return true;
}
} // namespace llvm
namespace exegesis { namespace exegesis {
void InitializeX86ExegesisTarget(); void InitializeX86ExegesisTarget();
namespace { namespace {
using testing::ElementsAre;
using testing::Gt; using testing::Gt;
using testing::NotNull; using testing::NotNull;
using testing::SizeIs; using testing::SizeIs;
using llvm::APInt;
using llvm::MCInst;
using llvm::MCInstBuilder;
constexpr const char kTriple[] = "x86_64-unknown-linux"; constexpr const char kTriple[] = "x86_64-unknown-linux";
class X86TargetTest : public ::testing::Test { class X86TargetTest : public ::testing::Test {
@ -166,5 +197,45 @@ TEST_F(X86TargetTest, SetRegToConstantZMM_AVX512) {
EXPECT_EQ(Insts[18].getOpcode(), llvm::X86::ADD64ri8); EXPECT_EQ(Insts[18].getOpcode(), llvm::X86::ADD64ri8);
} }
TEST_F(X86TargetTest, SetToAPInt) {
const std::unique_ptr<llvm::MCSubtargetInfo> STI(
Target_->createMCSubtargetInfo(kTriple, "core2", ""));
// EXPECT_THAT(ExegesisTarget_->setRegTo(*STI, APInt(8, 0xFFU),
// llvm::X86::AL),
// ElementsAre((MCInst)MCInstBuilder(llvm::X86::MOV8ri)
// .addReg(llvm::X86::AL)
// .addImm(0xFFU)));
// EXPECT_THAT(
// ExegesisTarget_->setRegTo(*STI, APInt(16, 0xFFFFU), llvm::X86::BX),
// ElementsAre((MCInst)MCInstBuilder(llvm::X86::MOV16ri)
// .addReg(llvm::X86::BX)
// .addImm(0xFFFFU)));
// EXPECT_THAT(
// ExegesisTarget_->setRegTo(*STI, APInt(32, 0x7FFFFU), llvm::X86::ECX),
// ElementsAre((MCInst)MCInstBuilder(llvm::X86::MOV32ri)
// .addReg(llvm::X86::ECX)
// .addImm(0x7FFFFU)));
// EXPECT_THAT(ExegesisTarget_->setRegTo(*STI, APInt(64,
// 0x7FFFFFFFFFFFFFFFULL),
// llvm::X86::RDX),
// ElementsAre((MCInst)MCInstBuilder(llvm::X86::MOV64ri)
// .addReg(llvm::X86::RDX)
// .addImm(0x7FFFFFFFFFFFFFFFULL)));
const std::unique_ptr<llvm::MCRegisterInfo> MRI(
Target_->createMCRegInfo(kTriple));
const std::unique_ptr<llvm::MCAsmInfo> MAI(
Target_->createMCAsmInfo(*MRI, kTriple));
const std::unique_ptr<llvm::MCInstrInfo> MII(Target_->createMCInstrInfo());
const std::unique_ptr<llvm::MCInstPrinter> MIP(
Target_->createMCInstPrinter(llvm::Triple(kTriple), 1, *MAI, *MII, *MRI));
for (const auto M : ExegesisTarget_->setRegTo(
*STI, APInt(80, "ABCD1234123456785678", 16), llvm::X86::MM0)) {
MIP->printInst(&M, llvm::errs(), "", *STI);
llvm::errs() << "\n";
}
}
} // namespace } // namespace
} // namespace exegesis } // namespace exegesis