| //===-------- CompressInstEmitter.cpp - Generator for Compression ---------===// | |
| // | |
| // 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 | |
| // | |
| // CompressInstEmitter implements a tablegen-driven CompressPat based | |
| // Instruction Compression mechanism. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // CompressInstEmitter implements a tablegen-driven CompressPat Instruction | |
| // Compression mechanism for generating compressed instructions from the | |
| // expanded instruction form. | |
| // This tablegen backend processes CompressPat declarations in a | |
| // td file and generates all the required checks to validate the pattern | |
| // declarations; validate the input and output operands to generate the correct | |
| // compressed instructions. The checks include validating different types of | |
| // operands; register operands, immediate operands, fixed register and fixed | |
| // immediate inputs. | |
| // | |
| // Example: | |
| // /// Defines a Pat match between compressed and uncompressed instruction. | |
| // /// The relationship and helper function generation are handled by | |
| // /// CompressInstEmitter backend. | |
| // class CompressPat<dag input, dag output, list<Predicate> predicates = []> { | |
| // /// Uncompressed instruction description. | |
| // dag Input = input; | |
| // /// Compressed instruction description. | |
| // dag Output = output; | |
| // /// Predicates that must be true for this to match. | |
| // list<Predicate> Predicates = predicates; | |
| // /// Duplicate match when tied operand is just different. | |
| // bit isCompressOnly = false; | |
| // } | |
| // | |
| // let Predicates = [HasStdExtZca] in { | |
| // def : CompressPat<(ADD GPRNoX0:$rs1, GPRNoX0:$rs1, GPRNoX0:$rs2), | |
| // (C_ADD GPRNoX0:$rs1, GPRNoX0:$rs2)>; | |
| // } | |
| // | |
| // The <TargetName>GenCompressInstEmitter.inc is an auto-generated header | |
| // file which exports two functions for compressing/uncompressing MCInst | |
| // instructions, plus some helper functions: | |
| // | |
| // bool compressInst(MCInst &OutInst, const MCInst &MI, | |
| // const MCSubtargetInfo &STI); | |
| // | |
| // bool uncompressInst(MCInst &OutInst, const MCInst &MI, | |
| // const MCSubtargetInfo &STI); | |
| // | |
| // In addition, it exports a function that returns the compressed instruction | |
| // size, or zero when the instruction is not compressible: | |
| // | |
| // unsigned getCompressedSize(const MachineInstr &MI, | |
| // const <TargetName>Subtarget &STI); | |
| // | |
| // | |
| // The clients that include this auto-generated header file and | |
| // invoke these functions can compress an instruction before emitting | |
| // it in the target-specific ASM or ELF streamer or can uncompress | |
| // an instruction before printing it when the expanded instruction | |
| // format aliases is favored. | |
| //===----------------------------------------------------------------------===// | |
| #include "Common/CodeGenHwModes.h" | |
| #include "Common/CodeGenInstruction.h" | |
| #include "Common/CodeGenRegisters.h" | |
| #include "Common/CodeGenTarget.h" | |
| #include "Common/InfoByHwMode.h" | |
| #include "Common/SubtargetFeatureInfo.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/CodeGenHelpers.h" | |
| #include "llvm/TableGen/Error.h" | |
| #include "llvm/TableGen/Record.h" | |
| #include "llvm/TableGen/TableGenBackend.h" | |
| #include <limits> | |
| #include <set> | |
| #include <vector> | |
| using namespace llvm; | |
| #define DEBUG_TYPE "compress-inst-emitter" | |
| namespace { | |
| class CompressInstEmitter { | |
| struct OpData { | |
| enum MapKind { Operand, Imm, Reg } Kind; | |
| // Info for an operand. | |
| struct OpndInfo { | |
| // Record from the Dag. | |
| const Record *DagRec; | |
| // Operand number mapped to. | |
| unsigned Idx; | |
| // Tied operand index within the instruction. | |
| int TiedOpIdx; | |
| }; | |
| union { | |
| OpndInfo OpInfo; | |
| // Integer immediate value. | |
| int64_t ImmVal; | |
| // Physical register. | |
| const Record *RegRec; | |
| }; | |
| }; | |
| struct ArgData { | |
| unsigned DAGOpNo; | |
| unsigned MIOpNo; | |
| }; | |
| struct CompressPat { | |
| // The source instruction definition. | |
| CodeGenInstruction Source; | |
| // The destination instruction to transform to. | |
| CodeGenInstruction Dest; | |
| // Required target features to enable pattern. | |
| std::vector<const Record *> PatReqFeatures; | |
| // Maps operands in the Source Instruction to | |
| // the corresponding Dest instruction operand. | |
| IndexedMap<OpData> SourceOperandMap; | |
| // Maps operands in the Dest Instruction | |
| // to the corresponding Source instruction operand. | |
| IndexedMap<OpData> DestOperandMap; | |
| bool IsCompressOnly; | |
| CompressPat(const CodeGenInstruction &S, const CodeGenInstruction &D, | |
| std::vector<const Record *> RF, | |
| const IndexedMap<OpData> &SourceMap, | |
| const IndexedMap<OpData> &DestMap, bool IsCompressOnly) | |
| : Source(S), Dest(D), PatReqFeatures(std::move(RF)), | |
| SourceOperandMap(SourceMap), DestOperandMap(DestMap), | |
| IsCompressOnly(IsCompressOnly) {} | |
| }; | |
| enum EmitterType { Compress, Uncompress, CheckCompress }; | |
| const RecordKeeper &Records; | |
| const CodeGenTarget Target; | |
| std::vector<CompressPat> CompressPatterns; | |
| void addDagOperandMapping(const Record *Rec, const DagInit *Dag, | |
| const CodeGenInstruction &Inst, | |
| IndexedMap<OpData> &OperandMap, | |
| StringMap<ArgData> &Operands, bool IsSourceInst, | |
| ArrayRef<const Record *> PatPreds); | |
| void evaluateCompressPat(const Record *Compress); | |
| void emitCompressInstEmitter(raw_ostream &OS, EmitterType EType); | |
| bool validateTypes(const Record *DagOpType, const Record *InstOpType, | |
| bool IsSourceInst, ArrayRef<const Record *> PatPreds); | |
| bool validateRegister(const Record *Reg, const Record *RegClass, | |
| ArrayRef<SMLoc> Loc, ArrayRef<const Record *> PatPreds); | |
| void checkDagOperandMapping(const Record *Rec, | |
| const StringMap<ArgData> &DestOperands, | |
| const DagInit *SourceDag, const DagInit *DestDag); | |
| void createInstOperandMapping(const Record *Rec, const DagInit *SourceDag, | |
| const DagInit *DestDag, | |
| IndexedMap<OpData> &SourceOperandMap, | |
| IndexedMap<OpData> &DestOperandMap, | |
| StringMap<ArgData> &SourceOperands, | |
| const CodeGenInstruction &DestInst); | |
| public: | |
| CompressInstEmitter(const RecordKeeper &R) : Records(R), Target(R) {} | |
| void run(raw_ostream &OS); | |
| }; | |
| } // End anonymous namespace. | |
| bool CompressInstEmitter::validateRegister(const Record *Reg, | |
| const Record *RegClass, | |
| ArrayRef<SMLoc> Loc, | |
| ArrayRef<const Record *> PatPreds) { | |
| assert((Reg->isSubClassOf("Register") || | |
| Reg->isSubClassOf("RegisterByHwMode")) && | |
| "Reg record should be a Register"); | |
| assert(RegClass->isSubClassOf("RegisterClassLike") && | |
| "RegClass record should be RegisterClassLike"); | |
| if (const Record *RC = | |
| Target.getHwModes().resolveModeSelect(RegClass, PatPreds)) | |
| RegClass = RC; | |
| if (const Record *R = Target.getHwModes().resolveModeSelect(Reg, PatPreds)) | |
| Reg = R; | |
| return Target.getRegBank().regClassContainsReg(RegClass, Reg, Loc); | |
| } | |
| bool CompressInstEmitter::validateTypes(const Record *DagOpType, | |
| const Record *InstOpType, | |
| bool IsSourceInst, | |
| ArrayRef<const Record *> PatPreds) { | |
| if (DagOpType == InstOpType) | |
| return true; | |
| const Record *ResolvedDag = | |
| Target.getHwModes().resolveModeSelect(DagOpType, PatPreds); | |
| const Record *ResolvedInst = | |
| Target.getHwModes().resolveModeSelect(InstOpType, PatPreds); | |
| if (ResolvedDag && ResolvedInst) { | |
| DagOpType = ResolvedDag; | |
| InstOpType = ResolvedInst; | |
| } else if (DagOpType->isSubClassOf("RegClassByHwMode") && | |
| InstOpType->isSubClassOf("RegClassByHwMode")) { | |
| RegClassByHwMode DagRC(DagOpType, Target.getRegBank()); | |
| RegClassByHwMode InstRC(InstOpType, Target.getRegBank()); | |
| const CodeGenHwModes &CGH = Target.getHwModes(); | |
| HwModePredicates PatPredsSet(PatPreds); | |
| bool HasCompatibleMode = false; | |
| for (const auto &[Mode, DagClass] : DagRC) { | |
| if (CGH.getModePredicates(Mode).conflictsWith(PatPredsSet)) | |
| continue; | |
| if (!InstRC.hasMode(Mode)) | |
| return false; | |
| const CodeGenRegisterClass *InstClass = InstRC.get(Mode); | |
| if (!InstClass->hasSubClass(DagClass)) | |
| return false; | |
| HasCompatibleMode = true; | |
| } | |
| for (const auto &[Mode, InstClass] : InstRC) { | |
| if (CGH.getModePredicates(Mode).conflictsWith(PatPredsSet)) | |
| continue; | |
| if (!DagRC.hasMode(Mode)) | |
| return false; | |
| } | |
| return HasCompatibleMode; | |
| } else if (DagOpType->isSubClassOf("HwModeSelect") || | |
| InstOpType->isSubClassOf("HwModeSelect")) { | |
| return false; | |
| } | |
| // Concrete check (when resolved uniquely). | |
| if (DagOpType == InstOpType) | |
| return true; | |
| if (DagOpType->isSubClassOf("RegisterClass") && | |
| InstOpType->isSubClassOf("RegisterClass")) { | |
| const CodeGenRegisterClass &RC = Target.getRegisterClass(InstOpType); | |
| const CodeGenRegisterClass &SubRC = Target.getRegisterClass(DagOpType); | |
| return RC.hasSubClass(&SubRC); | |
| } | |
| // At this point either or both types are not registers, reject the pattern. | |
| if (DagOpType->isSubClassOf("RegisterClass") || | |
| InstOpType->isSubClassOf("RegisterClass")) | |
| return false; | |
| // Let further validation happen when compress()/uncompress() functions are | |
| // invoked. | |
| LLVM_DEBUG(dbgs() << (IsSourceInst ? "Input" : "Output") | |
| << " Dag Operand Type: '" << DagOpType->getName() | |
| << "' and " | |
| << "Instruction Operand Type: '" << InstOpType->getName() | |
| << "' can't be checked at pattern validation time!\n"); | |
| return true; | |
| } | |
| static bool validateArgsTypes(const Init *Arg1, const Init *Arg2) { | |
| return cast<DefInit>(Arg1)->getDef() == cast<DefInit>(Arg2)->getDef(); | |
| } | |
| /// The patterns in the Dag contain different types of operands: | |
| /// Register operands, e.g.: GPRC:$rs1; Fixed registers, e.g: X1; Immediate | |
| /// operands, e.g.: simm6:$imm; Fixed immediate operands, e.g.: 0. This function | |
| /// maps Dag operands to its corresponding instruction operands. For register | |
| /// operands and fixed registers it expects the Dag operand type to be contained | |
| /// in the instantiated instruction operand type. For immediate operands and | |
| /// immediates no validation checks are enforced at pattern validation time. | |
| void CompressInstEmitter::addDagOperandMapping( | |
| const Record *Rec, const DagInit *Dag, const CodeGenInstruction &Inst, | |
| IndexedMap<OpData> &OperandMap, StringMap<ArgData> &Operands, | |
| bool IsSourceInst, ArrayRef<const Record *> PatPreds) { | |
| unsigned NumMIOperands = 0; | |
| if (!Inst.Operands.empty()) | |
| NumMIOperands = | |
| Inst.Operands.back().MIOperandNo + Inst.Operands.back().MINumOperands; | |
| OperandMap.grow(NumMIOperands); | |
| // Tied operands are not represented in the DAG so we count them separately. | |
| unsigned DAGOpNo = 0; | |
| unsigned OpNo = 0; | |
| for (const auto &Opnd : Inst.Operands) { | |
| int TiedOpIdx = Opnd.getTiedRegister(); | |
| if (-1 != TiedOpIdx) { | |
| assert((unsigned)TiedOpIdx < OpNo); | |
| // Set the entry in OperandMap for the tied operand we're skipping. | |
| OperandMap[OpNo] = OperandMap[TiedOpIdx]; | |
| ++OpNo; | |
| // Source instructions can have at most 1 tied operand. | |
| if (IsSourceInst && (OpNo - DAGOpNo > 1)) | |
| PrintFatalError(Rec->getLoc(), | |
| "Input operands for Inst '" + Inst.getName() + | |
| "' and input Dag operand count mismatch"); | |
| continue; | |
| } | |
| for (unsigned SubOp = 0; SubOp != Opnd.MINumOperands; | |
| ++SubOp, ++OpNo, ++DAGOpNo) { | |
| const Record *OpndRec = Opnd.Rec; | |
| if (Opnd.MINumOperands > 1) | |
| OpndRec = cast<DefInit>(Opnd.MIOperandInfo->getArg(SubOp))->getDef(); | |
| if (DAGOpNo >= Dag->getNumArgs()) | |
| PrintFatalError(Rec->getLoc(), "Inst '" + Inst.getName() + | |
| "' and Dag operand count mismatch"); | |
| if (const auto *DI = dyn_cast<DefInit>(Dag->getArg(DAGOpNo))) { | |
| if (DI->getDef()->isSubClassOf("Register") || | |
| DI->getDef()->isSubClassOf("RegisterByHwMode")) { | |
| // Check if the fixed register belongs to the Register class. | |
| if (!validateRegister(DI->getDef(), OpndRec, Rec->getLoc(), PatPreds)) | |
| PrintFatalError(Rec->getLoc(), | |
| "Error in Dag '" + Dag->getAsString() + | |
| "': Register '" + DI->getDef()->getName() + | |
| "' is not in register class '" + | |
| OpndRec->getName() + "'"); | |
| OperandMap[OpNo].Kind = OpData::Reg; | |
| OperandMap[OpNo].RegRec = DI->getDef(); | |
| continue; | |
| } | |
| // Validate that Dag operand type matches the type defined in the | |
| // corresponding instruction. Operands in the input and output Dag | |
| // patterns are allowed to be a subclass of the type specified in the | |
| // corresponding instruction operand instead of being an exact match. | |
| if (!validateTypes(DI->getDef(), OpndRec, IsSourceInst, PatPreds)) | |
| PrintFatalError(Rec->getLoc(), | |
| "Error in Dag '" + Dag->getAsString() + | |
| "'. Operand '" + Dag->getArgNameStr(DAGOpNo) + | |
| "' has type '" + DI->getDef()->getName() + | |
| "' which does not match the type '" + | |
| OpndRec->getName() + | |
| "' in the corresponding instruction operand!"); | |
| OperandMap[OpNo].Kind = OpData::Operand; | |
| OperandMap[OpNo].OpInfo.DagRec = DI->getDef(); | |
| OperandMap[OpNo].OpInfo.TiedOpIdx = -1; | |
| // Create a mapping between the operand name in the Dag (e.g. $rs1) and | |
| // its index in the list of Dag operands and check that operands with | |
| // the same name have the same type. For example in 'C_ADD $rs1, $rs2' | |
| // we generate the mapping $rs1 --> 0, $rs2 ---> 1. If the operand | |
| // appears twice in the same Dag (tied in the compressed instruction), | |
| // we note the previous index in the TiedOpIdx field. | |
| StringRef ArgName = Dag->getArgNameStr(DAGOpNo); | |
| if (ArgName.empty()) | |
| continue; | |
| if (IsSourceInst) { | |
| auto It = Operands.find(ArgName); | |
| if (It != Operands.end()) { | |
| OperandMap[OpNo].OpInfo.TiedOpIdx = It->getValue().MIOpNo; | |
| if (OperandMap[It->getValue().MIOpNo].OpInfo.DagRec != DI->getDef()) | |
| PrintFatalError(Rec->getLoc(), | |
| "Input Operand '" + ArgName + | |
| "' has a mismatched tied operand!"); | |
| } | |
| } | |
| Operands[ArgName] = {DAGOpNo, OpNo}; | |
| } else if (const auto *II = dyn_cast<IntInit>(Dag->getArg(DAGOpNo))) { | |
| // Validate that corresponding instruction operand expects an immediate. | |
| if (!OpndRec->isSubClassOf("Operand")) | |
| PrintFatalError(Rec->getLoc(), "Error in Dag '" + Dag->getAsString() + | |
| "' Found immediate: '" + | |
| II->getAsString() + | |
| "' but corresponding instruction " | |
| "operand expected a register!"); | |
| // No pattern validation check possible for values of fixed immediate. | |
| OperandMap[OpNo].Kind = OpData::Imm; | |
| OperandMap[OpNo].ImmVal = II->getValue(); | |
| LLVM_DEBUG( | |
| dbgs() << " Found immediate '" << II->getValue() << "' at " | |
| << (IsSourceInst ? "input " : "output ") | |
| << "Dag. No validation time check possible for values of " | |
| "fixed immediate.\n"); | |
| } else { | |
| llvm_unreachable("Unhandled CompressPat argument type!"); | |
| } | |
| } | |
| } | |
| // We shouldn't have extra Dag operands. | |
| if (DAGOpNo != Dag->getNumArgs()) | |
| PrintFatalError(Rec->getLoc(), "Inst '" + Inst.getName() + | |
| "' and Dag operand count mismatch"); | |
| } | |
| // Check that all names in the source DAG appear in the destionation DAG. | |
| void CompressInstEmitter::checkDagOperandMapping( | |
| const Record *Rec, const StringMap<ArgData> &DestOperands, | |
| const DagInit *SourceDag, const DagInit *DestDag) { | |
| for (unsigned I = 0; I < SourceDag->getNumArgs(); ++I) { | |
| // Skip fixed immediates and registers, they were handled in | |
| // addDagOperandMapping. | |
| StringRef ArgName = SourceDag->getArgNameStr(I); | |
| if (ArgName.empty()) | |
| continue; | |
| auto It = DestOperands.find(ArgName); | |
| if (It == DestOperands.end()) | |
| PrintFatalError(Rec->getLoc(), "Operand " + ArgName + | |
| " defined in Input Dag but not used in" | |
| " Output Dag!"); | |
| // Input Dag operand types must match output Dag operand type. | |
| if (!validateArgsTypes(DestDag->getArg(It->getValue().DAGOpNo), | |
| SourceDag->getArg(I))) | |
| PrintFatalError(Rec->getLoc(), "Type mismatch between Input and " | |
| "Output Dag operand '" + | |
| ArgName + "'!"); | |
| } | |
| } | |
| /// Map operand names in the Dag to their index in both corresponding input and | |
| /// output instructions. Validate that operands defined in the input are | |
| /// used in the output pattern while populating the maps. | |
| void CompressInstEmitter::createInstOperandMapping( | |
| const Record *Rec, const DagInit *SourceDag, const DagInit *DestDag, | |
| IndexedMap<OpData> &SourceOperandMap, IndexedMap<OpData> &DestOperandMap, | |
| StringMap<ArgData> &SourceOperands, const CodeGenInstruction &DestInst) { | |
| // TiedCount keeps track of the number of operands skipped in Inst | |
| // operands list to get to the corresponding Dag operand. | |
| unsigned TiedCount = 0; | |
| LLVM_DEBUG(dbgs() << " Operand mapping:\n Source Dest\n"); | |
| unsigned OpNo = 0; | |
| for (const auto &Operand : DestInst.Operands) { | |
| int TiedInstOpIdx = Operand.getTiedRegister(); | |
| if (TiedInstOpIdx != -1) { | |
| ++TiedCount; | |
| assert((unsigned)TiedInstOpIdx < OpNo); | |
| DestOperandMap[OpNo] = DestOperandMap[TiedInstOpIdx]; | |
| if (DestOperandMap[OpNo].Kind == OpData::Operand) | |
| // No need to fill the SourceOperandMap here since it was mapped to | |
| // destination operand 'TiedInstOpIdx' in a previous iteration. | |
| LLVM_DEBUG(dbgs() << " " << DestOperandMap[OpNo].OpInfo.Idx | |
| << " ====> " << OpNo | |
| << " Dest operand tied with operand '" | |
| << TiedInstOpIdx << "'\n"); | |
| ++OpNo; | |
| continue; | |
| } | |
| for (unsigned SubOp = 0; SubOp != Operand.MINumOperands; ++SubOp, ++OpNo) { | |
| // Skip fixed immediates and registers, they were handled in | |
| // addDagOperandMapping. | |
| if (DestOperandMap[OpNo].Kind != OpData::Operand) | |
| continue; | |
| unsigned DagArgIdx = OpNo - TiedCount; | |
| StringRef ArgName = DestDag->getArgNameStr(DagArgIdx); | |
| auto SourceOp = SourceOperands.find(ArgName); | |
| if (SourceOp == SourceOperands.end()) | |
| PrintFatalError(Rec->getLoc(), | |
| "Output Dag operand '" + ArgName + | |
| "' has no matching input Dag operand."); | |
| assert(ArgName == | |
| SourceDag->getArgNameStr(SourceOp->getValue().DAGOpNo) && | |
| "Incorrect operand mapping detected!\n"); | |
| unsigned SourceOpNo = SourceOp->getValue().MIOpNo; | |
| DestOperandMap[OpNo].OpInfo.Idx = SourceOpNo; | |
| SourceOperandMap[SourceOpNo].OpInfo.Idx = OpNo; | |
| LLVM_DEBUG(dbgs() << " " << SourceOpNo << " ====> " << OpNo << "\n"); | |
| } | |
| } | |
| } | |
| /// Validates the CompressPattern and create operand mapping. | |
| /// These are the checks to validate a CompressPat pattern declarations. | |
| /// Error out with message under these conditions: | |
| /// - Dag Input opcode is an expanded instruction and Dag Output opcode is a | |
| /// compressed instruction. | |
| /// - Operands in Dag Input must be all used in Dag Output. | |
| /// Register Operand type in Dag Input Type must be contained in the | |
| /// corresponding Source Instruction type. | |
| /// - Register Operand type in Dag Input must be the same as in Dag Ouput. | |
| /// - Register Operand type in Dag Output must be the same as the | |
| /// corresponding Destination Inst type. | |
| /// - Immediate Operand type in Dag Input must be the same as in Dag Ouput. | |
| /// - Immediate Operand type in Dag Ouput must be the same as the corresponding | |
| /// Destination Instruction type. | |
| /// - Fixed register must be contained in the corresponding Source Instruction | |
| /// type. | |
| /// - Fixed register must be contained in the corresponding Destination | |
| /// Instruction type. | |
| /// Warning message printed under these conditions: | |
| /// - Fixed immediate in Dag Input or Dag Ouput cannot be checked at this time | |
| /// and generate warning. | |
| /// - Immediate operand type in Dag Input differs from the corresponding Source | |
| /// Instruction type and generate a warning. | |
| void CompressInstEmitter::evaluateCompressPat(const Record *Rec) { | |
| // Validate input Dag operands. | |
| const DagInit *SourceDag = Rec->getValueAsDag("Input"); | |
| assert(SourceDag && "Missing 'Input' in compress pattern!"); | |
| LLVM_DEBUG(dbgs() << "Input: " << *SourceDag << "\n"); | |
| // Checking we are transforming from compressed to uncompressed instructions. | |
| const Record *SourceOperator = SourceDag->getOperatorAsDef(Rec->getLoc()); | |
| CodeGenInstruction SourceInst(SourceOperator); | |
| // Validate output Dag operands. | |
| const DagInit *DestDag = Rec->getValueAsDag("Output"); | |
| assert(DestDag && "Missing 'Output' in compress pattern!"); | |
| LLVM_DEBUG(dbgs() << "Output: " << *DestDag << "\n"); | |
| const Record *DestOperator = DestDag->getOperatorAsDef(Rec->getLoc()); | |
| CodeGenInstruction DestInst(DestOperator); | |
| if (SourceOperator->getValueAsInt("Size") <= | |
| DestOperator->getValueAsInt("Size")) | |
| PrintFatalError( | |
| Rec->getLoc(), | |
| "Compressed instruction '" + DestOperator->getName() + | |
| "'is not strictly smaller than the uncompressed instruction '" + | |
| SourceOperator->getName() + "' !"); | |
| // Get the target features for the CompressPat. | |
| std::vector<const Record *> PatReqFeatures; | |
| std::vector<const Record *> RF = Rec->getValueAsListOfDefs("Predicates"); | |
| copy_if(RF, std::back_inserter(PatReqFeatures), [](const Record *R) { | |
| return R->getValueAsBit("AssemblerMatcherPredicate"); | |
| }); | |
| // Fill the mapping from the source to destination instructions. | |
| IndexedMap<OpData> SourceOperandMap; | |
| // Map from arg name to DAG operand number and MI operand number. | |
| StringMap<ArgData> SourceOperands; | |
| // Create a mapping between source Dag operands and source Inst operands. | |
| addDagOperandMapping(Rec, SourceDag, SourceInst, SourceOperandMap, | |
| SourceOperands, /*IsSourceInst*/ true, PatReqFeatures); | |
| IndexedMap<OpData> DestOperandMap; | |
| // Map from arg name to DAG operand number and MI operand number. | |
| StringMap<ArgData> DestOperands; | |
| // Create a mapping between destination Dag operands and destination Inst | |
| // operands. | |
| addDagOperandMapping(Rec, DestDag, DestInst, DestOperandMap, DestOperands, | |
| /*IsSourceInst*/ false, PatReqFeatures); | |
| checkDagOperandMapping(Rec, DestOperands, SourceDag, DestDag); | |
| // Create operand mapping between the source and destination instructions. | |
| createInstOperandMapping(Rec, SourceDag, DestDag, SourceOperandMap, | |
| DestOperandMap, SourceOperands, DestInst); | |
| CompressPatterns.emplace_back(SourceInst, DestInst, std::move(PatReqFeatures), | |
| SourceOperandMap, DestOperandMap, | |
| Rec->getValueAsBit("isCompressOnly")); | |
| } | |
| static unsigned getPredicates(DenseMap<const Record *, unsigned> &PredicateMap, | |
| std::vector<const Record *> &Predicates, | |
| const Record *Rec, StringRef Name) { | |
| unsigned &Entry = PredicateMap[Rec]; | |
| if (Entry) | |
| return Entry; | |
| if (!Rec->isValueUnset(Name)) { | |
| Predicates.push_back(Rec); | |
| Entry = Predicates.size(); | |
| return Entry; | |
| } | |
| PrintFatalError(Rec->getLoc(), "No " + Name + | |
| " predicate on this operand at all: '" + | |
| Rec->getName() + "'"); | |
| return 0; | |
| } | |
| static void printPredicates(ArrayRef<const Record *> Predicates, StringRef Name, | |
| raw_ostream &OS) { | |
| for (unsigned I = 0; I < Predicates.size(); ++I) { | |
| StringRef Pred = Predicates[I]->getValueAsString(Name); | |
| Pred = Pred.trim(); | |
| OS.indent(2) << "case " << I + 1 << ": {\n"; | |
| OS.indent(4) << "// " << Predicates[I]->getName() << "\n"; | |
| OS.indent(4) << Pred << "\n"; | |
| OS.indent(2) << "}\n"; | |
| } | |
| } | |
| static void mergeCondAndCode(raw_ostream &CombinedStream, StringRef CondStr, | |
| StringRef CodeStr, unsigned CompressedSize, | |
| bool ReturnSize) { | |
| CombinedStream.indent(4) << "if (" << CondStr << ") {\n"; | |
| CombinedStream << CodeStr; | |
| CombinedStream.indent(4) << " return " | |
| << (ReturnSize ? std::to_string(CompressedSize) | |
| : "true") | |
| << ";\n"; | |
| CombinedStream.indent(4) << "} // if\n"; | |
| } | |
| void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &OS, | |
| EmitterType EType) { | |
| const Record *AsmWriter = Target.getAsmWriter(); | |
| if (!AsmWriter->getValueAsInt("PassSubtarget")) | |
| PrintFatalError(AsmWriter->getLoc(), | |
| "'PassSubtarget' is false. SubTargetInfo object is needed " | |
| "for target features."); | |
| StringRef TargetName = Target.getName(); | |
| // Sort entries in CompressPatterns to handle instructions that can have more | |
| // than one candidate for compression\uncompression, e.g ADD can be | |
| // transformed to a C_ADD or a C_MV. When emitting 'uncompress()' function the | |
| // source and destination are flipped and the sort key needs to change | |
| // accordingly. | |
| llvm::stable_sort(CompressPatterns, [EType](const CompressPat &LHS, | |
| const CompressPat &RHS) { | |
| if (EType == EmitterType::Compress || EType == EmitterType::CheckCompress) | |
| return LHS.Source.getName() < RHS.Source.getName(); | |
| return LHS.Dest.getName() < RHS.Dest.getName(); | |
| }); | |
| // A list of MCOperandPredicates for all operands in use, and the reverse map. | |
| std::vector<const Record *> MCOpPredicates; | |
| DenseMap<const Record *, unsigned> MCOpPredicateMap; | |
| // A list of ImmLeaf Predicates for all operands in use, and the reverse map. | |
| std::vector<const Record *> ImmLeafPredicates; | |
| DenseMap<const Record *, unsigned> ImmLeafPredicateMap; | |
| std::string F; | |
| std::string FH; | |
| raw_string_ostream Func(F); | |
| raw_string_ostream FuncH(FH); | |
| auto GetEmitterGuard = [EType]() -> StringRef { | |
| switch (EType) { | |
| case EmitterType::Compress: | |
| return "GEN_COMPRESS_INSTR"; | |
| case EmitterType::Uncompress: | |
| return "GEN_UNCOMPRESS_INSTR"; | |
| case EmitterType::CheckCompress: | |
| return "GEN_CHECK_COMPRESS_INSTR"; | |
| } | |
| llvm_unreachable("Invalid emitter type"); | |
| }; | |
| IfDefEmitter IfDef(OS, GetEmitterGuard()); | |
| if (EType == EmitterType::Compress) { | |
| FuncH << "static bool compressInst(MCInst &OutInst,\n"; | |
| FuncH.indent(25) << "const MCInst &MI,\n"; | |
| FuncH.indent(25) << "const MCSubtargetInfo &STI) {\n"; | |
| } else if (EType == EmitterType::Uncompress) { | |
| FuncH << "static bool uncompressInst(MCInst &OutInst,\n"; | |
| FuncH.indent(27) << "const MCInst &MI,\n"; | |
| FuncH.indent(27) << "const MCSubtargetInfo &STI) {\n"; | |
| } else if (EType == EmitterType::CheckCompress) { | |
| FuncH << "static unsigned getCompressedSize(const MachineInstr &MI,\n"; | |
| FuncH.indent(34) << "const " << TargetName << "Subtarget &STI) {\n"; | |
| FuncH.indent(2) | |
| << "// Returns the compressed size, or zero if not compressible.\n"; | |
| } | |
| // HwModeId is used if we have any RegClassByHwMode patterns | |
| if (!Target.getAllRegClassByHwMode().empty()) | |
| FuncH.indent(2) << "[[maybe_unused]] unsigned HwModeId = " | |
| << "STI.getHwMode(MCSubtargetInfo::HwMode_RegInfo);\n"; | |
| if (CompressPatterns.empty()) { | |
| OS << FH; | |
| OS.indent(2) << "return " | |
| << (EType == EmitterType::CheckCompress ? "0" : "false") | |
| << ";\n}\n"; | |
| return; | |
| } | |
| std::string CaseString; | |
| raw_string_ostream CaseStream(CaseString); | |
| StringRef PrevOp; | |
| StringRef CurOp; | |
| CaseStream << " switch (MI.getOpcode()) {\n"; | |
| CaseStream << " default: return " | |
| << (EType == EmitterType::CheckCompress ? "0" : "false") << ";\n"; | |
| bool CompressOrCheck = | |
| EType == EmitterType::Compress || EType == EmitterType::CheckCompress; | |
| bool CompressOrUncompress = | |
| EType == EmitterType::Compress || EType == EmitterType::Uncompress; | |
| std::string ValidatorName = | |
| CompressOrUncompress | |
| ? (TargetName + "ValidateMCOperandFor" + | |
| (EType == EmitterType::Compress ? "Compress" : "Uncompress")) | |
| .str() | |
| : ""; | |
| for (const auto &CompressPat : CompressPatterns) { | |
| if (EType == EmitterType::Uncompress && CompressPat.IsCompressOnly) | |
| continue; | |
| std::string CondString; | |
| std::string CodeString; | |
| raw_string_ostream CondStream(CondString); | |
| raw_string_ostream CodeStream(CodeString); | |
| const CodeGenInstruction &Source = | |
| CompressOrCheck ? CompressPat.Source : CompressPat.Dest; | |
| const CodeGenInstruction &Dest = | |
| CompressOrCheck ? CompressPat.Dest : CompressPat.Source; | |
| const IndexedMap<OpData> &SourceOperandMap = | |
| CompressOrCheck ? CompressPat.SourceOperandMap | |
| : CompressPat.DestOperandMap; | |
| const IndexedMap<OpData> &DestOperandMap = | |
| CompressOrCheck ? CompressPat.DestOperandMap | |
| : CompressPat.SourceOperandMap; | |
| CurOp = Source.getName(); | |
| // Check current and previous opcode to decide to continue or end a case. | |
| if (CurOp != PrevOp) { | |
| if (!PrevOp.empty()) { | |
| CaseStream.indent(4) << "break;\n"; | |
| CaseStream.indent(2) << "} // case " + PrevOp + "\n"; | |
| } | |
| CaseStream.indent(2) << "case " + TargetName + "::" + CurOp + ": {\n"; | |
| } | |
| std::set<SubtargetFeatureLiteral> FeaturesSet; | |
| std::set<std::set<SubtargetFeatureLiteral>> AnyOfFeatureSets; | |
| // Add CompressPat required features. | |
| getRequiredFeatures(FeaturesSet, AnyOfFeatureSets, | |
| CompressPat.PatReqFeatures); | |
| // Add Dest instruction required features. | |
| std::vector<const Record *> ReqFeatures; | |
| std::vector<const Record *> RF = | |
| Dest.TheDef->getValueAsListOfDefs("Predicates"); | |
| copy_if(RF, std::back_inserter(ReqFeatures), [](const Record *R) { | |
| return R->getValueAsBit("AssemblerMatcherPredicate"); | |
| }); | |
| getRequiredFeatures(FeaturesSet, AnyOfFeatureSets, ReqFeatures); | |
| ListSeparator CondSep(" &&\n "); | |
| // Emit checks for all required features. | |
| for (const auto &Op : FeaturesSet) { | |
| StringRef Not = Op.IsNot ? "!" : ""; | |
| CondStream << CondSep << Not << "STI.getFeatureBits()[" << TargetName | |
| << "::" << Op.Feature << "]"; | |
| } | |
| // Emit checks for all required feature groups. | |
| for (const auto &Set : AnyOfFeatureSets) { | |
| CondStream << CondSep << "("; | |
| for (const auto &Op : Set) { | |
| bool IsLast = &Op == &*Set.rbegin(); | |
| StringRef Not = Op.IsNot ? "!" : ""; | |
| CondStream << Not << "STI.getFeatureBits()[" << TargetName | |
| << "::" << Op.Feature << "]"; | |
| if (!IsLast) | |
| CondStream << " || "; | |
| } | |
| CondStream << ")"; | |
| } | |
| // Start Source Inst operands validation. | |
| unsigned OpNo = 0; | |
| for (const auto &SourceOperand : Source.Operands) { | |
| for (unsigned SubOp = 0; SubOp != SourceOperand.MINumOperands; ++SubOp) { | |
| // Check for fixed immediates\registers in the source instruction. | |
| switch (SourceOperandMap[OpNo].Kind) { | |
| case OpData::Operand: | |
| if (SourceOperandMap[OpNo].OpInfo.TiedOpIdx != -1) { | |
| if (Source.Operands[OpNo].Rec->isSubClassOf("RegisterClassLike")) | |
| CondStream << CondSep << "MI.getOperand(" << OpNo | |
| << ").isReg() && MI.getOperand(" | |
| << SourceOperandMap[OpNo].OpInfo.TiedOpIdx | |
| << ").isReg()" << CondSep << "(MI.getOperand(" << OpNo | |
| << ").getReg() == MI.getOperand(" | |
| << SourceOperandMap[OpNo].OpInfo.TiedOpIdx | |
| << ").getReg())"; | |
| else | |
| PrintFatalError("Unexpected tied operand types!"); | |
| } | |
| // We don't need to do anything for source instruction operand checks. | |
| break; | |
| case OpData::Imm: | |
| CondStream << CondSep << "MI.getOperand(" << OpNo << ").isImm()" | |
| << CondSep << "(MI.getOperand(" << OpNo | |
| << ").getImm() == " << SourceOperandMap[OpNo].ImmVal | |
| << ")"; | |
| break; | |
| case OpData::Reg: { | |
| const Record *Reg = SourceOperandMap[OpNo].RegRec; | |
| CondStream << CondSep << "MI.getOperand(" << OpNo << ").isReg()" | |
| << CondSep << "(MI.getOperand(" << OpNo | |
| << ").getReg() == "; | |
| if (Reg->isSubClassOf("RegisterByHwMode")) { | |
| RegisterByHwMode(Reg, Target.getRegBank()) | |
| .emitResolverCall(CondStream, "HwModeId"); | |
| } else { | |
| CondStream << TargetName << "::" << Reg->getName(); | |
| } | |
| CondStream << ")"; | |
| break; | |
| } | |
| } | |
| ++OpNo; | |
| } | |
| } | |
| CodeStream.indent(6) << "// " << Dest.AsmString << "\n"; | |
| if (CompressOrUncompress) | |
| CodeStream.indent(6) << "OutInst.setOpcode(" << TargetName | |
| << "::" << Dest.getName() << ");\n"; | |
| OpNo = 0; | |
| for (const auto &DestOperand : Dest.Operands) { | |
| CodeStream.indent(6) << "// Operand: " << DestOperand.Name << "\n"; | |
| for (unsigned SubOp = 0; SubOp != DestOperand.MINumOperands; ++SubOp) { | |
| const Record *DestRec = DestOperand.Rec; | |
| if (DestOperand.MINumOperands > 1) | |
| DestRec = | |
| cast<DefInit>(DestOperand.MIOperandInfo->getArg(SubOp))->getDef(); | |
| switch (DestOperandMap[OpNo].Kind) { | |
| case OpData::Operand: { | |
| unsigned OpIdx = DestOperandMap[OpNo].OpInfo.Idx; | |
| const Record *DagRec = DestOperandMap[OpNo].OpInfo.DagRec; | |
| // Check that the operand in the Source instruction fits | |
| // the type for the Dest instruction. | |
| if (auto *ClassRec = Target.getAsRegClassLike(DagRec)) { | |
| // This is a register operand. Check the register class. | |
| // Don't check register class if this is a tied operand, it was done | |
| // for the operand it's tied to. | |
| if (DestOperand.getTiedRegister() == -1) { | |
| CondStream << CondSep << "MI.getOperand(" << OpIdx << ").isReg()"; | |
| if (EType == EmitterType::CheckCompress) | |
| CondStream << " && MI.getOperand(" << OpIdx | |
| << ").getReg().isPhysical()"; | |
| CondStream << CondSep << "get" << TargetName | |
| << "MCRegisterClass("; | |
| if (ClassRec->isSubClassOf("RegClassByHwMode")) { | |
| CondStream << TargetName << "RegClassByHwModeTables[HwModeId][" | |
| << TargetName << "::" << ClassRec->getName() << "]"; | |
| } else { | |
| CondStream << TargetName << "::" << ClassRec->getName() | |
| << "RegClassID"; | |
| } | |
| CondStream << ").contains(MI.getOperand(" << OpIdx | |
| << ").getReg())"; | |
| } | |
| if (CompressOrUncompress) | |
| CodeStream.indent(6) | |
| << "OutInst.addOperand(MI.getOperand(" << OpIdx << "));\n"; | |
| } else { | |
| // Handling immediate operands. | |
| if (CompressOrUncompress) { | |
| unsigned Entry = getPredicates(MCOpPredicateMap, MCOpPredicates, | |
| DagRec, "MCOperandPredicate"); | |
| CondStream << CondSep << ValidatorName << "(" | |
| << "MI.getOperand(" << OpIdx << "), STI, " << Entry | |
| << " /* " << DagRec->getName() << " */)"; | |
| // Also check DestRec if different than DagRec. | |
| if (DagRec != DestRec) { | |
| Entry = getPredicates(MCOpPredicateMap, MCOpPredicates, DestRec, | |
| "MCOperandPredicate"); | |
| CondStream << CondSep << ValidatorName << "(" | |
| << "MI.getOperand(" << OpIdx << "), STI, " << Entry | |
| << " /* " << DestRec->getName() << " */)"; | |
| } | |
| } else { | |
| unsigned Entry = | |
| getPredicates(ImmLeafPredicateMap, ImmLeafPredicates, DagRec, | |
| "ImmediateCode"); | |
| CondStream << CondSep << "MI.getOperand(" << OpIdx << ").isImm()"; | |
| CondStream << CondSep << TargetName << "ValidateMachineOperand(" | |
| << "MI.getOperand(" << OpIdx << "), &STI, " << Entry | |
| << " /* " << DagRec->getName() << " */)"; | |
| if (DagRec != DestRec) { | |
| Entry = getPredicates(ImmLeafPredicateMap, ImmLeafPredicates, | |
| DestRec, "ImmediateCode"); | |
| CondStream << CondSep << "MI.getOperand(" << OpIdx | |
| << ").isImm()"; | |
| CondStream << CondSep << TargetName << "ValidateMachineOperand(" | |
| << "MI.getOperand(" << OpIdx << "), &STI, " << Entry | |
| << " /* " << DestRec->getName() << " */)"; | |
| } | |
| } | |
| if (CompressOrUncompress) | |
| CodeStream.indent(6) | |
| << "OutInst.addOperand(MI.getOperand(" << OpIdx << "));\n"; | |
| } | |
| break; | |
| } | |
| case OpData::Imm: { | |
| if (CompressOrUncompress) { | |
| unsigned Entry = getPredicates(MCOpPredicateMap, MCOpPredicates, | |
| DestRec, "MCOperandPredicate"); | |
| CondStream << CondSep << ValidatorName << "(" | |
| << "MCOperand::createImm(" << DestOperandMap[OpNo].ImmVal | |
| << "), STI, " << Entry << " /* " << DestRec->getName() | |
| << " */)"; | |
| } else { | |
| unsigned Entry = | |
| getPredicates(ImmLeafPredicateMap, ImmLeafPredicates, DestRec, | |
| "ImmediateCode"); | |
| CondStream << CondSep << TargetName | |
| << "ValidateMachineOperand(MachineOperand::CreateImm(" | |
| << DestOperandMap[OpNo].ImmVal << "), &STI, " << Entry | |
| << " /* " << DestRec->getName() << " */)"; | |
| } | |
| if (CompressOrUncompress) | |
| CodeStream.indent(6) << "OutInst.addOperand(MCOperand::createImm(" | |
| << DestOperandMap[OpNo].ImmVal << "));\n"; | |
| } break; | |
| case OpData::Reg: { | |
| if (CompressOrUncompress) { | |
| // Fixed register has been validated at pattern validation time. | |
| const Record *Reg = DestOperandMap[OpNo].RegRec; | |
| CodeStream.indent(6) << "OutInst.addOperand(MCOperand::createReg("; | |
| if (Reg->isSubClassOf("RegisterByHwMode")) { | |
| RegisterByHwMode(Reg, Target.getRegBank()) | |
| .emitResolverCall(CodeStream, "HwModeId"); | |
| } else { | |
| CodeStream << TargetName << "::" << Reg->getName(); | |
| } | |
| CodeStream << "));\n"; | |
| } | |
| } break; | |
| } | |
| ++OpNo; | |
| } | |
| } | |
| if (CompressOrUncompress) | |
| CodeStream.indent(6) << "OutInst.setLoc(MI.getLoc());\n"; | |
| mergeCondAndCode(CaseStream, CondString, CodeString, | |
| Dest.TheDef->getValueAsInt("Size"), | |
| EType == EmitterType::CheckCompress); | |
| PrevOp = CurOp; | |
| } | |
| Func << CaseString; | |
| Func.indent(4) << "break;\n"; | |
| // Close brace for the last case. | |
| Func.indent(2) << "} // case " << CurOp << "\n"; | |
| Func.indent(2) << "} // switch\n"; | |
| Func.indent(2) << "return " | |
| << (EType == EmitterType::CheckCompress ? "0" : "false") | |
| << ";\n}\n"; | |
| if (!MCOpPredicates.empty()) { | |
| auto IndentLength = ValidatorName.size() + 13; | |
| OS << "static bool " << ValidatorName << "(const MCOperand &MCOp,\n"; | |
| OS.indent(IndentLength) << "const MCSubtargetInfo &STI,\n"; | |
| OS.indent(IndentLength) << "unsigned PredicateIndex) {\n"; | |
| OS << " switch (PredicateIndex) {\n" | |
| << " default:\n" | |
| << " llvm_unreachable(\"Unknown MCOperandPredicate kind\");\n" | |
| << " break;\n"; | |
| printPredicates(MCOpPredicates, "MCOperandPredicate", OS); | |
| OS << " }\n" | |
| << "}\n\n"; | |
| } | |
| if (!ImmLeafPredicates.empty()) { | |
| auto IndentLength = TargetName.size() + 35; | |
| OS << "static bool " << TargetName | |
| << "ValidateMachineOperand(const MachineOperand &MO,\n"; | |
| OS.indent(IndentLength) | |
| << "const " << TargetName << "Subtarget *Subtarget,\n"; | |
| OS.indent(IndentLength) | |
| << "unsigned PredicateIndex) {\n" | |
| << " int64_t Imm = MO.getImm();\n" | |
| << " switch (PredicateIndex) {\n" | |
| << " default:\n" | |
| << " llvm_unreachable(\"Unknown ImmLeaf Predicate kind\");\n" | |
| << " break;\n"; | |
| printPredicates(ImmLeafPredicates, "ImmediateCode", OS); | |
| OS << " }\n" | |
| << "}\n\n"; | |
| } | |
| OS << FH; | |
| OS << F; | |
| } | |
| void CompressInstEmitter::run(raw_ostream &OS) { | |
| // Process the CompressPat definitions, validating them as we do so. | |
| for (const Record *Pat : Records.getAllDerivedDefinitions("CompressPat")) | |
| evaluateCompressPat(Pat); | |
| // Emit file header. | |
| emitSourceFileHeader("Compress instruction Source Fragment", OS, Records); | |
| // Generate compressInst() function. | |
| emitCompressInstEmitter(OS, EmitterType::Compress); | |
| // Generate uncompressInst() function. | |
| emitCompressInstEmitter(OS, EmitterType::Uncompress); | |
| // Generate getCompressedSize() function. | |
| emitCompressInstEmitter(OS, EmitterType::CheckCompress); | |
| } | |
| static TableGen::Emitter::OptClass<CompressInstEmitter> | |
| X("gen-compress-inst-emitter", "Generate compressed instructions."); |