182 lines
7.0 KiB
C++
182 lines
7.0 KiB
C++
|
//===-- SerialSnippetGenerator.cpp ------------------------------*- C++ -*-===//
|
||
|
//
|
||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "SerialSnippetGenerator.h"
|
||
|
|
||
|
#include "CodeTemplate.h"
|
||
|
#include "MCInstrDescView.h"
|
||
|
#include "Target.h"
|
||
|
#include <algorithm>
|
||
|
#include <numeric>
|
||
|
#include <vector>
|
||
|
|
||
|
namespace llvm {
|
||
|
namespace exegesis {
|
||
|
|
||
|
struct ExecutionClass {
|
||
|
ExecutionMode Mask;
|
||
|
const char *Description;
|
||
|
} static const kExecutionClasses[] = {
|
||
|
{ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS |
|
||
|
ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS,
|
||
|
"Repeating a single implicitly serial instruction"},
|
||
|
{ExecutionMode::SERIAL_VIA_EXPLICIT_REGS,
|
||
|
"Repeating a single explicitly serial instruction"},
|
||
|
{ExecutionMode::SERIAL_VIA_MEMORY_INSTR |
|
||
|
ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR,
|
||
|
"Repeating two instructions"},
|
||
|
};
|
||
|
|
||
|
static constexpr size_t kMaxAliasingInstructions = 10;
|
||
|
|
||
|
static std::vector<const Instruction *>
|
||
|
computeAliasingInstructions(const LLVMState &State, const Instruction *Instr,
|
||
|
size_t MaxAliasingInstructions,
|
||
|
const BitVector &ForbiddenRegisters) {
|
||
|
// Randomly iterate the set of instructions.
|
||
|
std::vector<unsigned> Opcodes;
|
||
|
Opcodes.resize(State.getInstrInfo().getNumOpcodes());
|
||
|
std::iota(Opcodes.begin(), Opcodes.end(), 0U);
|
||
|
std::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator());
|
||
|
|
||
|
std::vector<const Instruction *> AliasingInstructions;
|
||
|
for (const unsigned OtherOpcode : Opcodes) {
|
||
|
if (OtherOpcode == Instr->Description.getOpcode())
|
||
|
continue;
|
||
|
const Instruction &OtherInstr = State.getIC().getInstr(OtherOpcode);
|
||
|
const MCInstrDesc &OtherInstrDesc = OtherInstr.Description;
|
||
|
// Ignore instructions that we cannot run.
|
||
|
if (OtherInstrDesc.isPseudo() ||
|
||
|
OtherInstrDesc.isBranch() || OtherInstrDesc.isIndirectBranch() ||
|
||
|
OtherInstrDesc.isCall() || OtherInstrDesc.isReturn()) {
|
||
|
continue;
|
||
|
}
|
||
|
if (OtherInstr.hasMemoryOperands())
|
||
|
continue;
|
||
|
if (!State.getExegesisTarget().allowAsBackToBack(OtherInstr))
|
||
|
continue;
|
||
|
if (Instr->hasAliasingRegistersThrough(OtherInstr, ForbiddenRegisters))
|
||
|
AliasingInstructions.push_back(&OtherInstr);
|
||
|
if (AliasingInstructions.size() >= MaxAliasingInstructions)
|
||
|
break;
|
||
|
}
|
||
|
return AliasingInstructions;
|
||
|
}
|
||
|
|
||
|
static ExecutionMode getExecutionModes(const Instruction &Instr,
|
||
|
const BitVector &ForbiddenRegisters) {
|
||
|
ExecutionMode EM = ExecutionMode::UNKNOWN;
|
||
|
if (Instr.hasAliasingImplicitRegisters())
|
||
|
EM |= ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS;
|
||
|
if (Instr.hasTiedRegisters())
|
||
|
EM |= ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS;
|
||
|
if (Instr.hasMemoryOperands())
|
||
|
EM |= ExecutionMode::SERIAL_VIA_MEMORY_INSTR;
|
||
|
else {
|
||
|
if (Instr.hasAliasingRegisters(ForbiddenRegisters))
|
||
|
EM |= ExecutionMode::SERIAL_VIA_EXPLICIT_REGS;
|
||
|
if (Instr.hasOneUseOrOneDef())
|
||
|
EM |= ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR;
|
||
|
}
|
||
|
return EM;
|
||
|
}
|
||
|
|
||
|
static void appendCodeTemplates(const LLVMState &State,
|
||
|
InstructionTemplate Variant,
|
||
|
const BitVector &ForbiddenRegisters,
|
||
|
ExecutionMode ExecutionModeBit,
|
||
|
StringRef ExecutionClassDescription,
|
||
|
std::vector<CodeTemplate> &CodeTemplates) {
|
||
|
assert(isEnumValue(ExecutionModeBit) && "Bit must be a power of two");
|
||
|
switch (ExecutionModeBit) {
|
||
|
case ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS:
|
||
|
// Nothing to do, the instruction is always serial.
|
||
|
LLVM_FALLTHROUGH;
|
||
|
case ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS: {
|
||
|
// Picking whatever value for the tied variable will make the instruction
|
||
|
// serial.
|
||
|
CodeTemplate CT;
|
||
|
CT.Execution = ExecutionModeBit;
|
||
|
CT.Info = std::string(ExecutionClassDescription);
|
||
|
CT.Instructions.push_back(std::move(Variant));
|
||
|
CodeTemplates.push_back(std::move(CT));
|
||
|
return;
|
||
|
}
|
||
|
case ExecutionMode::SERIAL_VIA_MEMORY_INSTR: {
|
||
|
// Select back-to-back memory instruction.
|
||
|
// TODO: Implement me.
|
||
|
return;
|
||
|
}
|
||
|
case ExecutionMode::SERIAL_VIA_EXPLICIT_REGS: {
|
||
|
// Making the execution of this instruction serial by selecting one def
|
||
|
// register to alias with one use register.
|
||
|
const AliasingConfigurations SelfAliasing(Variant.getInstr(),
|
||
|
Variant.getInstr());
|
||
|
assert(!SelfAliasing.empty() && !SelfAliasing.hasImplicitAliasing() &&
|
||
|
"Instr must alias itself explicitly");
|
||
|
// This is a self aliasing instruction so defs and uses are from the same
|
||
|
// instance, hence twice Variant in the following call.
|
||
|
setRandomAliasing(SelfAliasing, Variant, Variant);
|
||
|
CodeTemplate CT;
|
||
|
CT.Execution = ExecutionModeBit;
|
||
|
CT.Info = std::string(ExecutionClassDescription);
|
||
|
CT.Instructions.push_back(std::move(Variant));
|
||
|
CodeTemplates.push_back(std::move(CT));
|
||
|
return;
|
||
|
}
|
||
|
case ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR: {
|
||
|
const Instruction &Instr = Variant.getInstr();
|
||
|
// Select back-to-back non-memory instruction.
|
||
|
for (const auto *OtherInstr : computeAliasingInstructions(
|
||
|
State, &Instr, kMaxAliasingInstructions, ForbiddenRegisters)) {
|
||
|
const AliasingConfigurations Forward(Instr, *OtherInstr);
|
||
|
const AliasingConfigurations Back(*OtherInstr, Instr);
|
||
|
InstructionTemplate ThisIT(Variant);
|
||
|
InstructionTemplate OtherIT(OtherInstr);
|
||
|
if (!Forward.hasImplicitAliasing())
|
||
|
setRandomAliasing(Forward, ThisIT, OtherIT);
|
||
|
else if (!Back.hasImplicitAliasing())
|
||
|
setRandomAliasing(Back, OtherIT, ThisIT);
|
||
|
CodeTemplate CT;
|
||
|
CT.Execution = ExecutionModeBit;
|
||
|
CT.Info = std::string(ExecutionClassDescription);
|
||
|
CT.Instructions.push_back(std::move(ThisIT));
|
||
|
CT.Instructions.push_back(std::move(OtherIT));
|
||
|
CodeTemplates.push_back(std::move(CT));
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
default:
|
||
|
llvm_unreachable("Unhandled enum value");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SerialSnippetGenerator::~SerialSnippetGenerator() = default;
|
||
|
|
||
|
Expected<std::vector<CodeTemplate>>
|
||
|
SerialSnippetGenerator::generateCodeTemplates(
|
||
|
InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const {
|
||
|
std::vector<CodeTemplate> Results;
|
||
|
const ExecutionMode EM =
|
||
|
getExecutionModes(Variant.getInstr(), ForbiddenRegisters);
|
||
|
for (const auto EC : kExecutionClasses) {
|
||
|
for (const auto ExecutionModeBit : getExecutionModeBits(EM & EC.Mask))
|
||
|
appendCodeTemplates(State, Variant, ForbiddenRegisters, ExecutionModeBit,
|
||
|
EC.Description, Results);
|
||
|
if (!Results.empty())
|
||
|
break;
|
||
|
}
|
||
|
if (Results.empty())
|
||
|
return make_error<Failure>(
|
||
|
"No strategy found to make the execution serial");
|
||
|
return std::move(Results);
|
||
|
}
|
||
|
|
||
|
} // namespace exegesis
|
||
|
} // namespace llvm
|