1 //===-- SerialSnippetGenerator.cpp ------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "SerialSnippetGenerator.h"
10 
11 #include "CodeTemplate.h"
12 #include "MCInstrDescView.h"
13 #include "Target.h"
14 #include <algorithm>
15 #include <numeric>
16 #include <vector>
17 
18 namespace llvm {
19 namespace exegesis {
20 
21 struct ExecutionClass {
22   ExecutionMode Mask;
23   const char *Description;
24 } static const kExecutionClasses[] = {
25     {ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS |
26          ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS,
27      "Repeating a single implicitly serial instruction"},
28     {ExecutionMode::SERIAL_VIA_EXPLICIT_REGS,
29      "Repeating a single explicitly serial instruction"},
30     {ExecutionMode::SERIAL_VIA_MEMORY_INSTR |
31          ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR,
32      "Repeating two instructions"},
33 };
34 
35 static constexpr size_t kMaxAliasingInstructions = 10;
36 
37 static std::vector<const Instruction *>
computeAliasingInstructions(const LLVMState & State,const Instruction * Instr,size_t MaxAliasingInstructions,const BitVector & ForbiddenRegisters)38 computeAliasingInstructions(const LLVMState &State, const Instruction *Instr,
39                             size_t MaxAliasingInstructions,
40                             const BitVector &ForbiddenRegisters) {
41   // Randomly iterate the set of instructions.
42   std::vector<unsigned> Opcodes;
43   Opcodes.resize(State.getInstrInfo().getNumOpcodes());
44   std::iota(Opcodes.begin(), Opcodes.end(), 0U);
45   llvm::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator());
46 
47   std::vector<const Instruction *> AliasingInstructions;
48   for (const unsigned OtherOpcode : Opcodes) {
49     if (OtherOpcode == Instr->Description.getOpcode())
50       continue;
51     const Instruction &OtherInstr = State.getIC().getInstr(OtherOpcode);
52     const MCInstrDesc &OtherInstrDesc = OtherInstr.Description;
53     // Ignore instructions that we cannot run.
54     if (OtherInstrDesc.isPseudo() || OtherInstrDesc.usesCustomInsertionHook() ||
55         OtherInstrDesc.isBranch() || OtherInstrDesc.isIndirectBranch() ||
56         OtherInstrDesc.isCall() || OtherInstrDesc.isReturn()) {
57           continue;
58     }
59     if (OtherInstr.hasMemoryOperands())
60       continue;
61     if (!State.getExegesisTarget().allowAsBackToBack(OtherInstr))
62       continue;
63     if (Instr->hasAliasingRegistersThrough(OtherInstr, ForbiddenRegisters))
64       AliasingInstructions.push_back(&OtherInstr);
65     if (AliasingInstructions.size() >= MaxAliasingInstructions)
66       break;
67   }
68   return AliasingInstructions;
69 }
70 
getExecutionModes(const Instruction & Instr,const BitVector & ForbiddenRegisters)71 static ExecutionMode getExecutionModes(const Instruction &Instr,
72                                        const BitVector &ForbiddenRegisters) {
73   ExecutionMode EM = ExecutionMode::UNKNOWN;
74   if (Instr.hasAliasingImplicitRegisters())
75     EM |= ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS;
76   if (Instr.hasTiedRegisters())
77     EM |= ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS;
78   if (Instr.hasMemoryOperands())
79     EM |= ExecutionMode::SERIAL_VIA_MEMORY_INSTR;
80   else {
81     if (Instr.hasAliasingRegisters(ForbiddenRegisters))
82       EM |= ExecutionMode::SERIAL_VIA_EXPLICIT_REGS;
83     if (Instr.hasOneUseOrOneDef())
84       EM |= ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR;
85   }
86   return EM;
87 }
88 
appendCodeTemplates(const LLVMState & State,InstructionTemplate Variant,const BitVector & ForbiddenRegisters,ExecutionMode ExecutionModeBit,StringRef ExecutionClassDescription,std::vector<CodeTemplate> & CodeTemplates)89 static void appendCodeTemplates(const LLVMState &State,
90                                 InstructionTemplate Variant,
91                                 const BitVector &ForbiddenRegisters,
92                                 ExecutionMode ExecutionModeBit,
93                                 StringRef ExecutionClassDescription,
94                                 std::vector<CodeTemplate> &CodeTemplates) {
95   assert(isEnumValue(ExecutionModeBit) && "Bit must be a power of two");
96   switch (ExecutionModeBit) {
97   case ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS:
98     // Nothing to do, the instruction is always serial.
99     LLVM_FALLTHROUGH;
100   case ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS: {
101     // Picking whatever value for the tied variable will make the instruction
102     // serial.
103     CodeTemplate CT;
104     CT.Execution = ExecutionModeBit;
105     CT.Info = std::string(ExecutionClassDescription);
106     CT.Instructions.push_back(std::move(Variant));
107     CodeTemplates.push_back(std::move(CT));
108     return;
109   }
110   case ExecutionMode::SERIAL_VIA_MEMORY_INSTR: {
111     // Select back-to-back memory instruction.
112     // TODO: Implement me.
113     return;
114   }
115   case ExecutionMode::SERIAL_VIA_EXPLICIT_REGS: {
116     // Making the execution of this instruction serial by selecting one def
117     // register to alias with one use register.
118     const AliasingConfigurations SelfAliasing(Variant.getInstr(),
119                                               Variant.getInstr());
120     assert(!SelfAliasing.empty() && !SelfAliasing.hasImplicitAliasing() &&
121            "Instr must alias itself explicitly");
122     // This is a self aliasing instruction so defs and uses are from the same
123     // instance, hence twice Variant in the following call.
124     setRandomAliasing(SelfAliasing, Variant, Variant);
125     CodeTemplate CT;
126     CT.Execution = ExecutionModeBit;
127     CT.Info = std::string(ExecutionClassDescription);
128     CT.Instructions.push_back(std::move(Variant));
129     CodeTemplates.push_back(std::move(CT));
130     return;
131   }
132   case ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR: {
133     const Instruction &Instr = Variant.getInstr();
134     // Select back-to-back non-memory instruction.
135     for (const auto *OtherInstr : computeAliasingInstructions(
136              State, &Instr, kMaxAliasingInstructions, ForbiddenRegisters)) {
137       const AliasingConfigurations Forward(Instr, *OtherInstr);
138       const AliasingConfigurations Back(*OtherInstr, Instr);
139       InstructionTemplate ThisIT(Variant);
140       InstructionTemplate OtherIT(OtherInstr);
141       if (!Forward.hasImplicitAliasing())
142         setRandomAliasing(Forward, ThisIT, OtherIT);
143       else if (!Back.hasImplicitAliasing())
144         setRandomAliasing(Back, OtherIT, ThisIT);
145       CodeTemplate CT;
146       CT.Execution = ExecutionModeBit;
147       CT.Info = std::string(ExecutionClassDescription);
148       CT.Instructions.push_back(std::move(ThisIT));
149       CT.Instructions.push_back(std::move(OtherIT));
150       CodeTemplates.push_back(std::move(CT));
151     }
152     return;
153   }
154   default:
155     llvm_unreachable("Unhandled enum value");
156   }
157 }
158 
159 SerialSnippetGenerator::~SerialSnippetGenerator() = default;
160 
161 Expected<std::vector<CodeTemplate>>
generateCodeTemplates(InstructionTemplate Variant,const BitVector & ForbiddenRegisters) const162 SerialSnippetGenerator::generateCodeTemplates(
163     InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const {
164   std::vector<CodeTemplate> Results;
165   const ExecutionMode EM =
166       getExecutionModes(Variant.getInstr(), ForbiddenRegisters);
167   for (const auto EC : kExecutionClasses) {
168     for (const auto ExecutionModeBit : getExecutionModeBits(EM & EC.Mask))
169       appendCodeTemplates(State, Variant, ForbiddenRegisters, ExecutionModeBit,
170                           EC.Description, Results);
171     if (!Results.empty())
172       break;
173   }
174   if (Results.empty())
175     return make_error<Failure>(
176         "No strategy found to make the execution serial");
177   return std::move(Results);
178 }
179 
180 } // namespace exegesis
181 } // namespace llvm
182