| //===- PseudoLoweringEmitter.cpp - PseudoLowering Generator -----*- 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 "Common/CodeGenInstruction.h" |
| #include "Common/CodeGenTarget.h" |
| #include "llvm/ADT/IndexedMap.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringMap.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/TableGen/Error.h" |
| #include "llvm/TableGen/Record.h" |
| #include "llvm/TableGen/TGTimer.h" |
| #include "llvm/TableGen/TableGenBackend.h" |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "pseudo-lowering" |
| |
| namespace { |
| class PseudoLoweringEmitter { |
| struct OpData { |
| enum MapKind { Operand, Imm, Reg } Kind; |
| union { |
| unsigned OpNo; // Operand number mapped to. |
| uint64_t ImmVal; // Integer immedate value. |
| const Record *RegRec; // Physical register. |
| }; |
| }; |
| struct PseudoExpansion { |
| CodeGenInstruction Source; // The source pseudo instruction definition. |
| CodeGenInstruction Dest; // The destination instruction to lower to. |
| IndexedMap<OpData> OperandMap; |
| |
| PseudoExpansion(CodeGenInstruction &s, CodeGenInstruction &d, |
| IndexedMap<OpData> &m) |
| : Source(s), Dest(d), OperandMap(m) {} |
| }; |
| |
| const RecordKeeper &Records; |
| |
| // It's overkill to have an instance of the full CodeGenTarget object, |
| // but it loads everything on demand, not in the constructor, so it's |
| // lightweight in performance, so it works out OK. |
| const CodeGenTarget Target; |
| |
| SmallVector<PseudoExpansion, 64> Expansions; |
| |
| void addOperandMapping(unsigned MIOpNo, unsigned NumOps, const Record *Rec, |
| const DagInit *Dag, unsigned DagIdx, |
| const Record *OpRec, IndexedMap<OpData> &OperandMap, |
| const StringMap<unsigned> &SourceOperands, |
| const CodeGenInstruction &SourceInsn); |
| void evaluateExpansion(const Record *Pseudo); |
| void emitLoweringEmitter(raw_ostream &o); |
| |
| public: |
| PseudoLoweringEmitter(const RecordKeeper &R) : Records(R), Target(R) {} |
| |
| /// run - Output the pseudo-lowerings. |
| void run(raw_ostream &o); |
| }; |
| } // End anonymous namespace |
| |
| void PseudoLoweringEmitter::addOperandMapping( |
| unsigned MIOpNo, unsigned NumOps, const Record *Rec, const DagInit *Dag, |
| unsigned DagIdx, const Record *OpRec, IndexedMap<OpData> &OperandMap, |
| const StringMap<unsigned> &SourceOperands, |
| const CodeGenInstruction &SourceInsn) { |
| const Init *DagArg = Dag->getArg(DagIdx); |
| if (const DefInit *DI = dyn_cast<DefInit>(DagArg)) { |
| // Physical register reference. Explicit check for the special case |
| // "zero_reg" definition. |
| if (DI->getDef()->isSubClassOf("Register") || |
| DI->getDef()->getName() == "zero_reg") { |
| auto &Entry = OperandMap[MIOpNo]; |
| Entry.Kind = OpData::Reg; |
| Entry.RegRec = DI->getDef(); |
| return; |
| } |
| |
| if (DI->getDef() != OpRec) |
| PrintFatalError(Rec, "In pseudo instruction '" + Rec->getName() + |
| "', operand type '" + DI->getDef()->getName() + |
| "' does not match expansion operand type '" + |
| OpRec->getName() + "'"); |
| |
| StringMap<unsigned>::const_iterator SourceOp = |
| SourceOperands.find(Dag->getArgNameStr(DagIdx)); |
| if (SourceOp == SourceOperands.end()) |
| PrintFatalError(Rec, "In pseudo instruction '" + Rec->getName() + |
| "', output operand '" + |
| Dag->getArgNameStr(DagIdx) + |
| "' has no matching source operand"); |
| const auto &SrcOpnd = SourceInsn.Operands[SourceOp->getValue()]; |
| if (NumOps != SrcOpnd.MINumOperands) |
| PrintFatalError( |
| Rec, |
| "In pseudo instruction '" + Rec->getName() + "', output operand '" + |
| OpRec->getName() + |
| "' has a different number of sub operands than source operand '" + |
| SrcOpnd.Rec->getName() + "'"); |
| |
| // Source operand maps to destination operand. Do it for each corresponding |
| // MachineInstr operand, not just the first. |
| for (unsigned I = 0, E = NumOps; I != E; ++I) { |
| auto &Entry = OperandMap[MIOpNo + I]; |
| Entry.Kind = OpData::Operand; |
| Entry.OpNo = SrcOpnd.MIOperandNo + I; |
| } |
| |
| LLVM_DEBUG(dbgs() << " " << SourceOp->getValue() << " ==> " << DagIdx |
| << "\n"); |
| } else if (const auto *II = dyn_cast<IntInit>(DagArg)) { |
| assert(NumOps == 1); |
| auto &Entry = OperandMap[MIOpNo]; |
| Entry.Kind = OpData::Imm; |
| Entry.ImmVal = II->getValue(); |
| } else if (const auto *BI = dyn_cast<BitsInit>(DagArg)) { |
| assert(NumOps == 1); |
| auto &Entry = OperandMap[MIOpNo]; |
| Entry.Kind = OpData::Imm; |
| Entry.ImmVal = *BI->convertInitializerToInt(); |
| } else { |
| llvm_unreachable("Unhandled pseudo-expansion argument type!"); |
| } |
| } |
| |
| void PseudoLoweringEmitter::evaluateExpansion(const Record *Rec) { |
| LLVM_DEBUG(dbgs() << "Pseudo definition: " << Rec->getName() << "\n"); |
| |
| // Validate that the result pattern has the corrent number and types |
| // of arguments for the instruction it references. |
| const DagInit *Dag = Rec->getValueAsDag("ResultInst"); |
| assert(Dag && "Missing result instruction in pseudo expansion!"); |
| LLVM_DEBUG(dbgs() << " Result: " << *Dag << "\n"); |
| |
| const DefInit *OpDef = dyn_cast<DefInit>(Dag->getOperator()); |
| if (!OpDef) |
| PrintFatalError(Rec, "In pseudo instruction '" + Rec->getName() + |
| "', result operator is not a record"); |
| const Record *Operator = OpDef->getDef(); |
| if (!Operator->isSubClassOf("Instruction")) |
| PrintFatalError(Rec, "In pseudo instruction '" + Rec->getName() + |
| "', result operator '" + Operator->getName() + |
| "' is not an instruction"); |
| |
| CodeGenInstruction Insn(Operator); |
| |
| if (Insn.isCodeGenOnly || Insn.isPseudo) |
| PrintFatalError(Rec, "In pseudo instruction '" + Rec->getName() + |
| "', result operator '" + Operator->getName() + |
| "' cannot be a pseudo instruction"); |
| |
| if (Insn.Operands.size() != Dag->getNumArgs()) |
| PrintFatalError(Rec, "In pseudo instruction '" + Rec->getName() + |
| "', result operator '" + Operator->getName() + |
| "' has the wrong number of operands"); |
| |
| // If there are more operands that weren't in the DAG, they have to |
| // be operands that have default values, or we have an error. Currently, |
| // Operands that are a subclass of OperandWithDefaultOp have default values. |
| |
| // Validate that each result pattern argument has a matching (by name) |
| // argument in the source instruction, in either the (outs) or (ins) list. |
| // Also check that the type of the arguments match. |
| // |
| // Record the mapping of the source to result arguments for use by |
| // the lowering emitter. |
| CodeGenInstruction SourceInsn(Rec); |
| StringMap<unsigned> SourceOperands; |
| for (const auto &[Idx, SrcOp] : enumerate(SourceInsn.Operands)) |
| SourceOperands[SrcOp.Name] = Idx; |
| |
| unsigned NumMIOperands = 0; |
| for (const auto &Op : Insn.Operands) |
| NumMIOperands += Op.MINumOperands; |
| IndexedMap<OpData> OperandMap; |
| OperandMap.grow(NumMIOperands); |
| |
| // FIXME: This pass currently can only expand a pseudo to a single |
| // instruction. The pseudo expansion really should take a list of dags, not |
| // just a single dag, so we can do fancier things. |
| LLVM_DEBUG(dbgs() << " Operand mapping:\n"); |
| for (const auto &[Idx, DstOp] : enumerate(Insn.Operands)) { |
| unsigned MIOpNo = DstOp.MIOperandNo; |
| |
| if (const auto *SubDag = dyn_cast<DagInit>(Dag->getArg(Idx))) { |
| if (!DstOp.MIOperandInfo || DstOp.MIOperandInfo->getNumArgs() == 0) |
| PrintFatalError(Rec, "In pseudo instruction '" + Rec->getName() + |
| "', operand '" + DstOp.Rec->getName() + |
| "' does not have suboperands"); |
| if (DstOp.MINumOperands != SubDag->getNumArgs()) { |
| PrintFatalError( |
| Rec, "In pseudo instruction '" + Rec->getName() + "', '" + |
| SubDag->getAsString() + |
| "' has wrong number of operands for operand type '" + |
| DstOp.Rec->getName() + "'"); |
| } |
| for (unsigned I = 0, E = DstOp.MINumOperands; I != E; ++I) { |
| auto *OpndRec = cast<DefInit>(DstOp.MIOperandInfo->getArg(I))->getDef(); |
| addOperandMapping(MIOpNo + I, 1, Rec, SubDag, I, OpndRec, OperandMap, |
| SourceOperands, SourceInsn); |
| } |
| } else { |
| addOperandMapping(MIOpNo, DstOp.MINumOperands, Rec, Dag, Idx, DstOp.Rec, |
| OperandMap, SourceOperands, SourceInsn); |
| } |
| } |
| |
| Expansions.emplace_back(SourceInsn, Insn, OperandMap); |
| } |
| |
| void PseudoLoweringEmitter::emitLoweringEmitter(raw_ostream &o) { |
| // Emit file header. |
| emitSourceFileHeader("Pseudo-instruction MC lowering Source Fragment", o); |
| |
| o << "bool " << Target.getName() + "AsmPrinter::\n" |
| << "lowerPseudoInstExpansion(const MachineInstr *MI, MCInst &Inst) {\n"; |
| |
| if (!Expansions.empty()) { |
| o << " Inst.clear();\n" |
| << " switch (MI->getOpcode()) {\n" |
| << " default: return false;\n"; |
| for (auto &Expansion : Expansions) { |
| CodeGenInstruction &Source = Expansion.Source; |
| CodeGenInstruction &Dest = Expansion.Dest; |
| o << " case " << Source.Namespace << "::" << Source.TheDef->getName() |
| << ": {\n" |
| << " MCOperand MCOp;\n" |
| << " Inst.setOpcode(" << Dest.Namespace |
| << "::" << Dest.TheDef->getName() << ");\n"; |
| |
| // Copy the operands from the source instruction. |
| // FIXME: Instruction operands with defaults values (predicates and cc_out |
| // in ARM, for example shouldn't need explicit values in the |
| // expansion DAG. |
| for (const auto &DestOperand : Dest.Operands) { |
| o << " // Operand: " << DestOperand.Name << "\n"; |
| unsigned MIOpNo = DestOperand.MIOperandNo; |
| for (unsigned i = 0, e = DestOperand.MINumOperands; i != e; ++i) { |
| switch (Expansion.OperandMap[MIOpNo + i].Kind) { |
| case OpData::Operand: |
| o << " lowerOperand(MI->getOperand(" |
| << Expansion.OperandMap[MIOpNo + i].OpNo << "), MCOp);\n" |
| << " Inst.addOperand(MCOp);\n"; |
| break; |
| case OpData::Imm: |
| o << " Inst.addOperand(MCOperand::createImm(" |
| << Expansion.OperandMap[MIOpNo + i].ImmVal << "));\n"; |
| break; |
| case OpData::Reg: { |
| const Record *Reg = Expansion.OperandMap[MIOpNo + i].RegRec; |
| o << " Inst.addOperand(MCOperand::createReg("; |
| // "zero_reg" is special. |
| if (Reg->getName() == "zero_reg") |
| o << "0"; |
| else |
| o << Reg->getValueAsString("Namespace") << "::" << Reg->getName(); |
| o << "));\n"; |
| break; |
| } |
| } |
| } |
| } |
| if (Dest.Operands.isVariadic) { |
| unsigned LastOpNo = 0; |
| for (const auto &Op : Source.Operands) |
| LastOpNo += Op.MINumOperands; |
| o << " // variable_ops\n"; |
| o << " for (unsigned i = " << LastOpNo |
| << ", e = MI->getNumOperands(); i != e; ++i)\n" |
| << " if (lowerOperand(MI->getOperand(i), MCOp))\n" |
| << " Inst.addOperand(MCOp);\n"; |
| } |
| o << " break;\n" |
| << " }\n"; |
| } |
| o << " }\n return true;"; |
| } else { |
| o << " return false;"; |
| } |
| |
| o << "\n}\n\n"; |
| } |
| |
| void PseudoLoweringEmitter::run(raw_ostream &OS) { |
| StringRef Classes[] = {"PseudoInstExpansion", "Instruction"}; |
| |
| // Process the pseudo expansion definitions, validating them as we do so. |
| TGTimer &Timer = Records.getTimer(); |
| Timer.startTimer("Process definitions"); |
| for (const Record *Inst : Records.getAllDerivedDefinitions(Classes)) |
| evaluateExpansion(Inst); |
| |
| // Generate expansion code to lower the pseudo to an MCInst of the real |
| // instruction. |
| Timer.startTimer("Emit expansion code"); |
| emitLoweringEmitter(OS); |
| } |
| |
| static TableGen::Emitter::OptClass<PseudoLoweringEmitter> |
| X("gen-pseudo-lowering", "Generate pseudo instruction lowering"); |