//===-- InstructionSnippetGenerator.cpp -------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "InstructionSnippetGenerator.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" #include "llvm/MC/MCInstBuilder.h" #include #include #include namespace exegesis { void Variable::print(llvm::raw_ostream &OS, const llvm::MCRegisterInfo *RegInfo) const { OS << "IsUse=" << IsUse << " IsDef=" << IsDef << " possible regs: {"; for (const size_t Reg : PossibleRegisters) { if (RegInfo) OS << RegInfo->getName(Reg); else OS << Reg; OS << ","; } OS << "} "; if (ExplicitOperands.empty()) { OS << "implicit"; } else { OS << "explicit ops: {"; for (const size_t Op : ExplicitOperands) OS << Op << ","; OS << "}"; } OS << "\n"; } // Update the state of a Variable with an explicit operand. static void updateExplicitOperandVariable(const llvm::MCRegisterInfo &RegInfo, const llvm::MCInstrDesc &InstrInfo, const size_t OpIndex, const llvm::BitVector &ReservedRegs, Variable &Var) { const bool IsDef = OpIndex < InstrInfo.getNumDefs(); if (IsDef) Var.IsDef = true; if (!IsDef) Var.IsUse = true; Var.ExplicitOperands.push_back(OpIndex); const llvm::MCOperandInfo &OpInfo = InstrInfo.opInfo_begin()[OpIndex]; if (OpInfo.RegClass >= 0) { Var.IsReg = true; for (const llvm::MCPhysReg &Reg : RegInfo.getRegClass(OpInfo.RegClass)) { if (!ReservedRegs[Reg]) Var.PossibleRegisters.insert(Reg); } } } static Variable &findVariableWithOperand(llvm::SmallVector &Vars, size_t OpIndex) { // Vars.size() is small (<10) so a linear scan is good enough. for (Variable &Var : Vars) { if (llvm::is_contained(Var.ExplicitOperands, OpIndex)) return Var; } assert(false && "Illegal state"); static Variable *const EmptyVariable = new Variable(); return *EmptyVariable; } llvm::SmallVector getVariables(const llvm::MCRegisterInfo &RegInfo, const llvm::MCInstrDesc &InstrInfo, const llvm::BitVector &ReservedRegs) { llvm::SmallVector Vars; // For each operand, its "tied to" operand or -1. llvm::SmallVector TiedToMap; for (size_t I = 0, E = InstrInfo.getNumOperands(); I < E; ++I) { TiedToMap.push_back(InstrInfo.getOperandConstraint(I, llvm::MCOI::TIED_TO)); } // Adding non tied operands. for (size_t I = 0, E = InstrInfo.getNumOperands(); I < E; ++I) { if (TiedToMap[I] >= 0) continue; // dropping tied ones. Vars.emplace_back(); updateExplicitOperandVariable(RegInfo, InstrInfo, I, ReservedRegs, Vars.back()); } // Adding tied operands to existing variables. for (size_t I = 0, E = InstrInfo.getNumOperands(); I < E; ++I) { if (TiedToMap[I] < 0) continue; // dropping non-tied ones. updateExplicitOperandVariable(RegInfo, InstrInfo, I, ReservedRegs, findVariableWithOperand(Vars, TiedToMap[I])); } // Adding implicit defs. for (size_t I = 0, E = InstrInfo.getNumImplicitDefs(); I < E; ++I) { Vars.emplace_back(); Variable &Var = Vars.back(); const llvm::MCPhysReg Reg = InstrInfo.getImplicitDefs()[I]; assert(!ReservedRegs[Reg] && "implicit def of reserved register"); Var.PossibleRegisters.insert(Reg); Var.IsDef = true; Var.IsReg = true; } // Adding implicit uses. for (size_t I = 0, E = InstrInfo.getNumImplicitUses(); I < E; ++I) { Vars.emplace_back(); Variable &Var = Vars.back(); const llvm::MCPhysReg Reg = InstrInfo.getImplicitUses()[I]; assert(!ReservedRegs[Reg] && "implicit use of reserved register"); Var.PossibleRegisters.insert(Reg); Var.IsUse = true; Var.IsReg = true; } return Vars; } VariableAssignment::VariableAssignment(size_t VarIdx, llvm::MCPhysReg AssignedReg) : VarIdx(VarIdx), AssignedReg(AssignedReg) {} bool VariableAssignment::operator==(const VariableAssignment &Other) const { return std::tie(VarIdx, AssignedReg) == std::tie(Other.VarIdx, Other.AssignedReg); } bool VariableAssignment::operator<(const VariableAssignment &Other) const { return std::tie(VarIdx, AssignedReg) < std::tie(Other.VarIdx, Other.AssignedReg); } void dumpAssignmentChain(const llvm::MCRegisterInfo &RegInfo, const AssignmentChain &Chain) { for (const VariableAssignment &Assignment : Chain) { llvm::outs() << llvm::format("(%d %s) ", Assignment.VarIdx, RegInfo.getName(Assignment.AssignedReg)); } llvm::outs() << "\n"; } std::vector computeSequentialAssignmentChains(const llvm::MCRegisterInfo &RegInfo, llvm::ArrayRef Vars) { using graph::Node; graph::Graph Graph; // Add register aliasing to the graph. setupRegisterAliasing(RegInfo, Graph); // Adding variables to the graph. for (size_t I = 0, E = Vars.size(); I < E; ++I) { const Variable &Var = Vars[I]; const Node N = Node::Var(I); if (Var.IsDef) { Graph.connect(Node::In(), N); for (const size_t Reg : Var.PossibleRegisters) Graph.connect(N, Node::Reg(Reg)); } if (Var.IsUse) { Graph.connect(N, Node::Out()); for (const size_t Reg : Var.PossibleRegisters) Graph.connect(Node::Reg(Reg), N); } } // Find all possible dependency chains (aka all possible paths from In to Out // node). std::vector AllChains; for (;;) { const auto Path = Graph.getPathFrom(Node::In(), Node::Out()); if (Path.empty()) break; switch (Path.size()) { case 0: case 1: case 2: case 4: assert(false && "Illegal state"); break; case 3: { // IN -> variable -> OUT const size_t VarIdx = Path[1].varValue(); for (size_t Reg : Vars[VarIdx].PossibleRegisters) { AllChains.emplace_back(); AllChains.back().emplace(VarIdx, Reg); } Graph.disconnect(Path[0], Path[1]); // IN -> variable Graph.disconnect(Path[1], Path[2]); // variable -> OUT break; } default: { // IN -> var1 -> Reg[...] -> var2 -> OUT const size_t Last = Path.size() - 1; const size_t Var1 = Path[1].varValue(); const llvm::MCPhysReg Reg1 = Path[2].regValue(); const llvm::MCPhysReg Reg2 = Path[Last - 2].regValue(); const size_t Var2 = Path[Last - 1].varValue(); AllChains.emplace_back(); AllChains.back().emplace(Var1, Reg1); AllChains.back().emplace(Var2, Reg2); Graph.disconnect(Path[1], Path[2]); // Var1 -> Reg[0] break; } } } return AllChains; } std::vector getRandomAssignment(llvm::ArrayRef Vars, llvm::ArrayRef Chains, const std::function &RandomIndexForSize) { // Registers are initialized with 0 (aka NoRegister). std::vector Registers(Vars.size(), 0); if (Chains.empty()) return Registers; // Pick one of the chains and set Registers that are fully constrained (have // no degrees of freedom). const size_t ChainIndex = RandomIndexForSize(Chains.size()); for (const VariableAssignment Assignment : Chains[ChainIndex]) Registers[Assignment.VarIdx] = Assignment.AssignedReg; // Registers with remaining degrees of freedom are assigned randomly. for (size_t I = 0, E = Vars.size(); I < E; ++I) { llvm::MCPhysReg &Reg = Registers[I]; const Variable &Var = Vars[I]; const auto &PossibleRegisters = Var.PossibleRegisters; if (Reg > 0 || PossibleRegisters.empty()) continue; Reg = PossibleRegisters[RandomIndexForSize(PossibleRegisters.size())]; } return Registers; } // Finds a matching register `reg` for variable `VarIdx` and sets // `RegAssignments[r]` to `VarIdx`. Returns false if no matching can be found. // `seen.count(r)` is 1 if register `reg` has been processed. static bool findMatchingRegister( llvm::ArrayRef Vars, const size_t VarIdx, std::unordered_set &Seen, std::unordered_map &RegAssignments) { for (const llvm::MCPhysReg Reg : Vars[VarIdx].PossibleRegisters) { if (!Seen.count(Reg)) { Seen.insert(Reg); // Mark `Reg` as seen. // If `Reg` is not assigned to a variable, or if `Reg` was assigned to a // variable which has an alternate possible register, assign `Reg` to // variable `VarIdx`. Since `Reg` is marked as assigned in the above line, // `RegAssignments[r]` in the following recursive call will not get // assigned `Reg` again. const auto AssignedVarIt = RegAssignments.find(Reg); if (AssignedVarIt == RegAssignments.end() || findMatchingRegister(Vars, AssignedVarIt->second, Seen, RegAssignments)) { RegAssignments[Reg] = VarIdx; return true; } } } return false; } // This is actually a maximum bipartite matching problem: // https://en.wikipedia.org/wiki/Matching_(graph_theory)#Bipartite_matching // The graph has variables on the left and registers on the right, with an edge // between variable `I` and register `Reg` iff // `Vars[I].PossibleRegisters.count(A)`. // Note that a greedy approach won't work for cases like: // Vars[0] PossibleRegisters={C,B} // Vars[1] PossibleRegisters={A,B} // Vars[2] PossibleRegisters={A,C} // There is a feasible solution {0->B, 1->A, 2->C}, but the greedy solution is // {0->C, 1->A, oops}. std::vector getExclusiveAssignment(llvm::ArrayRef Vars) { // `RegAssignments[r]` is the variable id that was assigned register `Reg`. std::unordered_map RegAssignments; for (size_t VarIdx = 0, E = Vars.size(); VarIdx < E; ++VarIdx) { if (!Vars[VarIdx].IsReg) continue; std::unordered_set Seen; if (!findMatchingRegister(Vars, VarIdx, Seen, RegAssignments)) return {}; // Infeasible. } std::vector Registers(Vars.size(), 0); for (const auto &RegVarIdx : RegAssignments) Registers[RegVarIdx.second] = RegVarIdx.first; return Registers; } std::vector getGreedyAssignment(llvm::ArrayRef Vars) { std::vector Registers(Vars.size(), 0); llvm::SmallSet Assigned; for (size_t VarIdx = 0, E = Vars.size(); VarIdx < E; ++VarIdx) { const auto &Var = Vars[VarIdx]; if (!Var.IsReg) continue; if (Var.PossibleRegisters.empty()) return {}; // Try possible registers until an unassigned one is found. for (const auto Reg : Var.PossibleRegisters) { if (Assigned.insert(Reg).second) { Registers[VarIdx] = Reg; break; } } // Fallback to first possible register. if (Registers[VarIdx] == 0) Registers[VarIdx] = Var.PossibleRegisters[0]; } return Registers; } llvm::MCInst generateMCInst(const llvm::MCInstrDesc &InstrInfo, llvm::ArrayRef Vars, llvm::ArrayRef VarRegs) { const size_t NumOperands = InstrInfo.getNumOperands(); llvm::SmallVector OperandToRegister(NumOperands, 0); // We browse the variable and for each explicit operands we set the selected // register in the OperandToRegister array. for (size_t I = 0, E = Vars.size(); I < E; ++I) { for (const size_t OpIndex : Vars[I].ExplicitOperands) { OperandToRegister[OpIndex] = VarRegs[I]; } } // Building the instruction. llvm::MCInstBuilder Builder(InstrInfo.getOpcode()); for (size_t I = 0, E = InstrInfo.getNumOperands(); I < E; ++I) { const llvm::MCOperandInfo &OpInfo = InstrInfo.opInfo_begin()[I]; switch (OpInfo.OperandType) { case llvm::MCOI::OperandType::OPERAND_REGISTER: Builder.addReg(OperandToRegister[I]); break; case llvm::MCOI::OperandType::OPERAND_IMMEDIATE: Builder.addImm(1); break; default: Builder.addOperand(llvm::MCOperand()); } } return Builder; } } // namespace exegesis