10b57cec5SDimitry Andric //===- PseudoLoweringEmitter.cpp - PseudoLowering Generator -----*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
9*0fca6ea1SDimitry Andric #include "Common/CodeGenInstruction.h"
10*0fca6ea1SDimitry Andric #include "Common/CodeGenTarget.h"
110b57cec5SDimitry Andric #include "llvm/ADT/IndexedMap.h"
120b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
130b57cec5SDimitry Andric #include "llvm/ADT/StringMap.h"
140b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
150b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
160b57cec5SDimitry Andric #include "llvm/TableGen/Error.h"
170b57cec5SDimitry Andric #include "llvm/TableGen/Record.h"
180b57cec5SDimitry Andric #include "llvm/TableGen/TableGenBackend.h"
190b57cec5SDimitry Andric #include <vector>
200b57cec5SDimitry Andric using namespace llvm;
210b57cec5SDimitry Andric
220b57cec5SDimitry Andric #define DEBUG_TYPE "pseudo-lowering"
230b57cec5SDimitry Andric
240b57cec5SDimitry Andric namespace {
250b57cec5SDimitry Andric class PseudoLoweringEmitter {
260b57cec5SDimitry Andric struct OpData {
270b57cec5SDimitry Andric enum MapKind { Operand, Imm, Reg };
280b57cec5SDimitry Andric MapKind Kind;
290b57cec5SDimitry Andric union {
300b57cec5SDimitry Andric unsigned Operand; // Operand number mapped to.
310b57cec5SDimitry Andric uint64_t Imm; // Integer immedate value.
320b57cec5SDimitry Andric Record *Reg; // Physical register.
330b57cec5SDimitry Andric } Data;
340b57cec5SDimitry Andric };
350b57cec5SDimitry Andric struct PseudoExpansion {
360b57cec5SDimitry Andric CodeGenInstruction Source; // The source pseudo instruction definition.
370b57cec5SDimitry Andric CodeGenInstruction Dest; // The destination instruction to lower to.
380b57cec5SDimitry Andric IndexedMap<OpData> OperandMap;
390b57cec5SDimitry Andric
PseudoExpansion__anon45ff304c0111::PseudoLoweringEmitter::PseudoExpansion400b57cec5SDimitry Andric PseudoExpansion(CodeGenInstruction &s, CodeGenInstruction &d,
41*0fca6ea1SDimitry Andric IndexedMap<OpData> &m)
42*0fca6ea1SDimitry Andric : Source(s), Dest(d), OperandMap(m) {}
430b57cec5SDimitry Andric };
440b57cec5SDimitry Andric
450b57cec5SDimitry Andric RecordKeeper &Records;
460b57cec5SDimitry Andric
470b57cec5SDimitry Andric // It's overkill to have an instance of the full CodeGenTarget object,
480b57cec5SDimitry Andric // but it loads everything on demand, not in the constructor, so it's
490b57cec5SDimitry Andric // lightweight in performance, so it works out OK.
500b57cec5SDimitry Andric CodeGenTarget Target;
510b57cec5SDimitry Andric
520b57cec5SDimitry Andric SmallVector<PseudoExpansion, 64> Expansions;
530b57cec5SDimitry Andric
540b57cec5SDimitry Andric unsigned addDagOperandMapping(Record *Rec, DagInit *Dag,
550b57cec5SDimitry Andric CodeGenInstruction &Insn,
560b57cec5SDimitry Andric IndexedMap<OpData> &OperandMap,
570b57cec5SDimitry Andric unsigned BaseIdx);
580b57cec5SDimitry Andric void evaluateExpansion(Record *Pseudo);
590b57cec5SDimitry Andric void emitLoweringEmitter(raw_ostream &o);
60*0fca6ea1SDimitry Andric
610b57cec5SDimitry Andric public:
PseudoLoweringEmitter(RecordKeeper & R)620b57cec5SDimitry Andric PseudoLoweringEmitter(RecordKeeper &R) : Records(R), Target(R) {}
630b57cec5SDimitry Andric
640b57cec5SDimitry Andric /// run - Output the pseudo-lowerings.
650b57cec5SDimitry Andric void run(raw_ostream &o);
660b57cec5SDimitry Andric };
670b57cec5SDimitry Andric } // End anonymous namespace
680b57cec5SDimitry Andric
690b57cec5SDimitry Andric // FIXME: This pass currently can only expand a pseudo to a single instruction.
700b57cec5SDimitry Andric // The pseudo expansion really should take a list of dags, not just
710b57cec5SDimitry Andric // a single dag, so we can do fancier things.
720b57cec5SDimitry Andric
addDagOperandMapping(Record * Rec,DagInit * Dag,CodeGenInstruction & Insn,IndexedMap<OpData> & OperandMap,unsigned BaseIdx)73*0fca6ea1SDimitry Andric unsigned PseudoLoweringEmitter::addDagOperandMapping(
74*0fca6ea1SDimitry Andric Record *Rec, DagInit *Dag, CodeGenInstruction &Insn,
750b57cec5SDimitry Andric IndexedMap<OpData> &OperandMap, unsigned BaseIdx) {
760b57cec5SDimitry Andric unsigned OpsAdded = 0;
770b57cec5SDimitry Andric for (unsigned i = 0, e = Dag->getNumArgs(); i != e; ++i) {
780b57cec5SDimitry Andric if (DefInit *DI = dyn_cast<DefInit>(Dag->getArg(i))) {
790b57cec5SDimitry Andric // Physical register reference. Explicit check for the special case
800b57cec5SDimitry Andric // "zero_reg" definition.
810b57cec5SDimitry Andric if (DI->getDef()->isSubClassOf("Register") ||
820b57cec5SDimitry Andric DI->getDef()->getName() == "zero_reg") {
830b57cec5SDimitry Andric OperandMap[BaseIdx + i].Kind = OpData::Reg;
840b57cec5SDimitry Andric OperandMap[BaseIdx + i].Data.Reg = DI->getDef();
850b57cec5SDimitry Andric ++OpsAdded;
860b57cec5SDimitry Andric continue;
870b57cec5SDimitry Andric }
880b57cec5SDimitry Andric
890b57cec5SDimitry Andric // Normal operands should always have the same type, or we have a
900b57cec5SDimitry Andric // problem.
910b57cec5SDimitry Andric // FIXME: We probably shouldn't ever get a non-zero BaseIdx here.
920b57cec5SDimitry Andric assert(BaseIdx == 0 && "Named subargument in pseudo expansion?!");
93e8d8bef9SDimitry Andric // FIXME: Are the message operand types backward?
94e8d8bef9SDimitry Andric if (DI->getDef() != Insn.Operands[BaseIdx + i].Rec) {
95e8d8bef9SDimitry Andric PrintError(Rec, "In pseudo instruction '" + Rec->getName() +
96e8d8bef9SDimitry Andric "', operand type '" + DI->getDef()->getName() +
970b57cec5SDimitry Andric "' does not match expansion operand type '" +
980b57cec5SDimitry Andric Insn.Operands[BaseIdx + i].Rec->getName() + "'");
99e8d8bef9SDimitry Andric PrintFatalNote(DI->getDef(),
100e8d8bef9SDimitry Andric "Value was assigned at the following location:");
101e8d8bef9SDimitry Andric }
1020b57cec5SDimitry Andric // Source operand maps to destination operand. The Data element
1030b57cec5SDimitry Andric // will be filled in later, just set the Kind for now. Do it
1040b57cec5SDimitry Andric // for each corresponding MachineInstr operand, not just the first.
1050b57cec5SDimitry Andric for (unsigned I = 0, E = Insn.Operands[i].MINumOperands; I != E; ++I)
1060b57cec5SDimitry Andric OperandMap[BaseIdx + i + I].Kind = OpData::Operand;
1070b57cec5SDimitry Andric OpsAdded += Insn.Operands[i].MINumOperands;
1080b57cec5SDimitry Andric } else if (IntInit *II = dyn_cast<IntInit>(Dag->getArg(i))) {
1090b57cec5SDimitry Andric OperandMap[BaseIdx + i].Kind = OpData::Imm;
1100b57cec5SDimitry Andric OperandMap[BaseIdx + i].Data.Imm = II->getValue();
1110b57cec5SDimitry Andric ++OpsAdded;
112fe6060f1SDimitry Andric } else if (auto *BI = dyn_cast<BitsInit>(Dag->getArg(i))) {
11381ad6265SDimitry Andric auto *II =
11481ad6265SDimitry Andric cast<IntInit>(BI->convertInitializerTo(IntRecTy::get(Records)));
115fe6060f1SDimitry Andric OperandMap[BaseIdx + i].Kind = OpData::Imm;
116fe6060f1SDimitry Andric OperandMap[BaseIdx + i].Data.Imm = II->getValue();
117fe6060f1SDimitry Andric ++OpsAdded;
1180b57cec5SDimitry Andric } else if (DagInit *SubDag = dyn_cast<DagInit>(Dag->getArg(i))) {
1190b57cec5SDimitry Andric // Just add the operands recursively. This is almost certainly
1200b57cec5SDimitry Andric // a constant value for a complex operand (> 1 MI operand).
1210b57cec5SDimitry Andric unsigned NewOps =
1220b57cec5SDimitry Andric addDagOperandMapping(Rec, SubDag, Insn, OperandMap, BaseIdx + i);
1230b57cec5SDimitry Andric OpsAdded += NewOps;
1240b57cec5SDimitry Andric // Since we added more than one, we also need to adjust the base.
1250b57cec5SDimitry Andric BaseIdx += NewOps - 1;
1260b57cec5SDimitry Andric } else
1270b57cec5SDimitry Andric llvm_unreachable("Unhandled pseudo-expansion argument type!");
1280b57cec5SDimitry Andric }
1290b57cec5SDimitry Andric return OpsAdded;
1300b57cec5SDimitry Andric }
1310b57cec5SDimitry Andric
evaluateExpansion(Record * Rec)1320b57cec5SDimitry Andric void PseudoLoweringEmitter::evaluateExpansion(Record *Rec) {
1330b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Pseudo definition: " << Rec->getName() << "\n");
1340b57cec5SDimitry Andric
1350b57cec5SDimitry Andric // Validate that the result pattern has the corrent number and types
1360b57cec5SDimitry Andric // of arguments for the instruction it references.
1370b57cec5SDimitry Andric DagInit *Dag = Rec->getValueAsDag("ResultInst");
1380b57cec5SDimitry Andric assert(Dag && "Missing result instruction in pseudo expansion!");
1390b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << " Result: " << *Dag << "\n");
1400b57cec5SDimitry Andric
1410b57cec5SDimitry Andric DefInit *OpDef = dyn_cast<DefInit>(Dag->getOperator());
142e8d8bef9SDimitry Andric if (!OpDef) {
143e8d8bef9SDimitry Andric PrintError(Rec, "In pseudo instruction '" + Rec->getName() +
144e8d8bef9SDimitry Andric "', result operator is not a record");
145e8d8bef9SDimitry Andric PrintFatalNote(Rec->getValue("ResultInst"),
146e8d8bef9SDimitry Andric "Result was assigned at the following location:");
147e8d8bef9SDimitry Andric }
1480b57cec5SDimitry Andric Record *Operator = OpDef->getDef();
149e8d8bef9SDimitry Andric if (!Operator->isSubClassOf("Instruction")) {
150e8d8bef9SDimitry Andric PrintError(Rec, "In pseudo instruction '" + Rec->getName() +
151e8d8bef9SDimitry Andric "', result operator '" + Operator->getName() +
152e8d8bef9SDimitry Andric "' is not an instruction");
153e8d8bef9SDimitry Andric PrintFatalNote(Rec->getValue("ResultInst"),
154e8d8bef9SDimitry Andric "Result was assigned at the following location:");
155e8d8bef9SDimitry Andric }
1560b57cec5SDimitry Andric
1570b57cec5SDimitry Andric CodeGenInstruction Insn(Operator);
1580b57cec5SDimitry Andric
159e8d8bef9SDimitry Andric if (Insn.isCodeGenOnly || Insn.isPseudo) {
160e8d8bef9SDimitry Andric PrintError(Rec, "In pseudo instruction '" + Rec->getName() +
161e8d8bef9SDimitry Andric "', result operator '" + Operator->getName() +
162e8d8bef9SDimitry Andric "' cannot be a pseudo instruction");
163e8d8bef9SDimitry Andric PrintFatalNote(Rec->getValue("ResultInst"),
164e8d8bef9SDimitry Andric "Result was assigned at the following location:");
165e8d8bef9SDimitry Andric }
1660b57cec5SDimitry Andric
167e8d8bef9SDimitry Andric if (Insn.Operands.size() != Dag->getNumArgs()) {
168e8d8bef9SDimitry Andric PrintError(Rec, "In pseudo instruction '" + Rec->getName() +
169e8d8bef9SDimitry Andric "', result operator '" + Operator->getName() +
170e8d8bef9SDimitry Andric "' has the wrong number of operands");
171e8d8bef9SDimitry Andric PrintFatalNote(Rec->getValue("ResultInst"),
172e8d8bef9SDimitry Andric "Result was assigned at the following location:");
173e8d8bef9SDimitry Andric }
1740b57cec5SDimitry Andric
1750b57cec5SDimitry Andric unsigned NumMIOperands = 0;
1760b57cec5SDimitry Andric for (unsigned i = 0, e = Insn.Operands.size(); i != e; ++i)
1770b57cec5SDimitry Andric NumMIOperands += Insn.Operands[i].MINumOperands;
1780b57cec5SDimitry Andric IndexedMap<OpData> OperandMap;
1790b57cec5SDimitry Andric OperandMap.grow(NumMIOperands);
1800b57cec5SDimitry Andric
1810b57cec5SDimitry Andric addDagOperandMapping(Rec, Dag, Insn, OperandMap, 0);
1820b57cec5SDimitry Andric
1830b57cec5SDimitry Andric // If there are more operands that weren't in the DAG, they have to
1840b57cec5SDimitry Andric // be operands that have default values, or we have an error. Currently,
1850b57cec5SDimitry Andric // Operands that are a subclass of OperandWithDefaultOp have default values.
1860b57cec5SDimitry Andric
1870b57cec5SDimitry Andric // Validate that each result pattern argument has a matching (by name)
1880b57cec5SDimitry Andric // argument in the source instruction, in either the (outs) or (ins) list.
1890b57cec5SDimitry Andric // Also check that the type of the arguments match.
1900b57cec5SDimitry Andric //
1910b57cec5SDimitry Andric // Record the mapping of the source to result arguments for use by
1920b57cec5SDimitry Andric // the lowering emitter.
1930b57cec5SDimitry Andric CodeGenInstruction SourceInsn(Rec);
1940b57cec5SDimitry Andric StringMap<unsigned> SourceOperands;
1950b57cec5SDimitry Andric for (unsigned i = 0, e = SourceInsn.Operands.size(); i != e; ++i)
1960b57cec5SDimitry Andric SourceOperands[SourceInsn.Operands[i].Name] = i;
1970b57cec5SDimitry Andric
1980b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << " Operand mapping:\n");
1990b57cec5SDimitry Andric for (unsigned i = 0, e = Insn.Operands.size(); i != e; ++i) {
2000b57cec5SDimitry Andric // We've already handled constant values. Just map instruction operands
2010b57cec5SDimitry Andric // here.
2020b57cec5SDimitry Andric if (OperandMap[Insn.Operands[i].MIOperandNo].Kind != OpData::Operand)
2030b57cec5SDimitry Andric continue;
2040b57cec5SDimitry Andric StringMap<unsigned>::iterator SourceOp =
2050b57cec5SDimitry Andric SourceOperands.find(Dag->getArgNameStr(i));
206e8d8bef9SDimitry Andric if (SourceOp == SourceOperands.end()) {
207e8d8bef9SDimitry Andric PrintError(Rec, "In pseudo instruction '" + Rec->getName() +
208e8d8bef9SDimitry Andric "', output operand '" + Dag->getArgNameStr(i) +
209e8d8bef9SDimitry Andric "' has no matching source operand");
210e8d8bef9SDimitry Andric PrintFatalNote(Rec->getValue("ResultInst"),
211e8d8bef9SDimitry Andric "Value was assigned at the following location:");
212e8d8bef9SDimitry Andric }
2130b57cec5SDimitry Andric // Map the source operand to the destination operand index for each
2140b57cec5SDimitry Andric // MachineInstr operand.
2150b57cec5SDimitry Andric for (unsigned I = 0, E = Insn.Operands[i].MINumOperands; I != E; ++I)
2160b57cec5SDimitry Andric OperandMap[Insn.Operands[i].MIOperandNo + I].Data.Operand =
2170b57cec5SDimitry Andric SourceOp->getValue();
2180b57cec5SDimitry Andric
2190b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << " " << SourceOp->getValue() << " ==> " << i
2200b57cec5SDimitry Andric << "\n");
2210b57cec5SDimitry Andric }
2220b57cec5SDimitry Andric
2230b57cec5SDimitry Andric Expansions.push_back(PseudoExpansion(SourceInsn, Insn, OperandMap));
2240b57cec5SDimitry Andric }
2250b57cec5SDimitry Andric
emitLoweringEmitter(raw_ostream & o)2260b57cec5SDimitry Andric void PseudoLoweringEmitter::emitLoweringEmitter(raw_ostream &o) {
2270b57cec5SDimitry Andric // Emit file header.
2280b57cec5SDimitry Andric emitSourceFileHeader("Pseudo-instruction MC lowering Source Fragment", o);
2290b57cec5SDimitry Andric
230*0fca6ea1SDimitry Andric o << "bool " << Target.getName() + "AsmPrinter"
231*0fca6ea1SDimitry Andric << "::\n"
2320b57cec5SDimitry Andric << "emitPseudoExpansionLowering(MCStreamer &OutStreamer,\n"
2330b57cec5SDimitry Andric << " const MachineInstr *MI) {\n";
2340b57cec5SDimitry Andric
2350b57cec5SDimitry Andric if (!Expansions.empty()) {
2360b57cec5SDimitry Andric o << " switch (MI->getOpcode()) {\n"
2370b57cec5SDimitry Andric << " default: return false;\n";
2380b57cec5SDimitry Andric for (auto &Expansion : Expansions) {
2390b57cec5SDimitry Andric CodeGenInstruction &Source = Expansion.Source;
2400b57cec5SDimitry Andric CodeGenInstruction &Dest = Expansion.Dest;
241*0fca6ea1SDimitry Andric o << " case " << Source.Namespace << "::" << Source.TheDef->getName()
242*0fca6ea1SDimitry Andric << ": {\n"
2430b57cec5SDimitry Andric << " MCInst TmpInst;\n"
2440b57cec5SDimitry Andric << " MCOperand MCOp;\n"
245*0fca6ea1SDimitry Andric << " TmpInst.setOpcode(" << Dest.Namespace
246*0fca6ea1SDimitry Andric << "::" << Dest.TheDef->getName() << ");\n";
2470b57cec5SDimitry Andric
2480b57cec5SDimitry Andric // Copy the operands from the source instruction.
2490b57cec5SDimitry Andric // FIXME: Instruction operands with defaults values (predicates and cc_out
2500b57cec5SDimitry Andric // in ARM, for example shouldn't need explicit values in the
2510b57cec5SDimitry Andric // expansion DAG.
2520b57cec5SDimitry Andric unsigned MIOpNo = 0;
2530b57cec5SDimitry Andric for (const auto &DestOperand : Dest.Operands) {
2540b57cec5SDimitry Andric o << " // Operand: " << DestOperand.Name << "\n";
2550b57cec5SDimitry Andric for (unsigned i = 0, e = DestOperand.MINumOperands; i != e; ++i) {
2560b57cec5SDimitry Andric switch (Expansion.OperandMap[MIOpNo + i].Kind) {
2570b57cec5SDimitry Andric case OpData::Operand:
2580b57cec5SDimitry Andric o << " lowerOperand(MI->getOperand("
259*0fca6ea1SDimitry Andric << Source.Operands[Expansion.OperandMap[MIOpNo].Data.Operand]
260*0fca6ea1SDimitry Andric .MIOperandNo +
261*0fca6ea1SDimitry Andric i
2620b57cec5SDimitry Andric << "), MCOp);\n"
2630b57cec5SDimitry Andric << " TmpInst.addOperand(MCOp);\n";
2640b57cec5SDimitry Andric break;
2650b57cec5SDimitry Andric case OpData::Imm:
2660b57cec5SDimitry Andric o << " TmpInst.addOperand(MCOperand::createImm("
2670b57cec5SDimitry Andric << Expansion.OperandMap[MIOpNo + i].Data.Imm << "));\n";
2680b57cec5SDimitry Andric break;
2690b57cec5SDimitry Andric case OpData::Reg: {
2700b57cec5SDimitry Andric Record *Reg = Expansion.OperandMap[MIOpNo + i].Data.Reg;
2710b57cec5SDimitry Andric o << " TmpInst.addOperand(MCOperand::createReg(";
2720b57cec5SDimitry Andric // "zero_reg" is special.
2730b57cec5SDimitry Andric if (Reg->getName() == "zero_reg")
2740b57cec5SDimitry Andric o << "0";
2750b57cec5SDimitry Andric else
276*0fca6ea1SDimitry Andric o << Reg->getValueAsString("Namespace") << "::" << Reg->getName();
2770b57cec5SDimitry Andric o << "));\n";
2780b57cec5SDimitry Andric break;
2790b57cec5SDimitry Andric }
2800b57cec5SDimitry Andric }
2810b57cec5SDimitry Andric }
2820b57cec5SDimitry Andric MIOpNo += DestOperand.MINumOperands;
2830b57cec5SDimitry Andric }
2840b57cec5SDimitry Andric if (Dest.Operands.isVariadic) {
2850b57cec5SDimitry Andric MIOpNo = Source.Operands.size() + 1;
2860b57cec5SDimitry Andric o << " // variable_ops\n";
2870b57cec5SDimitry Andric o << " for (unsigned i = " << MIOpNo
2880b57cec5SDimitry Andric << ", e = MI->getNumOperands(); i != e; ++i)\n"
2890b57cec5SDimitry Andric << " if (lowerOperand(MI->getOperand(i), MCOp))\n"
2900b57cec5SDimitry Andric << " TmpInst.addOperand(MCOp);\n";
2910b57cec5SDimitry Andric }
2920b57cec5SDimitry Andric o << " EmitToStreamer(OutStreamer, TmpInst);\n"
2930b57cec5SDimitry Andric << " break;\n"
2940b57cec5SDimitry Andric << " }\n";
2950b57cec5SDimitry Andric }
2960b57cec5SDimitry Andric o << " }\n return true;";
2970b57cec5SDimitry Andric } else
2980b57cec5SDimitry Andric o << " return false;";
2990b57cec5SDimitry Andric
3000b57cec5SDimitry Andric o << "\n}\n\n";
3010b57cec5SDimitry Andric }
3020b57cec5SDimitry Andric
run(raw_ostream & o)3030b57cec5SDimitry Andric void PseudoLoweringEmitter::run(raw_ostream &o) {
304e8d8bef9SDimitry Andric StringRef Classes[] = {"PseudoInstExpansion", "Instruction"};
305bdd1243dSDimitry Andric std::vector<Record *> Insts = Records.getAllDerivedDefinitions(Classes);
3060b57cec5SDimitry Andric
3070b57cec5SDimitry Andric // Process the pseudo expansion definitions, validating them as we do so.
308e8d8bef9SDimitry Andric Records.startTimer("Process definitions");
3090b57cec5SDimitry Andric for (unsigned i = 0, e = Insts.size(); i != e; ++i)
3100b57cec5SDimitry Andric evaluateExpansion(Insts[i]);
3110b57cec5SDimitry Andric
3120b57cec5SDimitry Andric // Generate expansion code to lower the pseudo to an MCInst of the real
3130b57cec5SDimitry Andric // instruction.
314e8d8bef9SDimitry Andric Records.startTimer("Emit expansion code");
3150b57cec5SDimitry Andric emitLoweringEmitter(o);
3160b57cec5SDimitry Andric }
3170b57cec5SDimitry Andric
31806c3fb27SDimitry Andric static TableGen::Emitter::OptClass<PseudoLoweringEmitter>
31906c3fb27SDimitry Andric X("gen-pseudo-lowering", "Generate pseudo instruction lowering");
320