| //===-- SnippetGenerator.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 <array> |
| #include <string> |
| |
| #include "Assembler.h" |
| #include "Error.h" |
| #include "MCInstrDescView.h" |
| #include "SnippetGenerator.h" |
| #include "Target.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/FormatVariadic.h" |
| #include "llvm/Support/Program.h" |
| |
| namespace llvm { |
| namespace exegesis { |
| |
| std::vector<CodeTemplate> getSingleton(CodeTemplate &&CT) { |
| std::vector<CodeTemplate> Result; |
| Result.push_back(std::move(CT)); |
| return Result; |
| } |
| |
| SnippetGeneratorFailure::SnippetGeneratorFailure(const Twine &S) |
| : StringError(S, inconvertibleErrorCode()) {} |
| |
| SnippetGenerator::SnippetGenerator(const LLVMState &State, const Options &Opts) |
| : State(State), Opts(Opts) {} |
| |
| SnippetGenerator::~SnippetGenerator() = default; |
| |
| Error SnippetGenerator::generateConfigurations( |
| const InstructionTemplate &Variant, std::vector<BenchmarkCode> &Benchmarks, |
| const BitVector &ExtraForbiddenRegs) const { |
| BitVector ForbiddenRegs = State.getRATC().reservedRegisters(); |
| ForbiddenRegs |= ExtraForbiddenRegs; |
| // If the instruction has memory registers, prevent the generator from |
| // using the scratch register and its aliasing registers. |
| if (Variant.getInstr().hasMemoryOperands()) { |
| const auto &ET = State.getExegesisTarget(); |
| unsigned ScratchSpacePointerInReg = |
| ET.getScratchMemoryRegister(State.getTargetMachine().getTargetTriple()); |
| if (ScratchSpacePointerInReg == 0) |
| return make_error<Failure>( |
| "Infeasible : target does not support memory instructions"); |
| const auto &ScratchRegAliases = |
| State.getRATC().getRegister(ScratchSpacePointerInReg).aliasedBits(); |
| // If the instruction implicitly writes to ScratchSpacePointerInReg , abort. |
| // FIXME: We could make a copy of the scratch register. |
| for (const auto &Op : Variant.getInstr().Operands) { |
| if (Op.isDef() && Op.isImplicitReg() && |
| ScratchRegAliases.test(Op.getImplicitReg())) |
| return make_error<Failure>( |
| "Infeasible : memory instruction uses scratch memory register"); |
| } |
| ForbiddenRegs |= ScratchRegAliases; |
| } |
| |
| if (auto E = generateCodeTemplates(Variant, ForbiddenRegs)) { |
| MutableArrayRef<CodeTemplate> Templates = E.get(); |
| |
| // Avoid reallocations in the loop. |
| Benchmarks.reserve(Benchmarks.size() + Templates.size()); |
| for (CodeTemplate &CT : Templates) { |
| // TODO: Generate as many BenchmarkCode as needed. |
| { |
| BenchmarkCode BC; |
| BC.Info = CT.Info; |
| for (InstructionTemplate &IT : CT.Instructions) { |
| if (auto error = randomizeUnsetVariables(State, ForbiddenRegs, IT)) |
| return error; |
| BC.Key.Instructions.push_back(IT.build()); |
| } |
| if (CT.ScratchSpacePointerInReg) |
| BC.LiveIns.push_back(CT.ScratchSpacePointerInReg); |
| BC.Key.RegisterInitialValues = |
| computeRegisterInitialValues(CT.Instructions); |
| BC.Key.Config = CT.Config; |
| Benchmarks.emplace_back(std::move(BC)); |
| if (Benchmarks.size() >= Opts.MaxConfigsPerOpcode) { |
| // We reached the number of allowed configs and return early. |
| return Error::success(); |
| } |
| } |
| } |
| return Error::success(); |
| } else |
| return E.takeError(); |
| } |
| |
| std::vector<RegisterValue> SnippetGenerator::computeRegisterInitialValues( |
| const std::vector<InstructionTemplate> &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. |
| BitVector DefinedRegs = State.getRATC().emptyRegisters(); |
| std::vector<RegisterValue> RIV; |
| for (const InstructionTemplate &IT : Instructions) { |
| // Returns the register that this Operand sets or uses, or 0 if this is not |
| // a register. |
| const auto GetOpReg = [&IT](const Operand &Op) -> unsigned { |
| if (Op.isMemory()) |
| return 0; |
| if (Op.isImplicitReg()) |
| return Op.getImplicitReg(); |
| if (Op.isExplicit() && IT.getValueFor(Op).isReg()) |
| return IT.getValueFor(Op).getReg(); |
| return 0; |
| }; |
| // Collect used registers that have never been def'ed. |
| for (const Operand &Op : IT.getInstr().Operands) { |
| if (Op.isUse()) { |
| const unsigned Reg = GetOpReg(Op); |
| if (Reg > 0 && !DefinedRegs.test(Reg)) { |
| RIV.push_back(RegisterValue::zero(Reg)); |
| DefinedRegs.set(Reg); |
| } |
| } |
| } |
| // Mark defs as having been def'ed. |
| for (const Operand &Op : IT.getInstr().Operands) { |
| if (Op.isDef()) { |
| const unsigned Reg = GetOpReg(Op); |
| if (Reg > 0) |
| DefinedRegs.set(Reg); |
| } |
| } |
| } |
| return RIV; |
| } |
| |
| Expected<std::vector<CodeTemplate>> |
| generateSelfAliasingCodeTemplates(InstructionTemplate Variant) { |
| const AliasingConfigurations SelfAliasing(Variant.getInstr(), |
| Variant.getInstr()); |
| if (SelfAliasing.empty()) |
| return make_error<SnippetGeneratorFailure>("empty self aliasing"); |
| std::vector<CodeTemplate> Result; |
| Result.emplace_back(); |
| CodeTemplate &CT = Result.back(); |
| if (SelfAliasing.hasImplicitAliasing()) { |
| CT.Info = "implicit Self cycles, picking random values."; |
| } else { |
| CT.Info = "explicit self cycles, selecting one aliasing Conf."; |
| // 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); |
| } |
| CT.Instructions.push_back(std::move(Variant)); |
| return std::move(Result); |
| } |
| |
| Expected<std::vector<CodeTemplate>> |
| generateUnconstrainedCodeTemplates(const InstructionTemplate &Variant, |
| StringRef Msg) { |
| std::vector<CodeTemplate> Result; |
| Result.emplace_back(); |
| CodeTemplate &CT = Result.back(); |
| CT.Info = |
| std::string(formatv("{0}, repeating an unconstrained assignment", Msg)); |
| CT.Instructions.push_back(std::move(Variant)); |
| return std::move(Result); |
| } |
| |
| std::mt19937 &randomGenerator() { |
| static std::random_device RandomDevice; |
| static std::mt19937 RandomGenerator(RandomDevice()); |
| return RandomGenerator; |
| } |
| |
| size_t randomIndex(size_t Max) { |
| std::uniform_int_distribution<> Distribution(0, Max); |
| return Distribution(randomGenerator()); |
| } |
| |
| template <typename C> static decltype(auto) randomElement(const C &Container) { |
| assert(!Container.empty() && |
| "Can't pick a random element from an empty container)"); |
| return Container[randomIndex(Container.size() - 1)]; |
| } |
| |
| static void setRegisterOperandValue(const RegisterOperandAssignment &ROV, |
| InstructionTemplate &IB) { |
| assert(ROV.Op); |
| if (ROV.Op->isExplicit()) { |
| auto &AssignedValue = IB.getValueFor(*ROV.Op); |
| if (AssignedValue.isValid()) { |
| assert(AssignedValue.isReg() && AssignedValue.getReg() == ROV.Reg); |
| return; |
| } |
| AssignedValue = MCOperand::createReg(ROV.Reg); |
| } else { |
| assert(ROV.Op->isImplicitReg()); |
| assert(ROV.Reg == ROV.Op->getImplicitReg()); |
| } |
| } |
| |
| size_t randomBit(const BitVector &Vector) { |
| assert(Vector.any()); |
| auto Itr = Vector.set_bits_begin(); |
| for (size_t I = randomIndex(Vector.count() - 1); I != 0; --I) |
| ++Itr; |
| return *Itr; |
| } |
| |
| void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations, |
| InstructionTemplate &DefIB, InstructionTemplate &UseIB) { |
| assert(!AliasingConfigurations.empty()); |
| assert(!AliasingConfigurations.hasImplicitAliasing()); |
| const auto &RandomConf = randomElement(AliasingConfigurations.Configurations); |
| setRegisterOperandValue(randomElement(RandomConf.Defs), DefIB); |
| setRegisterOperandValue(randomElement(RandomConf.Uses), UseIB); |
| } |
| |
| static Error randomizeMCOperand(const LLVMState &State, |
| const Instruction &Instr, const Variable &Var, |
| MCOperand &AssignedValue, |
| const BitVector &ForbiddenRegs) { |
| const Operand &Op = Instr.getPrimaryOperand(Var); |
| if (Op.getExplicitOperandInfo().OperandType >= |
| MCOI::OperandType::OPERAND_FIRST_TARGET) |
| return State.getExegesisTarget().randomizeTargetMCOperand( |
| Instr, Var, AssignedValue, ForbiddenRegs); |
| switch (Op.getExplicitOperandInfo().OperandType) { |
| case MCOI::OperandType::OPERAND_IMMEDIATE: |
| // FIXME: explore immediate values too. |
| AssignedValue = MCOperand::createImm(1); |
| break; |
| case MCOI::OperandType::OPERAND_REGISTER: { |
| assert(Op.isReg()); |
| auto AllowedRegs = Op.getRegisterAliasing().sourceBits(); |
| assert(AllowedRegs.size() == ForbiddenRegs.size()); |
| for (auto I : ForbiddenRegs.set_bits()) |
| AllowedRegs.reset(I); |
| if (!AllowedRegs.any()) |
| return make_error<Failure>( |
| Twine("no available registers:\ncandidates:\n") |
| .concat(debugString(State.getRegInfo(), |
| Op.getRegisterAliasing().sourceBits())) |
| .concat("\nforbidden:\n") |
| .concat(debugString(State.getRegInfo(), ForbiddenRegs))); |
| AssignedValue = MCOperand::createReg(randomBit(AllowedRegs)); |
| break; |
| } |
| default: |
| break; |
| } |
| return Error::success(); |
| } |
| |
| Error randomizeUnsetVariables(const LLVMState &State, |
| const BitVector &ForbiddenRegs, |
| InstructionTemplate &IT) { |
| for (const Variable &Var : IT.getInstr().Variables) { |
| MCOperand &AssignedValue = IT.getValueFor(Var); |
| if (!AssignedValue.isValid()) |
| if (auto Err = randomizeMCOperand(State, IT.getInstr(), Var, |
| AssignedValue, ForbiddenRegs)) |
| return Err; |
| } |
| return Error::success(); |
| } |
| |
| } // namespace exegesis |
| } // namespace llvm |