//===- PatternParser.cpp ----------------------------------------*- 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/GlobalISel/PatternParser.h" #include "Basic/CodeGenIntrinsics.h" #include "Common/CodeGenTarget.h" #include "Common/GlobalISel/CombinerUtils.h" #include "Common/GlobalISel/Patterns.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" namespace llvm { namespace gi { static constexpr StringLiteral MIFlagsEnumClassName = "MIFlagEnum"; namespace { class PrettyStackTraceParse : public PrettyStackTraceEntry { const Record &Def; public: PrettyStackTraceParse(const Record &Def) : Def(Def) {} void print(raw_ostream &OS) const override { if (Def.isSubClassOf("GICombineRule")) OS << "Parsing GICombineRule '" << Def.getName() << '\''; else if (Def.isSubClassOf(PatFrag::ClassName)) OS << "Parsing " << PatFrag::ClassName << " '" << Def.getName() << '\''; else OS << "Parsing '" << Def.getName() << '\''; OS << '\n'; } }; } // namespace bool PatternParser::parsePatternList( const DagInit &List, function_ref)> ParseAction, StringRef Operator, StringRef AnonPatNamePrefix) { if (List.getOperatorAsDef(DiagLoc)->getName() != Operator) { PrintError(DiagLoc, "Expected " + Operator + " operator"); return false; } if (List.getNumArgs() == 0) { PrintError(DiagLoc, Operator + " pattern list is empty"); return false; } // The match section consists of a list of matchers and predicates. Parse each // one and add the equivalent GIMatchDag nodes, predicates, and edges. for (unsigned I = 0; I < List.getNumArgs(); ++I) { Init *Arg = List.getArg(I); std::string Name = List.getArgName(I) ? List.getArgName(I)->getValue().str() : ("__" + AnonPatNamePrefix + "_" + Twine(I)).str(); if (auto Pat = parseInstructionPattern(*Arg, Name)) { if (!ParseAction(std::move(Pat))) return false; continue; } if (auto Pat = parseWipMatchOpcodeMatcher(*Arg, Name)) { if (!ParseAction(std::move(Pat))) return false; continue; } // Parse arbitrary C++ code if (const auto *StringI = dyn_cast(Arg)) { auto CXXPat = std::make_unique(*StringI, insertStrRef(Name)); if (!ParseAction(std::move(CXXPat))) return false; continue; } PrintError(DiagLoc, "Failed to parse pattern: '" + Arg->getAsString() + '\''); return false; } return true; } static const CodeGenInstruction & getInstrForIntrinsic(const CodeGenTarget &CGT, const CodeGenIntrinsic *I) { StringRef Opc; if (I->isConvergent) { Opc = I->hasSideEffects ? "G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS" : "G_INTRINSIC_CONVERGENT"; } else { Opc = I->hasSideEffects ? "G_INTRINSIC_W_SIDE_EFFECTS" : "G_INTRINSIC"; } RecordKeeper &RK = I->TheDef->getRecords(); return CGT.getInstruction(RK.getDef(Opc)); } static const CodeGenIntrinsic *getCodeGenIntrinsic(Record *R) { // Intrinsics need to have a static lifetime because the match table keeps // references to CodeGenIntrinsic objects. static DenseMap> AllIntrinsics; auto &Ptr = AllIntrinsics[R]; if (!Ptr) Ptr = std::make_unique(R, std::vector()); return Ptr.get(); } std::unique_ptr PatternParser::parseInstructionPattern(const Init &Arg, StringRef Name) { const DagInit *DagPat = dyn_cast(&Arg); if (!DagPat) return nullptr; std::unique_ptr Pat; if (const DagInit *IP = getDagWithOperatorOfSubClass(Arg, "Instruction")) { auto &Instr = CGT.getInstruction(IP->getOperatorAsDef(DiagLoc)); Pat = std::make_unique(Instr, insertStrRef(Name)); } else if (const DagInit *IP = getDagWithOperatorOfSubClass(Arg, "Intrinsic")) { Record *TheDef = IP->getOperatorAsDef(DiagLoc); const CodeGenIntrinsic *Intrin = getCodeGenIntrinsic(TheDef); const CodeGenInstruction &Instr = getInstrForIntrinsic(CGT, Intrin); Pat = std::make_unique(Instr, insertStrRef(Name)); cast(*Pat).setIntrinsic(Intrin); } else if (const DagInit *PFP = getDagWithOperatorOfSubClass(Arg, PatFrag::ClassName)) { const Record *Def = PFP->getOperatorAsDef(DiagLoc); const PatFrag *PF = parsePatFrag(Def); if (!PF) return nullptr; // Already diagnosed by parsePatFrag Pat = std::make_unique(*PF, insertStrRef(Name)); } else if (const DagInit *BP = getDagWithOperatorOfSubClass(Arg, BuiltinPattern::ClassName)) { Pat = std::make_unique(*BP->getOperatorAsDef(DiagLoc), insertStrRef(Name)); } else return nullptr; for (unsigned K = 0; K < DagPat->getNumArgs(); ++K) { Init *Arg = DagPat->getArg(K); if (auto *DagArg = getDagWithSpecificOperator(*Arg, "MIFlags")) { if (!parseInstructionPatternMIFlags(*Pat, DagArg)) return nullptr; continue; } if (!parseInstructionPatternOperand(*Pat, Arg, DagPat->getArgName(K))) return nullptr; } if (!Pat->checkSemantics(DiagLoc)) return nullptr; return std::move(Pat); } std::unique_ptr PatternParser::parseWipMatchOpcodeMatcher(const Init &Arg, StringRef Name) { const DagInit *Matcher = getDagWithSpecificOperator(Arg, "wip_match_opcode"); if (!Matcher) return nullptr; if (Matcher->getNumArgs() == 0) { PrintError(DiagLoc, "Empty wip_match_opcode"); return nullptr; } // Each argument is an opcode that can match. auto Result = std::make_unique(insertStrRef(Name)); for (const auto &Arg : Matcher->getArgs()) { Record *OpcodeDef = getDefOfSubClass(*Arg, "Instruction"); if (OpcodeDef) { Result->addOpcode(&CGT.getInstruction(OpcodeDef)); continue; } PrintError(DiagLoc, "Arguments to wip_match_opcode must be instructions"); return nullptr; } return std::move(Result); } bool PatternParser::parseInstructionPatternOperand(InstructionPattern &IP, const Init *OpInit, const StringInit *OpName) { const auto ParseErr = [&]() { PrintError(DiagLoc, "cannot parse operand '" + OpInit->getAsUnquotedString() + "' "); if (OpName) PrintNote(DiagLoc, "operand name is '" + OpName->getAsUnquotedString() + '\''); return false; }; // untyped immediate, e.g. 0 if (const auto *IntImm = dyn_cast(OpInit)) { std::string Name = OpName ? OpName->getAsUnquotedString() : ""; IP.addOperand(IntImm->getValue(), insertStrRef(Name), PatternType()); return true; } // typed immediate, e.g. (i32 0) if (const auto *DagOp = dyn_cast(OpInit)) { if (DagOp->getNumArgs() != 1) return ParseErr(); const Record *TyDef = DagOp->getOperatorAsDef(DiagLoc); auto ImmTy = PatternType::get(DiagLoc, TyDef, "cannot parse immediate '" + DagOp->getAsUnquotedString() + '\''); if (!ImmTy) return false; if (!IP.hasAllDefs()) { PrintError(DiagLoc, "out operand of '" + IP.getInstName() + "' cannot be an immediate"); return false; } const auto *Val = dyn_cast(DagOp->getArg(0)); if (!Val) return ParseErr(); std::string Name = OpName ? OpName->getAsUnquotedString() : ""; IP.addOperand(Val->getValue(), insertStrRef(Name), *ImmTy); return true; } // Typed operand e.g. $x/$z in (G_FNEG $x, $z) if (auto *DefI = dyn_cast(OpInit)) { if (!OpName) { PrintError(DiagLoc, "expected an operand name after '" + OpInit->getAsString() + '\''); return false; } const Record *Def = DefI->getDef(); auto Ty = PatternType::get(DiagLoc, Def, "cannot parse operand type"); if (!Ty) return false; IP.addOperand(insertStrRef(OpName->getAsUnquotedString()), *Ty); return true; } // Untyped operand e.g. $x/$z in (G_FNEG $x, $z) if (isa(OpInit)) { assert(OpName && "Unset w/ no OpName?"); IP.addOperand(insertStrRef(OpName->getAsUnquotedString()), PatternType()); return true; } return ParseErr(); } bool PatternParser::parseInstructionPatternMIFlags(InstructionPattern &IP, const DagInit *Op) { auto *CGIP = dyn_cast(&IP); if (!CGIP) { PrintError(DiagLoc, "matching/writing MIFlags is only allowed on CodeGenInstruction " "patterns"); return false; } const auto CheckFlagEnum = [&](const Record *R) { if (!R->isSubClassOf(MIFlagsEnumClassName)) { PrintError(DiagLoc, "'" + R->getName() + "' is not a subclass of '" + MIFlagsEnumClassName + "'"); return false; } return true; }; if (CGIP->getMIFlagsInfo()) { PrintError(DiagLoc, "MIFlags can only be present once on an instruction"); return false; } auto &FI = CGIP->getOrCreateMIFlagsInfo(); for (unsigned K = 0; K < Op->getNumArgs(); ++K) { const Init *Arg = Op->getArg(K); // Match/set a flag: (MIFlags FmNoNans) if (const auto *Def = dyn_cast(Arg)) { const Record *R = Def->getDef(); if (!CheckFlagEnum(R)) return false; FI.addSetFlag(R); continue; } // Do not match a flag/unset a flag: (MIFlags (not FmNoNans)) if (const DagInit *NotDag = getDagWithSpecificOperator(*Arg, "not")) { for (const Init *NotArg : NotDag->getArgs()) { const DefInit *DefArg = dyn_cast(NotArg); if (!DefArg) { PrintError(DiagLoc, "cannot parse '" + NotArg->getAsUnquotedString() + "': expected a '" + MIFlagsEnumClassName + "'"); return false; } const Record *R = DefArg->getDef(); if (!CheckFlagEnum(R)) return false; FI.addUnsetFlag(R); continue; } continue; } // Copy flags from a matched instruction: (MIFlags $mi) if (isa(Arg)) { FI.addCopyFlag(insertStrRef(Op->getArgName(K)->getAsUnquotedString())); continue; } } return true; } std::unique_ptr PatternParser::parsePatFragImpl(const Record *Def) { auto StackTrace = PrettyStackTraceParse(*Def); if (!Def->isSubClassOf(PatFrag::ClassName)) return nullptr; const DagInit *Ins = Def->getValueAsDag("InOperands"); if (Ins->getOperatorAsDef(Def->getLoc())->getName() != "ins") { PrintError(Def, "expected 'ins' operator for " + PatFrag::ClassName + " in operands list"); return nullptr; } const DagInit *Outs = Def->getValueAsDag("OutOperands"); if (Outs->getOperatorAsDef(Def->getLoc())->getName() != "outs") { PrintError(Def, "expected 'outs' operator for " + PatFrag::ClassName + " out operands list"); return nullptr; } auto Result = std::make_unique(*Def); if (!parsePatFragParamList(*Outs, [&](StringRef Name, unsigned Kind) { Result->addOutParam(insertStrRef(Name), (PatFrag::ParamKind)Kind); return true; })) return nullptr; if (!parsePatFragParamList(*Ins, [&](StringRef Name, unsigned Kind) { Result->addInParam(insertStrRef(Name), (PatFrag::ParamKind)Kind); return true; })) return nullptr; const ListInit *Alts = Def->getValueAsListInit("Alternatives"); unsigned AltIdx = 0; for (const Init *Alt : *Alts) { const auto *PatDag = dyn_cast(Alt); if (!PatDag) { PrintError(Def, "expected dag init for PatFrag pattern alternative"); return nullptr; } PatFrag::Alternative &A = Result->addAlternative(); const auto AddPat = [&](std::unique_ptr Pat) { A.Pats.push_back(std::move(Pat)); return true; }; SaveAndRestore> DiagLocSAR(DiagLoc, Def->getLoc()); if (!parsePatternList( *PatDag, AddPat, "pattern", /*AnonPatPrefix*/ (Def->getName() + "_alt" + Twine(AltIdx++) + "_pattern").str())) return nullptr; } if (!Result->buildOperandsTables() || !Result->checkSemantics()) return nullptr; return Result; } bool PatternParser::parsePatFragParamList( const DagInit &OpsList, function_ref ParseAction) { for (unsigned K = 0; K < OpsList.getNumArgs(); ++K) { const StringInit *Name = OpsList.getArgName(K); const Init *Ty = OpsList.getArg(K); if (!Name) { PrintError(DiagLoc, "all operands must be named'"); return false; } const std::string NameStr = Name->getAsUnquotedString(); PatFrag::ParamKind OpKind; if (isSpecificDef(*Ty, "gi_imm")) OpKind = PatFrag::PK_Imm; else if (isSpecificDef(*Ty, "root")) OpKind = PatFrag::PK_Root; else if (isa(Ty) || isSpecificDef(*Ty, "gi_mo")) // no type = gi_mo. OpKind = PatFrag::PK_MachineOperand; else { PrintError( DiagLoc, '\'' + NameStr + "' operand type was expected to be 'root', 'gi_imm' or 'gi_mo'"); return false; } if (!ParseAction(NameStr, (unsigned)OpKind)) return false; } return true; } const PatFrag *PatternParser::parsePatFrag(const Record *Def) { // Cache already parsed PatFrags to avoid doing extra work. static DenseMap> ParsedPatFrags; auto It = ParsedPatFrags.find(Def); if (It != ParsedPatFrags.end()) { SeenPatFrags.insert(It->second.get()); return It->second.get(); } std::unique_ptr NewPatFrag = parsePatFragImpl(Def); if (!NewPatFrag) { PrintError(Def, "Could not parse " + PatFrag::ClassName + " '" + Def->getName() + "'"); // Put a nullptr in the map so we don't attempt parsing this again. ParsedPatFrags[Def] = nullptr; return nullptr; } const auto *Res = NewPatFrag.get(); ParsedPatFrags[Def] = std::move(NewPatFrag); SeenPatFrags.insert(Res); return Res; } } // namespace gi } // namespace llvm