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 *> 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); 45*73471bf0Spatrick 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. 54*73471bf0Spatrick 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 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 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. 99097a140dSpatrick LLVM_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. 118097a140dSpatrick const AliasingConfigurations SelfAliasing(Variant.getInstr(), 119097a140dSpatrick Variant.getInstr()); 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)) { 137097a140dSpatrick const AliasingConfigurations Forward(Instr, *OtherInstr); 138097a140dSpatrick const AliasingConfigurations Back(*OtherInstr, Instr); 139097a140dSpatrick InstructionTemplate ThisIT(Variant); 140097a140dSpatrick InstructionTemplate OtherIT(OtherInstr); 141097a140dSpatrick if (!Forward.hasImplicitAliasing()) 142097a140dSpatrick setRandomAliasing(Forward, ThisIT, OtherIT); 143*73471bf0Spatrick else if (!Back.hasImplicitAliasing()) 144097a140dSpatrick setRandomAliasing(Back, OtherIT, ThisIT); 145097a140dSpatrick CodeTemplate CT; 146097a140dSpatrick CT.Execution = ExecutionModeBit; 147097a140dSpatrick CT.Info = std::string(ExecutionClassDescription); 148097a140dSpatrick CT.Instructions.push_back(std::move(ThisIT)); 149097a140dSpatrick CT.Instructions.push_back(std::move(OtherIT)); 150097a140dSpatrick CodeTemplates.push_back(std::move(CT)); 151097a140dSpatrick } 152097a140dSpatrick return; 153097a140dSpatrick } 154097a140dSpatrick default: 155097a140dSpatrick llvm_unreachable("Unhandled enum value"); 156097a140dSpatrick } 157097a140dSpatrick } 158097a140dSpatrick 159097a140dSpatrick SerialSnippetGenerator::~SerialSnippetGenerator() = default; 160097a140dSpatrick 161097a140dSpatrick Expected<std::vector<CodeTemplate>> 162097a140dSpatrick SerialSnippetGenerator::generateCodeTemplates( 163097a140dSpatrick InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const { 164097a140dSpatrick std::vector<CodeTemplate> Results; 165097a140dSpatrick const ExecutionMode EM = 166097a140dSpatrick getExecutionModes(Variant.getInstr(), ForbiddenRegisters); 167097a140dSpatrick for (const auto EC : kExecutionClasses) { 168097a140dSpatrick for (const auto ExecutionModeBit : getExecutionModeBits(EM & EC.Mask)) 169097a140dSpatrick appendCodeTemplates(State, Variant, ForbiddenRegisters, ExecutionModeBit, 170097a140dSpatrick EC.Description, Results); 171097a140dSpatrick if (!Results.empty()) 172097a140dSpatrick break; 173097a140dSpatrick } 174097a140dSpatrick if (Results.empty()) 175097a140dSpatrick return make_error<Failure>( 176097a140dSpatrick "No strategy found to make the execution serial"); 177097a140dSpatrick return std::move(Results); 178097a140dSpatrick } 179097a140dSpatrick 180097a140dSpatrick } // namespace exegesis 181097a140dSpatrick } // namespace llvm 182