1097a140dSpatrick //===-- SerialSnippetGenerator.cpp ------------------------------*- C++ -*-===//
2097a140dSpatrick //
3097a140dSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4097a140dSpatrick // See https://llvm.org/LICENSE.txt for license information.
5097a140dSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6097a140dSpatrick //
7097a140dSpatrick //===----------------------------------------------------------------------===//
8097a140dSpatrick
9097a140dSpatrick #include "SerialSnippetGenerator.h"
10097a140dSpatrick
11097a140dSpatrick #include "CodeTemplate.h"
12097a140dSpatrick #include "MCInstrDescView.h"
13097a140dSpatrick #include "Target.h"
14097a140dSpatrick #include <algorithm>
15097a140dSpatrick #include <numeric>
16097a140dSpatrick #include <vector>
17097a140dSpatrick
18097a140dSpatrick namespace llvm {
19097a140dSpatrick namespace exegesis {
20097a140dSpatrick
21097a140dSpatrick struct ExecutionClass {
22097a140dSpatrick ExecutionMode Mask;
23097a140dSpatrick const char *Description;
24097a140dSpatrick } static const kExecutionClasses[] = {
25097a140dSpatrick {ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS |
26097a140dSpatrick ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS,
27097a140dSpatrick "Repeating a single implicitly serial instruction"},
28097a140dSpatrick {ExecutionMode::SERIAL_VIA_EXPLICIT_REGS,
29097a140dSpatrick "Repeating a single explicitly serial instruction"},
30097a140dSpatrick {ExecutionMode::SERIAL_VIA_MEMORY_INSTR |
31097a140dSpatrick ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR,
32097a140dSpatrick "Repeating two instructions"},
33097a140dSpatrick };
34097a140dSpatrick
35097a140dSpatrick static constexpr size_t kMaxAliasingInstructions = 10;
36097a140dSpatrick
37097a140dSpatrick static std::vector<const Instruction *>
computeAliasingInstructions(const LLVMState & State,const Instruction * Instr,size_t MaxAliasingInstructions,const BitVector & ForbiddenRegisters)38097a140dSpatrick computeAliasingInstructions(const LLVMState &State, const Instruction *Instr,
39097a140dSpatrick size_t MaxAliasingInstructions,
40097a140dSpatrick const BitVector &ForbiddenRegisters) {
41097a140dSpatrick // Randomly iterate the set of instructions.
42097a140dSpatrick std::vector<unsigned> Opcodes;
43097a140dSpatrick Opcodes.resize(State.getInstrInfo().getNumOpcodes());
44097a140dSpatrick std::iota(Opcodes.begin(), Opcodes.end(), 0U);
4573471bf0Spatrick llvm::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator());
46097a140dSpatrick
47097a140dSpatrick std::vector<const Instruction *> AliasingInstructions;
48097a140dSpatrick for (const unsigned OtherOpcode : Opcodes) {
49097a140dSpatrick if (OtherOpcode == Instr->Description.getOpcode())
50097a140dSpatrick continue;
51097a140dSpatrick const Instruction &OtherInstr = State.getIC().getInstr(OtherOpcode);
52097a140dSpatrick const MCInstrDesc &OtherInstrDesc = OtherInstr.Description;
53097a140dSpatrick // Ignore instructions that we cannot run.
5473471bf0Spatrick if (OtherInstrDesc.isPseudo() || OtherInstrDesc.usesCustomInsertionHook() ||
55097a140dSpatrick OtherInstrDesc.isBranch() || OtherInstrDesc.isIndirectBranch() ||
56097a140dSpatrick OtherInstrDesc.isCall() || OtherInstrDesc.isReturn()) {
57097a140dSpatrick continue;
58097a140dSpatrick }
59097a140dSpatrick if (OtherInstr.hasMemoryOperands())
60097a140dSpatrick continue;
61097a140dSpatrick if (!State.getExegesisTarget().allowAsBackToBack(OtherInstr))
62097a140dSpatrick continue;
63097a140dSpatrick if (Instr->hasAliasingRegistersThrough(OtherInstr, ForbiddenRegisters))
64097a140dSpatrick AliasingInstructions.push_back(&OtherInstr);
65097a140dSpatrick if (AliasingInstructions.size() >= MaxAliasingInstructions)
66097a140dSpatrick break;
67097a140dSpatrick }
68097a140dSpatrick return AliasingInstructions;
69097a140dSpatrick }
70097a140dSpatrick
getExecutionModes(const Instruction & Instr,const BitVector & ForbiddenRegisters)71097a140dSpatrick static ExecutionMode getExecutionModes(const Instruction &Instr,
72097a140dSpatrick const BitVector &ForbiddenRegisters) {
73097a140dSpatrick ExecutionMode EM = ExecutionMode::UNKNOWN;
74097a140dSpatrick if (Instr.hasAliasingImplicitRegisters())
75097a140dSpatrick EM |= ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS;
76097a140dSpatrick if (Instr.hasTiedRegisters())
77097a140dSpatrick EM |= ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS;
78097a140dSpatrick if (Instr.hasMemoryOperands())
79097a140dSpatrick EM |= ExecutionMode::SERIAL_VIA_MEMORY_INSTR;
80097a140dSpatrick else {
81097a140dSpatrick if (Instr.hasAliasingRegisters(ForbiddenRegisters))
82097a140dSpatrick EM |= ExecutionMode::SERIAL_VIA_EXPLICIT_REGS;
83097a140dSpatrick if (Instr.hasOneUseOrOneDef())
84097a140dSpatrick EM |= ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR;
85097a140dSpatrick }
86097a140dSpatrick return EM;
87097a140dSpatrick }
88097a140dSpatrick
appendCodeTemplates(const LLVMState & State,InstructionTemplate Variant,const BitVector & ForbiddenRegisters,ExecutionMode ExecutionModeBit,StringRef ExecutionClassDescription,std::vector<CodeTemplate> & CodeTemplates)89097a140dSpatrick static void appendCodeTemplates(const LLVMState &State,
90097a140dSpatrick InstructionTemplate Variant,
91097a140dSpatrick const BitVector &ForbiddenRegisters,
92097a140dSpatrick ExecutionMode ExecutionModeBit,
93097a140dSpatrick StringRef ExecutionClassDescription,
94097a140dSpatrick std::vector<CodeTemplate> &CodeTemplates) {
95097a140dSpatrick assert(isEnumValue(ExecutionModeBit) && "Bit must be a power of two");
96097a140dSpatrick switch (ExecutionModeBit) {
97097a140dSpatrick case ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS:
98097a140dSpatrick // Nothing to do, the instruction is always serial.
99*d415bd75Srobert [[fallthrough]];
100097a140dSpatrick case ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS: {
101097a140dSpatrick // Picking whatever value for the tied variable will make the instruction
102097a140dSpatrick // serial.
103097a140dSpatrick CodeTemplate CT;
104097a140dSpatrick CT.Execution = ExecutionModeBit;
105097a140dSpatrick CT.Info = std::string(ExecutionClassDescription);
106097a140dSpatrick CT.Instructions.push_back(std::move(Variant));
107097a140dSpatrick CodeTemplates.push_back(std::move(CT));
108097a140dSpatrick return;
109097a140dSpatrick }
110097a140dSpatrick case ExecutionMode::SERIAL_VIA_MEMORY_INSTR: {
111097a140dSpatrick // Select back-to-back memory instruction.
112097a140dSpatrick // TODO: Implement me.
113097a140dSpatrick return;
114097a140dSpatrick }
115097a140dSpatrick case ExecutionMode::SERIAL_VIA_EXPLICIT_REGS: {
116097a140dSpatrick // Making the execution of this instruction serial by selecting one def
117097a140dSpatrick // register to alias with one use register.
118*d415bd75Srobert const AliasingConfigurations SelfAliasing(
119*d415bd75Srobert Variant.getInstr(), Variant.getInstr(), ForbiddenRegisters);
120097a140dSpatrick assert(!SelfAliasing.empty() && !SelfAliasing.hasImplicitAliasing() &&
121097a140dSpatrick "Instr must alias itself explicitly");
122097a140dSpatrick // This is a self aliasing instruction so defs and uses are from the same
123097a140dSpatrick // instance, hence twice Variant in the following call.
124097a140dSpatrick setRandomAliasing(SelfAliasing, Variant, Variant);
125097a140dSpatrick CodeTemplate CT;
126097a140dSpatrick CT.Execution = ExecutionModeBit;
127097a140dSpatrick CT.Info = std::string(ExecutionClassDescription);
128097a140dSpatrick CT.Instructions.push_back(std::move(Variant));
129097a140dSpatrick CodeTemplates.push_back(std::move(CT));
130097a140dSpatrick return;
131097a140dSpatrick }
132097a140dSpatrick case ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR: {
133097a140dSpatrick const Instruction &Instr = Variant.getInstr();
134097a140dSpatrick // Select back-to-back non-memory instruction.
135097a140dSpatrick for (const auto *OtherInstr : computeAliasingInstructions(
136097a140dSpatrick State, &Instr, kMaxAliasingInstructions, ForbiddenRegisters)) {
137*d415bd75Srobert const AliasingConfigurations Forward(Instr, *OtherInstr,
138*d415bd75Srobert ForbiddenRegisters);
139*d415bd75Srobert const AliasingConfigurations Back(*OtherInstr, Instr, ForbiddenRegisters);
140097a140dSpatrick InstructionTemplate ThisIT(Variant);
141097a140dSpatrick InstructionTemplate OtherIT(OtherInstr);
142097a140dSpatrick if (!Forward.hasImplicitAliasing())
143097a140dSpatrick setRandomAliasing(Forward, ThisIT, OtherIT);
14473471bf0Spatrick else if (!Back.hasImplicitAliasing())
145097a140dSpatrick setRandomAliasing(Back, OtherIT, ThisIT);
146097a140dSpatrick CodeTemplate CT;
147097a140dSpatrick CT.Execution = ExecutionModeBit;
148097a140dSpatrick CT.Info = std::string(ExecutionClassDescription);
149097a140dSpatrick CT.Instructions.push_back(std::move(ThisIT));
150097a140dSpatrick CT.Instructions.push_back(std::move(OtherIT));
151097a140dSpatrick CodeTemplates.push_back(std::move(CT));
152097a140dSpatrick }
153097a140dSpatrick return;
154097a140dSpatrick }
155097a140dSpatrick default:
156097a140dSpatrick llvm_unreachable("Unhandled enum value");
157097a140dSpatrick }
158097a140dSpatrick }
159097a140dSpatrick
160097a140dSpatrick SerialSnippetGenerator::~SerialSnippetGenerator() = default;
161097a140dSpatrick
162097a140dSpatrick Expected<std::vector<CodeTemplate>>
generateCodeTemplates(InstructionTemplate Variant,const BitVector & ForbiddenRegisters) const163097a140dSpatrick SerialSnippetGenerator::generateCodeTemplates(
164097a140dSpatrick InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const {
165097a140dSpatrick std::vector<CodeTemplate> Results;
166097a140dSpatrick const ExecutionMode EM =
167097a140dSpatrick getExecutionModes(Variant.getInstr(), ForbiddenRegisters);
168097a140dSpatrick for (const auto EC : kExecutionClasses) {
169097a140dSpatrick for (const auto ExecutionModeBit : getExecutionModeBits(EM & EC.Mask))
170097a140dSpatrick appendCodeTemplates(State, Variant, ForbiddenRegisters, ExecutionModeBit,
171097a140dSpatrick EC.Description, Results);
172097a140dSpatrick if (!Results.empty())
173097a140dSpatrick break;
174097a140dSpatrick }
175097a140dSpatrick if (Results.empty())
176097a140dSpatrick return make_error<Failure>(
177097a140dSpatrick "No strategy found to make the execution serial");
178097a140dSpatrick return std::move(Results);
179097a140dSpatrick }
180097a140dSpatrick
181097a140dSpatrick } // namespace exegesis
182097a140dSpatrick } // namespace llvm
183