Skip to content

Commit

Permalink
[llvm-exegesis] Improve Register Setup.
Browse files Browse the repository at this point in the history
Summary:
Added function to set a register to a particular value + tests.
Add EFLAGS test, use new setRegTo instead of setRegToConstant.

Reviewers: courbet, javed.absar

Subscribers: mgorny, tschuett, llvm-commits

Differential Revision: https://reviews.llvm.org/D51856

llvm-svn: 342466
  • Loading branch information
gchatelet committed Sep 18, 2018
1 parent 8721ad9 commit 937f3fe
Show file tree
Hide file tree
Showing 16 changed files with 187 additions and 137 deletions.
47 changes: 33 additions & 14 deletions llvm/tools/llvm-exegesis/lib/AArch64/Target.cpp
Expand Up @@ -9,6 +9,7 @@
#include "../Target.h"
#include "../Latency.h"
#include "AArch64.h"
#include "AArch64RegisterInfo.h"

namespace exegesis {

Expand All @@ -26,33 +27,51 @@ class AArch64LatencyBenchmarkRunner : public LatencyBenchmarkRunner {
}
};

class ExegesisAArch64Target : public ExegesisTarget {
std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI,
const llvm::APInt &Value,
unsigned Reg) const override {
llvm_unreachable("Not yet implemented");
}
namespace {

unsigned getScratchMemoryRegister(const llvm::Triple &) const override {
llvm_unreachable("Not yet implemented");
static unsigned getLoadImmediateOpcode(unsigned RegBitWidth) {
switch (RegBitWidth) {
case 32:
return llvm::AArch64::MOVi32imm;
case 64:
return llvm::AArch64::MOVi64imm;
}
llvm_unreachable("Invalid Value Width");
}

void fillMemoryOperands(InstructionBuilder &IB, unsigned Reg,
unsigned Offset) const override {
llvm_unreachable("Not yet implemented");
}
// Generates instruction to load an immediate value into a register.
static llvm::MCInst loadImmediate(unsigned Reg, unsigned RegBitWidth,
const llvm::APInt &Value) {
if (Value.getBitWidth() > RegBitWidth)
llvm_unreachable("Value must fit in the Register");
return llvm::MCInstBuilder(getLoadImmediateOpcode(RegBitWidth))
.addReg(Reg)
.addImm(Value.getZExtValue());
}

unsigned getMaxMemoryAccessSize() const override {
llvm_unreachable("Not yet implemented");
} // namespace

class ExegesisAArch64Target : public ExegesisTarget {
std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI,
unsigned Reg,
const llvm::APInt &Value) const override {
if (llvm::AArch64::GPR32RegClass.contains(Reg))
return {loadImmediate(Reg, 32, Value)};
if (llvm::AArch64::GPR64RegClass.contains(Reg))
return {loadImmediate(Reg, 64, Value)};
llvm::errs() << "setRegTo is not implemented, results will be unreliable\n";
return {};
}

bool matchesArch(llvm::Triple::ArchType Arch) const override {
return Arch == llvm::Triple::aarch64 || Arch == llvm::Triple::aarch64_be;
}

void addTargetSpecificPasses(llvm::PassManagerBase &PM) const override {
// Function return is a pseudo-instruction that needs to be expanded
PM.add(llvm::createAArch64ExpandPseudoPass());
}

std::unique_ptr<BenchmarkRunner>
createLatencyBenchmarkRunner(const LLVMState &State) const override {
return llvm::make_unique<AArch64LatencyBenchmarkRunner>(State);
Expand Down
39 changes: 19 additions & 20 deletions llvm/tools/llvm-exegesis/lib/Assembler.cpp
Expand Up @@ -29,18 +29,18 @@ static constexpr const char ModuleID[] = "ExegesisInfoTest";
static constexpr const char FunctionID[] = "foo";

static std::vector<llvm::MCInst>
generateSnippetSetupCode(const llvm::ArrayRef<unsigned> RegsToDef,
const ExegesisTarget &ET,
const llvm::LLVMTargetMachine &TM, bool &IsComplete) {
IsComplete = true;
generateSnippetSetupCode(const ExegesisTarget &ET,
const llvm::MCSubtargetInfo *const MSI,
llvm::ArrayRef<RegisterValue> RegisterInitialValues,
bool &IsSnippetSetupComplete) {
std::vector<llvm::MCInst> Result;
// for (const unsigned Reg : RegsToDef) {
// // Load a constant in the register.
// const auto Code = ET.setRegToConstant(*TM.getMCSubtargetInfo(), Reg);
// if (Code.empty())
// IsComplete = false;
// Result.insert(Result.end(), Code.begin(), Code.end());
// }
for (const RegisterValue &RV : RegisterInitialValues) {
// Load a constant in the register.
const auto SetRegisterCode = ET.setRegTo(*MSI, RV.Register, RV.Value);
if (SetRegisterCode.empty())
IsSnippetSetupComplete = false;
Result.insert(Result.end(), SetRegisterCode.begin(), SetRegisterCode.end());
}
return Result;
}

Expand Down Expand Up @@ -149,7 +149,7 @@ llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM) {
void assembleToStream(const ExegesisTarget &ET,
std::unique_ptr<llvm::LLVMTargetMachine> TM,
llvm::ArrayRef<unsigned> LiveIns,
llvm::ArrayRef<unsigned> RegsToDef,
llvm::ArrayRef<RegisterValue> RegisterInitialValues,
llvm::ArrayRef<llvm::MCInst> Instructions,
llvm::raw_pwrite_stream &AsmStream) {
std::unique_ptr<llvm::LLVMContext> Context =
Expand All @@ -171,13 +171,12 @@ void assembleToStream(const ExegesisTarget &ET,
MF.getRegInfo().addLiveIn(Reg);

bool IsSnippetSetupComplete = false;
std::vector<llvm::MCInst> SnippetWithSetup =
generateSnippetSetupCode(RegsToDef, ET, *TM, IsSnippetSetupComplete);
if (!SnippetWithSetup.empty()) {
SnippetWithSetup.insert(SnippetWithSetup.end(), Instructions.begin(),
Instructions.end());
Instructions = SnippetWithSetup;
}
std::vector<llvm::MCInst> Code =
generateSnippetSetupCode(ET, TM->getMCSubtargetInfo(),
RegisterInitialValues, IsSnippetSetupComplete);

Code.insert(Code.end(), Instructions.begin(), Instructions.end());

// If the snippet setup is not complete, we disable liveliness tracking. This
// means that we won't know what values are in the registers.
if (!IsSnippetSetupComplete)
Expand All @@ -188,7 +187,7 @@ void assembleToStream(const ExegesisTarget &ET,
MF.getRegInfo().freezeReservedRegs(MF);

// Fill the MachineFunction from the instructions.
fillMachineFunction(MF, LiveIns, Instructions);
fillMachineFunction(MF, LiveIns, Code);

// We create the pass manager, run the passes to populate AsmBuffer.
llvm::MCContext &MCContext = MMI->getContext();
Expand Down
8 changes: 7 additions & 1 deletion llvm/tools/llvm-exegesis/lib/Assembler.h
Expand Up @@ -39,14 +39,20 @@ class ExegesisTarget;
// convention and target machine).
llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM);

// A simple object storing the value for a particular register.
struct RegisterValue {
unsigned Register;
llvm::APInt Value;
};

// Creates a temporary `void foo(char*)` function containing the provided
// Instructions. Runs a set of llvm Passes to provide correct prologue and
// epilogue. Once the MachineFunction is ready, it is assembled for TM to
// AsmStream, the temporary function is eventually discarded.
void assembleToStream(const ExegesisTarget &ET,
std::unique_ptr<llvm::LLVMTargetMachine> TM,
llvm::ArrayRef<unsigned> LiveIns,
llvm::ArrayRef<unsigned> RegsToDef,
llvm::ArrayRef<RegisterValue> RegisterInitialValues,
llvm::ArrayRef<llvm::MCInst> Instructions,
llvm::raw_pwrite_stream &AsmStream);

Expand Down
2 changes: 1 addition & 1 deletion llvm/tools/llvm-exegesis/lib/BenchmarkCode.h
Expand Up @@ -23,7 +23,7 @@ struct BenchmarkCode {

// Before the code is executed some instructions are added to setup the
// registers initial values.
std::vector<unsigned> RegsToDef;
std::vector<RegisterValue> RegisterInitialValues;

// We also need to provide the registers that are live on entry for the
// assembler to generate proper prologue/epilogue.
Expand Down
2 changes: 1 addition & 1 deletion llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
Expand Up @@ -104,7 +104,7 @@ BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC,
return std::move(E);
llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/);
assembleToStream(State.getExegesisTarget(), State.createTargetMachine(),
BC.LiveIns, BC.RegsToDef, Code, OFS);
BC.LiveIns, BC.RegisterInitialValues, Code, OFS);
return ResultPath.str();
}

Expand Down
10 changes: 5 additions & 5 deletions llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
Expand Up @@ -49,22 +49,22 @@ SnippetGenerator::generateConfigurations(unsigned Opcode) const {
}
if (CT.ScratchSpacePointerInReg)
BC.LiveIns.push_back(CT.ScratchSpacePointerInReg);
BC.RegsToDef = computeRegsToDef(CT.Instructions);
BC.RegisterInitialValues = computeRegisterInitialValues(CT.Instructions);
Output.push_back(std::move(BC));
}
return Output;
} else
return E.takeError();
}

std::vector<unsigned> SnippetGenerator::computeRegsToDef(
std::vector<RegisterValue> SnippetGenerator::computeRegisterInitialValues(
const std::vector<InstructionBuilder> &Instructions) const {
// Collect all register uses and create an assignment for each of them.
// Ignore memory operands which are handled separately.
// Loop invariant: DefinedRegs[i] is true iif it has been set at least once
// before the current instruction.
llvm::BitVector DefinedRegs = RATC.emptyRegisters();
std::vector<unsigned> RegsToDef;
std::vector<RegisterValue> RIV;
for (const InstructionBuilder &IB : Instructions) {
// Returns the register that this Operand sets or uses, or 0 if this is not
// a register.
Expand All @@ -82,7 +82,7 @@ std::vector<unsigned> SnippetGenerator::computeRegsToDef(
if (!Op.IsDef) {
const unsigned Reg = GetOpReg(Op);
if (Reg > 0 && !DefinedRegs.test(Reg)) {
RegsToDef.push_back(Reg);
RIV.push_back(RegisterValue{Reg, llvm::APInt()});
DefinedRegs.set(Reg);
}
}
Expand All @@ -96,7 +96,7 @@ std::vector<unsigned> SnippetGenerator::computeRegsToDef(
}
}
}
return RegsToDef;
return RIV;
}

llvm::Expected<CodeTemplate> SnippetGenerator::generateSelfAliasingCodeTemplate(
Expand Down
4 changes: 2 additions & 2 deletions llvm/tools/llvm-exegesis/lib/SnippetGenerator.h
Expand Up @@ -48,8 +48,8 @@ class SnippetGenerator {
generateConfigurations(unsigned Opcode) const;

// Given a snippet, computes which registers the setup code needs to define.
std::vector<unsigned>
computeRegsToDef(const std::vector<InstructionBuilder> &Snippet) const;
std::vector<RegisterValue> computeRegisterInitialValues(
const std::vector<InstructionBuilder> &Snippet) const;

protected:
const LLVMState &State;
Expand Down
17 changes: 2 additions & 15 deletions llvm/tools/llvm-exegesis/lib/Target.cpp
Expand Up @@ -90,21 +90,8 @@ namespace {
class ExegesisDefaultTarget : public ExegesisTarget {
private:
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 {
unsigned Reg,
const llvm::APInt &Value) const override {
llvm_unreachable("Not yet implemented");
}

Expand Down
18 changes: 12 additions & 6 deletions llvm/tools/llvm-exegesis/lib/Target.h
Expand Up @@ -36,25 +36,31 @@ class ExegesisTarget {
virtual void addTargetSpecificPasses(llvm::PassManagerBase &PM) const {}

// Generates code to move a constant into a the given register.
virtual std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI,
const llvm::APInt &Value,
unsigned Reg) const = 0;
// Precondition: Value must fit into Reg.
virtual std::vector<llvm::MCInst>
setRegTo(const llvm::MCSubtargetInfo &STI, unsigned Reg,
const llvm::APInt &Value) const = 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;
virtual unsigned getScratchMemoryRegister(const llvm::Triple &) const {
return 0;
}

// Fills memory operands with references to the address at [Reg] + Offset.
virtual void fillMemoryOperands(InstructionBuilder &IB, unsigned Reg,
unsigned Offset) const = 0;
unsigned Offset) const {
llvm_unreachable(
"fillMemoryOperands() requires getScratchMemoryRegister() > 0");
}

// 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
// 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
// matter as long as it's large enough.
virtual unsigned getMaxMemoryAccessSize() const = 0;
virtual unsigned getMaxMemoryAccessSize() const { return 0; }

// Creates a snippet generator for the given mode.
std::unique_ptr<SnippetGenerator>
Expand Down

0 comments on commit 937f3fe

Please sign in to comment.