Path: blob/main/contrib/llvm-project/llvm/utils/TableGen/Common/GlobalISel/Patterns.cpp
35315 views
//===- Patterns.cpp --------------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Patterns.h"9#include "Basic/CodeGenIntrinsics.h"10#include "CXXPredicates.h"11#include "CodeExpander.h"12#include "CodeExpansions.h"13#include "Common/CodeGenInstruction.h"14#include "llvm/ADT/StringSet.h"15#include "llvm/Support/Debug.h"16#include "llvm/Support/raw_ostream.h"17#include "llvm/TableGen/Error.h"18#include "llvm/TableGen/Record.h"1920namespace llvm {21namespace gi {2223//===- PatternType --------------------------------------------------------===//2425std::optional<PatternType> PatternType::get(ArrayRef<SMLoc> DiagLoc,26const Record *R, Twine DiagCtx) {27assert(R);28if (R->isSubClassOf("ValueType")) {29PatternType PT(PT_ValueType);30PT.Data.Def = R;31return PT;32}3334if (R->isSubClassOf(TypeOfClassName)) {35auto RawOpName = R->getValueAsString("OpName");36if (!RawOpName.starts_with("$")) {37PrintError(DiagLoc, DiagCtx + ": invalid operand name format '" +38RawOpName + "' in " + TypeOfClassName +39": expected '$' followed by an operand name");40return std::nullopt;41}4243PatternType PT(PT_TypeOf);44PT.Data.Str = RawOpName.drop_front(1);45return PT;46}4748PrintError(DiagLoc, DiagCtx + ": unknown type '" + R->getName() + "'");49return std::nullopt;50}5152PatternType PatternType::getTypeOf(StringRef OpName) {53PatternType PT(PT_TypeOf);54PT.Data.Str = OpName;55return PT;56}5758StringRef PatternType::getTypeOfOpName() const {59assert(isTypeOf());60return Data.Str;61}6263const Record *PatternType::getLLTRecord() const {64assert(isLLT());65return Data.Def;66}6768bool PatternType::operator==(const PatternType &Other) const {69if (Kind != Other.Kind)70return false;7172switch (Kind) {73case PT_None:74return true;75case PT_ValueType:76return Data.Def == Other.Data.Def;77case PT_TypeOf:78return Data.Str == Other.Data.Str;79}8081llvm_unreachable("Unknown Type Kind");82}8384std::string PatternType::str() const {85switch (Kind) {86case PT_None:87return "";88case PT_ValueType:89return Data.Def->getName().str();90case PT_TypeOf:91return (TypeOfClassName + "<$" + getTypeOfOpName() + ">").str();92}9394llvm_unreachable("Unknown type!");95}9697//===- Pattern ------------------------------------------------------------===//9899void Pattern::dump() const { return print(dbgs()); }100101const char *Pattern::getKindName() const {102switch (Kind) {103case K_AnyOpcode:104return "AnyOpcodePattern";105case K_CXX:106return "CXXPattern";107case K_CodeGenInstruction:108return "CodeGenInstructionPattern";109case K_PatFrag:110return "PatFragPattern";111case K_Builtin:112return "BuiltinPattern";113}114115llvm_unreachable("unknown pattern kind!");116}117118void Pattern::printImpl(raw_ostream &OS, bool PrintName,119function_ref<void()> ContentPrinter) const {120OS << "(" << getKindName() << " ";121if (PrintName)122OS << "name:" << getName() << " ";123ContentPrinter();124OS << ")";125}126127//===- AnyOpcodePattern ---------------------------------------------------===//128129void AnyOpcodePattern::print(raw_ostream &OS, bool PrintName) const {130printImpl(OS, PrintName, [&OS, this]() {131OS << "["132<< join(map_range(Insts,133[](const auto *I) { return I->TheDef->getName(); }),134", ")135<< "]";136});137}138139//===- CXXPattern ---------------------------------------------------------===//140141CXXPattern::CXXPattern(const StringInit &Code, StringRef Name)142: CXXPattern(Code.getAsUnquotedString(), Name) {}143144const CXXPredicateCode &145CXXPattern::expandCode(const CodeExpansions &CE, ArrayRef<SMLoc> Locs,146function_ref<void(raw_ostream &)> AddComment) const {147assert(!IsApply && "'apply' CXX patterns should be handled differently!");148149std::string Result;150raw_string_ostream OS(Result);151152if (AddComment)153AddComment(OS);154155CodeExpander Expander(RawCode, CE, Locs, /*ShowExpansions*/ false);156Expander.emit(OS);157return CXXPredicateCode::getMatchCode(std::move(Result));158}159160void CXXPattern::print(raw_ostream &OS, bool PrintName) const {161printImpl(OS, PrintName, [&OS, this] {162OS << (IsApply ? "apply" : "match") << " code:\"";163printEscapedString(getRawCode(), OS);164OS << "\"";165});166}167168//===- InstructionOperand -------------------------------------------------===//169170std::string InstructionOperand::describe() const {171if (!hasImmValue())172return "MachineOperand $" + getOperandName().str() + "";173std::string Str = "imm " + std::to_string(getImmValue());174if (isNamedImmediate())175Str += ":$" + getOperandName().str() + "";176return Str;177}178179void InstructionOperand::print(raw_ostream &OS) const {180if (isDef())181OS << "<def>";182183bool NeedsColon = true;184if (Type) {185if (hasImmValue())186OS << "(" << Type.str() << " " << getImmValue() << ")";187else188OS << Type.str();189} else if (hasImmValue())190OS << getImmValue();191else192NeedsColon = false;193194if (isNamedOperand())195OS << (NeedsColon ? ":" : "") << "$" << getOperandName();196}197198void InstructionOperand::dump() const { return print(dbgs()); }199200//===- InstructionPattern -------------------------------------------------===//201202bool InstructionPattern::diagnoseAllSpecialTypes(ArrayRef<SMLoc> Loc,203Twine Msg) const {204bool HasDiag = false;205for (const auto &[Idx, Op] : enumerate(operands())) {206if (Op.getType().isSpecial()) {207PrintError(Loc, Msg);208PrintNote(Loc, "operand " + Twine(Idx) + " of '" + getName() +209"' has type '" + Op.getType().str() + "'");210HasDiag = true;211}212}213return HasDiag;214}215216void InstructionPattern::reportUnreachable(ArrayRef<SMLoc> Locs) const {217PrintError(Locs, "pattern '" + getName() + "' ('" + getInstName() +218"') is unreachable from the pattern root!");219}220221bool InstructionPattern::checkSemantics(ArrayRef<SMLoc> Loc) {222unsigned NumExpectedOperands = getNumInstOperands();223224if (isVariadic()) {225if (Operands.size() < NumExpectedOperands) {226PrintError(Loc, +"'" + getInstName() + "' expected at least " +227Twine(NumExpectedOperands) + " operands, got " +228Twine(Operands.size()));229return false;230}231} else if (NumExpectedOperands != Operands.size()) {232PrintError(Loc, +"'" + getInstName() + "' expected " +233Twine(NumExpectedOperands) + " operands, got " +234Twine(Operands.size()));235return false;236}237238unsigned OpIdx = 0;239unsigned NumDefs = getNumInstDefs();240for (auto &Op : Operands)241Op.setIsDef(OpIdx++ < NumDefs);242243return true;244}245246void InstructionPattern::print(raw_ostream &OS, bool PrintName) const {247printImpl(OS, PrintName, [&OS, this] {248OS << getInstName() << " operands:[";249StringRef Sep;250for (const auto &Op : Operands) {251OS << Sep;252Op.print(OS);253Sep = ", ";254}255OS << "]";256257printExtras(OS);258});259}260261//===- OperandTable -------------------------------------------------------===//262263bool OperandTable::addPattern(InstructionPattern *P,264function_ref<void(StringRef)> DiagnoseRedef) {265for (const auto &Op : P->named_operands()) {266StringRef OpName = Op.getOperandName();267268// We always create an entry in the OperandTable, even for uses.269// Uses of operands that don't have a def (= live-ins) will remain with a270// nullptr as the Def.271//272// This allows us tell whether an operand exists in a pattern or not. If273// there is no entry for it, it doesn't exist, if there is an entry, it's274// used/def'd at least once.275auto &Def = Table[OpName];276277if (!Op.isDef())278continue;279280if (Def) {281DiagnoseRedef(OpName);282return false;283}284285Def = P;286}287288return true;289}290291void OperandTable::print(raw_ostream &OS, StringRef Name,292StringRef Indent) const {293OS << Indent << "(OperandTable ";294if (!Name.empty())295OS << Name << " ";296if (Table.empty()) {297OS << "<empty>)\n";298return;299}300301SmallVector<StringRef, 0> Keys(Table.keys());302sort(Keys);303304OS << '\n';305for (const auto &Key : Keys) {306const auto *Def = Table.at(Key);307OS << Indent << " " << Key << " -> "308<< (Def ? Def->getName() : "<live-in>") << '\n';309}310OS << Indent << ")\n";311}312313void OperandTable::dump() const { print(dbgs()); }314315//===- MIFlagsInfo --------------------------------------------------------===//316317void MIFlagsInfo::addSetFlag(const Record *R) {318SetF.insert(R->getValueAsString("EnumName"));319}320321void MIFlagsInfo::addUnsetFlag(const Record *R) {322UnsetF.insert(R->getValueAsString("EnumName"));323}324325void MIFlagsInfo::addCopyFlag(StringRef InstName) { CopyF.insert(InstName); }326327//===- CodeGenInstructionPattern ------------------------------------------===//328329bool CodeGenInstructionPattern::is(StringRef OpcodeName) const {330return I.TheDef->getName() == OpcodeName;331}332333bool CodeGenInstructionPattern::isVariadic() const {334return !isIntrinsic() && I.Operands.isVariadic;335}336337bool CodeGenInstructionPattern::hasVariadicDefs() const {338// Note: we cannot use variadicOpsAreDefs, it's not set for339// GenericInstructions.340if (!isVariadic())341return false;342343if (I.variadicOpsAreDefs)344return true;345346DagInit *OutOps = I.TheDef->getValueAsDag("OutOperandList");347if (OutOps->arg_empty())348return false;349350auto *LastArgTy = dyn_cast<DefInit>(OutOps->getArg(OutOps->arg_size() - 1));351return LastArgTy && LastArgTy->getDef()->getName() == "variable_ops";352}353354unsigned CodeGenInstructionPattern::getNumInstDefs() const {355if (isIntrinsic())356return IntrinInfo->IS.RetTys.size();357358if (!isVariadic() || !hasVariadicDefs())359return I.Operands.NumDefs;360unsigned NumOuts = I.Operands.size() - I.Operands.NumDefs;361assert(Operands.size() > NumOuts);362return std::max<unsigned>(I.Operands.NumDefs, Operands.size() - NumOuts);363}364365unsigned CodeGenInstructionPattern::getNumInstOperands() const {366if (isIntrinsic())367return IntrinInfo->IS.RetTys.size() + IntrinInfo->IS.ParamTys.size();368369unsigned NumCGIOps = I.Operands.size();370return isVariadic() ? std::max<unsigned>(NumCGIOps, Operands.size())371: NumCGIOps;372}373374MIFlagsInfo &CodeGenInstructionPattern::getOrCreateMIFlagsInfo() {375if (!FI)376FI = std::make_unique<MIFlagsInfo>();377return *FI;378}379380StringRef CodeGenInstructionPattern::getInstName() const {381return I.TheDef->getName();382}383384void CodeGenInstructionPattern::printExtras(raw_ostream &OS) const {385if (isIntrinsic())386OS << " intrinsic(@" << IntrinInfo->Name << ")";387388if (!FI)389return;390391OS << " (MIFlags";392if (!FI->set_flags().empty())393OS << " (set " << join(FI->set_flags(), ", ") << ")";394if (!FI->unset_flags().empty())395OS << " (unset " << join(FI->unset_flags(), ", ") << ")";396if (!FI->copy_flags().empty())397OS << " (copy " << join(FI->copy_flags(), ", ") << ")";398OS << ')';399}400401//===- OperandTypeChecker -------------------------------------------------===//402403bool OperandTypeChecker::check(404InstructionPattern &P,405std::function<bool(const PatternType &)> VerifyTypeOfOperand) {406Pats.push_back(&P);407408for (auto &Op : P.operands()) {409const auto Ty = Op.getType();410if (!Ty)411continue;412413if (Ty.isTypeOf() && !VerifyTypeOfOperand(Ty))414return false;415416if (!Op.isNamedOperand())417continue;418419StringRef OpName = Op.getOperandName();420auto &Info = Types[OpName];421if (!Info.Type) {422Info.Type = Ty;423Info.PrintTypeSrcNote = [this, OpName, Ty, &P]() {424PrintSeenWithTypeIn(P, OpName, Ty);425};426continue;427}428429if (Info.Type != Ty) {430PrintError(DiagLoc, "conflicting types for operand '" +431Op.getOperandName() + "': '" + Info.Type.str() +432"' vs '" + Ty.str() + "'");433PrintSeenWithTypeIn(P, OpName, Ty);434Info.PrintTypeSrcNote();435return false;436}437}438439return true;440}441442void OperandTypeChecker::propagateTypes() {443for (auto *Pat : Pats) {444for (auto &Op : Pat->named_operands()) {445if (auto &Info = Types[Op.getOperandName()]; Info.Type)446Op.setType(Info.Type);447}448}449}450451void OperandTypeChecker::PrintSeenWithTypeIn(InstructionPattern &P,452StringRef OpName,453PatternType Ty) const {454PrintNote(DiagLoc, "'" + OpName + "' seen with type '" + Ty.str() + "' in '" +455P.getName() + "'");456}457458StringRef PatFrag::getParamKindStr(ParamKind OK) {459switch (OK) {460case PK_Root:461return "root";462case PK_MachineOperand:463return "machine_operand";464case PK_Imm:465return "imm";466}467468llvm_unreachable("Unknown operand kind!");469}470471//===- PatFrag -----------------------------------------------------------===//472473PatFrag::PatFrag(const Record &Def) : Def(Def) {474assert(Def.isSubClassOf(ClassName));475}476477StringRef PatFrag::getName() const { return Def.getName(); }478479ArrayRef<SMLoc> PatFrag::getLoc() const { return Def.getLoc(); }480481void PatFrag::addInParam(StringRef Name, ParamKind Kind) {482Params.emplace_back(Param{Name, Kind});483}484485iterator_range<PatFrag::ParamIt> PatFrag::in_params() const {486return {Params.begin() + NumOutParams, Params.end()};487}488489void PatFrag::addOutParam(StringRef Name, ParamKind Kind) {490assert(NumOutParams == Params.size() &&491"Adding out-param after an in-param!");492Params.emplace_back(Param{Name, Kind});493++NumOutParams;494}495496iterator_range<PatFrag::ParamIt> PatFrag::out_params() const {497return {Params.begin(), Params.begin() + NumOutParams};498}499500unsigned PatFrag::num_roots() const {501return count_if(out_params(),502[&](const auto &P) { return P.Kind == PK_Root; });503}504505unsigned PatFrag::getParamIdx(StringRef Name) const {506for (const auto &[Idx, Op] : enumerate(Params)) {507if (Op.Name == Name)508return Idx;509}510511return -1;512}513514bool PatFrag::checkSemantics() {515for (const auto &Alt : Alts) {516for (const auto &Pat : Alt.Pats) {517switch (Pat->getKind()) {518case Pattern::K_AnyOpcode:519PrintError("wip_match_opcode cannot be used in " + ClassName);520return false;521case Pattern::K_Builtin:522PrintError("Builtin instructions cannot be used in " + ClassName);523return false;524case Pattern::K_CXX:525continue;526case Pattern::K_CodeGenInstruction:527if (cast<CodeGenInstructionPattern>(Pat.get())->diagnoseAllSpecialTypes(528Def.getLoc(), PatternType::SpecialTyClassName +529" is not supported in " + ClassName))530return false;531continue;532case Pattern::K_PatFrag:533// TODO: It's just that the emitter doesn't handle it but technically534// there is no reason why we can't. We just have to be careful with535// operand mappings, it could get complex.536PrintError("nested " + ClassName + " are not supported");537return false;538}539}540}541542StringSet<> SeenOps;543for (const auto &Op : in_params()) {544if (SeenOps.count(Op.Name)) {545PrintError("duplicate parameter '" + Op.Name + "'");546return false;547}548549// Check this operand is NOT defined in any alternative's patterns.550for (const auto &Alt : Alts) {551if (Alt.OpTable.lookup(Op.Name).Def) {552PrintError("input parameter '" + Op.Name + "' cannot be redefined!");553return false;554}555}556557if (Op.Kind == PK_Root) {558PrintError("input parameterr '" + Op.Name + "' cannot be a root!");559return false;560}561562SeenOps.insert(Op.Name);563}564565for (const auto &Op : out_params()) {566if (Op.Kind != PK_Root && Op.Kind != PK_MachineOperand) {567PrintError("output parameter '" + Op.Name +568"' must be 'root' or 'gi_mo'");569return false;570}571572if (SeenOps.count(Op.Name)) {573PrintError("duplicate parameter '" + Op.Name + "'");574return false;575}576577// Check this operand is defined in all alternative's patterns.578for (const auto &Alt : Alts) {579const auto *OpDef = Alt.OpTable.getDef(Op.Name);580if (!OpDef) {581PrintError("output parameter '" + Op.Name +582"' must be defined by all alternative patterns in '" +583Def.getName() + "'");584return false;585}586587if (Op.Kind == PK_Root && OpDef->getNumInstDefs() != 1) {588// The instruction that defines the root must have a single def.589// Otherwise we'd need to support multiple roots and it gets messy.590//591// e.g. this is not supported:592// (pattern (G_UNMERGE_VALUES $x, $root, $vec))593PrintError("all instructions that define root '" + Op.Name + "' in '" +594Def.getName() + "' can only have a single output operand");595return false;596}597}598599SeenOps.insert(Op.Name);600}601602if (num_out_params() != 0 && num_roots() == 0) {603PrintError(ClassName + " must have one root in its 'out' operands");604return false;605}606607if (num_roots() > 1) {608PrintError(ClassName + " can only have one root");609return false;610}611612// TODO: find unused params613614const auto CheckTypeOf = [&](const PatternType &) -> bool {615llvm_unreachable("GITypeOf should have been rejected earlier!");616};617618// Now, typecheck all alternatives.619for (auto &Alt : Alts) {620OperandTypeChecker OTC(Def.getLoc());621for (auto &Pat : Alt.Pats) {622if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {623if (!OTC.check(*IP, CheckTypeOf))624return false;625}626}627OTC.propagateTypes();628}629630return true;631}632633bool PatFrag::handleUnboundInParam(StringRef ParamName, StringRef ArgName,634ArrayRef<SMLoc> DiagLoc) const {635// The parameter must be a live-in of all alternatives for this to work.636// Otherwise, we risk having unbound parameters being used (= crashes).637//638// Examples:639//640// in (ins $y), (patterns (G_FNEG $dst, $y), "return matchFnegOp(${y})")641// even if $y is unbound, we'll lazily bind it when emitting the G_FNEG.642//643// in (ins $y), (patterns "return matchFnegOp(${y})")644// if $y is unbound when this fragment is emitted, C++ code expansion will645// fail.646for (const auto &Alt : Alts) {647auto &OT = Alt.OpTable;648if (!OT.lookup(ParamName).Found) {649llvm::PrintError(DiagLoc, "operand '" + ArgName + "' (for parameter '" +650ParamName + "' of '" + getName() +651"') cannot be unbound");652PrintNote(653DiagLoc,654"one or more alternatives of '" + getName() + "' do not bind '" +655ParamName +656"' to an instruction operand; either use a bound operand or "657"ensure '" +658Def.getName() + "' binds '" + ParamName +659"' in all alternatives");660return false;661}662}663664return true;665}666667bool PatFrag::buildOperandsTables() {668// enumerate(...) doesn't seem to allow lvalues so we need to count the old669// way.670unsigned Idx = 0;671672const auto DiagnoseRedef = [this, &Idx](StringRef OpName) {673PrintError("Operand '" + OpName +674"' is defined multiple times in patterns of alternative #" +675std::to_string(Idx));676};677678for (auto &Alt : Alts) {679for (auto &Pat : Alt.Pats) {680auto *IP = dyn_cast<InstructionPattern>(Pat.get());681if (!IP)682continue;683684if (!Alt.OpTable.addPattern(IP, DiagnoseRedef))685return false;686}687688++Idx;689}690691return true;692}693694void PatFrag::print(raw_ostream &OS, StringRef Indent) const {695OS << Indent << "(PatFrag name:" << getName() << '\n';696if (!in_params().empty()) {697OS << Indent << " (ins ";698printParamsList(OS, in_params());699OS << ")\n";700}701702if (!out_params().empty()) {703OS << Indent << " (outs ";704printParamsList(OS, out_params());705OS << ")\n";706}707708// TODO: Dump OperandTable as well.709OS << Indent << " (alternatives [\n";710for (const auto &Alt : Alts) {711OS << Indent << " [\n";712for (const auto &Pat : Alt.Pats) {713OS << Indent << " ";714Pat->print(OS, /*PrintName=*/true);715OS << ",\n";716}717OS << Indent << " ],\n";718}719OS << Indent << " ])\n";720721OS << Indent << ')';722}723724void PatFrag::dump() const { print(dbgs()); }725726void PatFrag::printParamsList(raw_ostream &OS, iterator_range<ParamIt> Params) {727OS << '['728<< join(map_range(Params,729[](auto &O) {730return (O.Name + ":" + getParamKindStr(O.Kind)).str();731}),732", ")733<< ']';734}735736void PatFrag::PrintError(Twine Msg) const { llvm::PrintError(&Def, Msg); }737738ArrayRef<InstructionOperand> PatFragPattern::getApplyDefsNeeded() const {739assert(PF.num_roots() == 1);740// Only roots need to be redef.741for (auto [Idx, Param] : enumerate(PF.out_params())) {742if (Param.Kind == PatFrag::PK_Root)743return getOperand(Idx);744}745llvm_unreachable("root not found!");746}747748//===- PatFragPattern -----------------------------------------------------===//749750bool PatFragPattern::checkSemantics(ArrayRef<SMLoc> DiagLoc) {751if (!InstructionPattern::checkSemantics(DiagLoc))752return false;753754for (const auto &[Idx, Op] : enumerate(Operands)) {755switch (PF.getParam(Idx).Kind) {756case PatFrag::PK_Imm:757if (!Op.hasImmValue()) {758PrintError(DiagLoc, "expected operand " + std::to_string(Idx) +759" of '" + getInstName() +760"' to be an immediate; got " + Op.describe());761return false;762}763if (Op.isNamedImmediate()) {764PrintError(DiagLoc, "operand " + std::to_string(Idx) + " of '" +765getInstName() +766"' cannot be a named immediate");767return false;768}769break;770case PatFrag::PK_Root:771case PatFrag::PK_MachineOperand:772if (!Op.isNamedOperand() || Op.isNamedImmediate()) {773PrintError(DiagLoc, "expected operand " + std::to_string(Idx) +774" of '" + getInstName() +775"' to be a MachineOperand; got " +776Op.describe());777return false;778}779break;780}781}782783return true;784}785786bool PatFragPattern::mapInputCodeExpansions(const CodeExpansions &ParentCEs,787CodeExpansions &PatFragCEs,788ArrayRef<SMLoc> DiagLoc) const {789for (const auto &[Idx, Op] : enumerate(operands())) {790StringRef ParamName = PF.getParam(Idx).Name;791792// Operands to a PFP can only be named, or be an immediate, but not a named793// immediate.794assert(!Op.isNamedImmediate());795796if (Op.isNamedOperand()) {797StringRef ArgName = Op.getOperandName();798// Map it only if it's been defined.799auto It = ParentCEs.find(ArgName);800if (It == ParentCEs.end()) {801if (!PF.handleUnboundInParam(ParamName, ArgName, DiagLoc))802return false;803} else804PatFragCEs.declare(ParamName, It->second);805continue;806}807808if (Op.hasImmValue()) {809PatFragCEs.declare(ParamName, std::to_string(Op.getImmValue()));810continue;811}812813llvm_unreachable("Unknown Operand Type!");814}815816return true;817}818819//===- BuiltinPattern -----------------------------------------------------===//820821BuiltinPattern::BuiltinInfo BuiltinPattern::getBuiltinInfo(const Record &Def) {822assert(Def.isSubClassOf(ClassName));823824StringRef Name = Def.getName();825for (const auto &KBI : KnownBuiltins) {826if (KBI.DefName == Name)827return KBI;828}829830PrintFatalError(Def.getLoc(),831"Unimplemented " + ClassName + " def '" + Name + "'");832}833834bool BuiltinPattern::checkSemantics(ArrayRef<SMLoc> Loc) {835if (!InstructionPattern::checkSemantics(Loc))836return false;837838// For now all builtins just take names, no immediates.839for (const auto &[Idx, Op] : enumerate(operands())) {840if (!Op.isNamedOperand() || Op.isNamedImmediate()) {841PrintError(Loc, "expected operand " + std::to_string(Idx) + " of '" +842getInstName() + "' to be a name");843return false;844}845}846847return true;848}849850} // namespace gi851} // namespace llvm852853854