#include "ParallelSnippetGenerator.h"
#include "BenchmarkRunner.h"
#include "MCInstrDescView.h"
#include "Target.h"
namespace llvm {
namespace exegesis {
static SmallVector<const Variable *, 8>
getVariablesWithTiedOperands(const Instruction &Instr) {
SmallVector<const Variable *, 8> Result;
for (const auto &Var : Instr.Variables)
if (Var.hasTiedOperands())
Result.push_back(&Var);
return Result;
}
ParallelSnippetGenerator::~ParallelSnippetGenerator() = default;
void ParallelSnippetGenerator::instantiateMemoryOperands(
const unsigned ScratchSpacePointerInReg,
std::vector<InstructionTemplate> &Instructions) const {
if (ScratchSpacePointerInReg == 0)
return; const auto &ET = State.getExegesisTarget();
const unsigned MemStep = ET.getMaxMemoryAccessSize();
const size_t OriginalInstructionsSize = Instructions.size();
size_t I = 0;
for (InstructionTemplate &IT : Instructions) {
ET.fillMemoryOperands(IT, ScratchSpacePointerInReg, I * MemStep);
++I;
}
while (Instructions.size() < kMinNumDifferentAddresses) {
InstructionTemplate IT = Instructions[I % OriginalInstructionsSize];
ET.fillMemoryOperands(IT, ScratchSpacePointerInReg, I * MemStep);
++I;
Instructions.push_back(std::move(IT));
}
assert(I * MemStep < BenchmarkRunner::ScratchSpace::kSize &&
"not enough scratch space");
}
static std::vector<InstructionTemplate> generateSnippetUsingStaticRenaming(
const LLVMState &State, const InstructionTemplate &IT,
const ArrayRef<const Variable *> TiedVariables,
const BitVector &ForbiddenRegisters) {
std::vector<InstructionTemplate> Instructions;
std::vector<BitVector> PossibleRegsForVar;
for (const Variable *Var : TiedVariables) {
assert(Var);
const Operand &Op = IT.getInstr().getPrimaryOperand(*Var);
assert(Op.isReg());
BitVector PossibleRegs = Op.getRegisterAliasing().sourceBits();
remove(PossibleRegs, ForbiddenRegisters);
PossibleRegsForVar.push_back(std::move(PossibleRegs));
}
SmallVector<int, 2> Iterators(TiedVariables.size(), 0);
while (true) {
InstructionTemplate TmpIT = IT;
for (size_t VarId = 0; VarId < TiedVariables.size(); ++VarId) {
const int NextPossibleReg =
PossibleRegsForVar[VarId].find_next(Iterators[VarId]);
if (NextPossibleReg <= 0) {
return Instructions;
}
TmpIT.getValueFor(*TiedVariables[VarId]) =
MCOperand::createReg(NextPossibleReg);
Iterators[VarId] = NextPossibleReg;
for (BitVector &OtherPossibleRegs : PossibleRegsForVar) {
OtherPossibleRegs.reset(NextPossibleReg);
}
}
Instructions.push_back(std::move(TmpIT));
}
}
Expected<std::vector<CodeTemplate>>
ParallelSnippetGenerator::generateCodeTemplates(
InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const {
const Instruction &Instr = Variant.getInstr();
CodeTemplate CT;
CT.ScratchSpacePointerInReg =
Instr.hasMemoryOperands()
? State.getExegesisTarget().getScratchMemoryRegister(
State.getTargetMachine().getTargetTriple())
: 0;
const AliasingConfigurations SelfAliasing(Instr, Instr);
if (SelfAliasing.empty()) {
CT.Info = "instruction is parallel, repeating a random one.";
CT.Instructions.push_back(std::move(Variant));
instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
return getSingleton(std::move(CT));
}
if (SelfAliasing.hasImplicitAliasing()) {
CT.Info = "instruction is serial, repeating a random one.";
CT.Instructions.push_back(std::move(Variant));
instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
return getSingleton(std::move(CT));
}
const auto TiedVariables = getVariablesWithTiedOperands(Instr);
if (!TiedVariables.empty()) {
CT.Info = "instruction has tied variables, using static renaming.";
CT.Instructions = generateSnippetUsingStaticRenaming(
State, Variant, TiedVariables, ForbiddenRegisters);
instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
return getSingleton(std::move(CT));
}
BitVector ImplicitUses(State.getRegInfo().getNumRegs());
BitVector ImplicitDefs(State.getRegInfo().getNumRegs());
for (const auto &Op : Instr.Operands) {
if (Op.isReg() && Op.isImplicit() && !Op.isMemory()) {
assert(Op.isImplicitReg() && "Not an implicit register operand?");
if (Op.isUse())
ImplicitUses.set(Op.getImplicitReg());
else {
assert(Op.isDef() && "Not a use and not a def?");
ImplicitDefs.set(Op.getImplicitReg());
}
}
}
const auto ImplicitUseAliases =
getAliasedBits(State.getRegInfo(), ImplicitUses);
const auto ImplicitDefAliases =
getAliasedBits(State.getRegInfo(), ImplicitDefs);
BitVector Defs(State.getRegInfo().getNumRegs());
for (const auto &Op : Instr.Operands) {
if (Op.isReg() && Op.isExplicit() && Op.isDef() && !Op.isMemory()) {
auto PossibleRegisters = Op.getRegisterAliasing().sourceBits();
remove(PossibleRegisters, ForbiddenRegisters);
remove(PossibleRegisters, ImplicitUseAliases);
if (!PossibleRegisters.any())
return make_error<StringError>(
Twine("no available registers:\ncandidates:\n")
.concat(debugString(State.getRegInfo(),
Op.getRegisterAliasing().sourceBits()))
.concat("\nforbidden:\n")
.concat(debugString(State.getRegInfo(), ForbiddenRegisters))
.concat("\nimplicit use:\n")
.concat(debugString(State.getRegInfo(), ImplicitUseAliases)),
inconvertibleErrorCode());
const auto RandomReg = randomBit(PossibleRegisters);
Defs.set(RandomReg);
Variant.getValueFor(Op) = MCOperand::createReg(RandomReg);
}
}
const auto DefAliases = getAliasedBits(State.getRegInfo(), Defs);
for (const auto &Op : Instr.Operands) {
if (Op.isReg() && Op.isExplicit() && Op.isUse() && !Op.isMemory()) {
auto PossibleRegisters = Op.getRegisterAliasing().sourceBits();
remove(PossibleRegisters, ForbiddenRegisters);
remove(PossibleRegisters, DefAliases);
remove(PossibleRegisters, ImplicitDefAliases);
assert(PossibleRegisters.any() && "No register left to choose from");
const auto RandomReg = randomBit(PossibleRegisters);
Variant.getValueFor(Op) = MCOperand::createReg(RandomReg);
}
}
CT.Info =
"instruction has no tied variables picking Uses different from defs";
CT.Instructions.push_back(std::move(Variant));
instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
return getSingleton(std::move(CT));
}
constexpr const size_t ParallelSnippetGenerator::kMinNumDifferentAddresses;
} }