forked from OSchip/llvm-project
parent
4d9d76d800
commit
5ad2909e52
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue