578 lines · cpp
1//===- CodeGenInstruction.cpp - CodeGen Instruction Class Wrapper ---------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the CodeGenInstruction class.10//11//===----------------------------------------------------------------------===//12 13#include "CodeGenInstruction.h"14#include "CodeGenTarget.h"15#include "llvm/ADT/StringExtras.h"16#include "llvm/TableGen/Error.h"17#include "llvm/TableGen/Record.h"18#include <set>19using namespace llvm;20 21//===----------------------------------------------------------------------===//22// CGIOperandList Implementation23//===----------------------------------------------------------------------===//24 25CGIOperandList::CGIOperandList(const Record *R) : TheDef(R) {26 isPredicable = false;27 hasOptionalDef = false;28 isVariadic = false;29 30 const DagInit *OutDI = R->getValueAsDag("OutOperandList");31 32 if (const DefInit *Init = dyn_cast<DefInit>(OutDI->getOperator())) {33 if (Init->getDef()->getName() != "outs")34 PrintFatalError(R->getLoc(),35 R->getName() +36 ": invalid def name for output list: use 'outs'");37 } else {38 PrintFatalError(R->getLoc(),39 R->getName() + ": invalid output list: use 'outs'");40 }41 42 NumDefs = OutDI->getNumArgs();43 44 const DagInit *InDI = R->getValueAsDag("InOperandList");45 if (const DefInit *Init = dyn_cast<DefInit>(InDI->getOperator())) {46 if (Init->getDef()->getName() != "ins")47 PrintFatalError(R->getLoc(),48 R->getName() +49 ": invalid def name for input list: use 'ins'");50 } else {51 PrintFatalError(R->getLoc(),52 R->getName() + ": invalid input list: use 'ins'");53 }54 55 unsigned MIOperandNo = 0;56 std::set<std::string> OperandNames;57 unsigned e = InDI->getNumArgs() + OutDI->getNumArgs();58 OperandList.reserve(e);59 bool VariadicOuts = false;60 for (unsigned i = 0; i != e; ++i) {61 const Init *ArgInit;62 StringRef ArgName;63 if (i < NumDefs) {64 ArgInit = OutDI->getArg(i);65 ArgName = OutDI->getArgNameStr(i);66 } else {67 ArgInit = InDI->getArg(i - NumDefs);68 ArgName = InDI->getArgNameStr(i - NumDefs);69 }70 71 const DagInit *SubArgDag = dyn_cast<DagInit>(ArgInit);72 if (SubArgDag)73 ArgInit = SubArgDag->getOperator();74 75 const DefInit *Arg = dyn_cast<DefInit>(ArgInit);76 if (!Arg)77 PrintFatalError(R->getLoc(), "Illegal operand for the '" + R->getName() +78 "' instruction!");79 80 const Record *Rec = Arg->getDef();81 StringRef PrintMethod = "printOperand";82 StringRef EncoderMethod;83 std::string OperandType = "OPERAND_UNKNOWN";84 std::string OperandNamespace = "MCOI";85 unsigned NumOps = 1;86 const DagInit *MIOpInfo = nullptr;87 if (Rec->isSubClassOf("RegisterOperand")) {88 PrintMethod = Rec->getValueAsString("PrintMethod");89 OperandType = Rec->getValueAsString("OperandType").str();90 OperandNamespace = Rec->getValueAsString("OperandNamespace").str();91 EncoderMethod = Rec->getValueAsString("EncoderMethod");92 } else if (Rec->isSubClassOf("Operand")) {93 PrintMethod = Rec->getValueAsString("PrintMethod");94 OperandType = Rec->getValueAsString("OperandType").str();95 OperandNamespace = Rec->getValueAsString("OperandNamespace").str();96 // If there is an explicit encoder method, use it.97 EncoderMethod = Rec->getValueAsString("EncoderMethod");98 MIOpInfo = Rec->getValueAsDag("MIOperandInfo");99 100 // Verify that MIOpInfo has an 'ops' root value.101 if (!isa<DefInit>(MIOpInfo->getOperator()) ||102 cast<DefInit>(MIOpInfo->getOperator())->getDef()->getName() != "ops")103 PrintFatalError(R->getLoc(),104 "Bad value for MIOperandInfo in operand '" +105 Rec->getName() + "'\n");106 107 // If we have MIOpInfo, then we have #operands equal to number of entries108 // in MIOperandInfo.109 if (unsigned NumArgs = MIOpInfo->getNumArgs())110 NumOps = NumArgs;111 112 if (Rec->isSubClassOf("PredicateOp"))113 isPredicable = true;114 else if (Rec->isSubClassOf("OptionalDefOperand"))115 hasOptionalDef = true;116 } else if (Rec->getName() == "variable_ops") {117 if (i < NumDefs)118 VariadicOuts = true;119 isVariadic = true;120 continue;121 } else if (Rec->isSubClassOf("RegisterClassLike")) {122 OperandType = "OPERAND_REGISTER";123 } else if (!Rec->isSubClassOf("PointerLikeRegClass") &&124 !Rec->isSubClassOf("unknown_class")) {125 PrintFatalError(R->getLoc(), "Unknown operand class '" + Rec->getName() +126 "' in '" + R->getName() +127 "' instruction!");128 }129 130 // Check that the operand has a name and that it's unique.131 if (ArgName.empty())132 PrintFatalError(R->getLoc(), "In instruction '" + R->getName() +133 "', operand #" + Twine(i) +134 " has no name!");135 if (!OperandNames.insert(ArgName.str()).second)136 PrintFatalError(R->getLoc(),137 "In instruction '" + R->getName() + "', operand #" +138 Twine(i) +139 " has the same name as a previous operand!");140 141 OperandInfo &OpInfo = OperandList.emplace_back(142 Rec, ArgName, PrintMethod, OperandNamespace + "::" + OperandType,143 MIOperandNo, NumOps, MIOpInfo);144 145 if (SubArgDag) {146 if (!MIOpInfo) {147 PrintFatalError(R->getLoc(), "In instruction '" + R->getName() +148 "', operand #" + Twine(i) + " has " +149 Twine(SubArgDag->getNumArgs()) +150 " sub-arg names, but no sub-operands");151 }152 153 unsigned NumSubArgs = SubArgDag->getNumArgs();154 unsigned NumSubOps = MIOpInfo->getNumArgs();155 if (NumSubArgs != NumSubOps) {156 PrintFatalError(R->getLoc(),157 "In instruction '" + R->getName() + "', operand #" +158 Twine(i) + " has " + Twine(NumSubArgs) +159 " sub-arg names, expected " + Twine(NumSubOps));160 }161 162 for (unsigned j = 0; j < NumOps; ++j) {163 if (!isa<UnsetInit>(SubArgDag->getArg(j)))164 PrintFatalError(R->getLoc(),165 "In instruction '" + R->getName() + "', operand #" +166 Twine(i) + " sub-arg #" + Twine(j) +167 " has unexpected operand (expected only $name).");168 169 StringRef SubArgName = SubArgDag->getArgNameStr(j);170 if (SubArgName.empty())171 PrintFatalError(R->getLoc(), "In instruction '" + R->getName() +172 "', operand #" + Twine(i) +173 " has no name!");174 if (!OperandNames.insert(SubArgName.str()).second)175 PrintFatalError(R->getLoc(),176 "In instruction '" + R->getName() + "', operand #" +177 Twine(i) + " sub-arg #" + Twine(j) +178 " has the same name as a previous operand!");179 180 if (auto MaybeEncoderMethod =181 cast<DefInit>(MIOpInfo->getArg(j))182 ->getDef()183 ->getValueAsOptionalString("EncoderMethod")) {184 OpInfo.EncoderMethodNames[j] = *MaybeEncoderMethod;185 }186 187 OpInfo.SubOpNames[j] = SubArgName;188 SubOpAliases[SubArgName] = {i, j};189 }190 } else if (!EncoderMethod.empty()) {191 // If we have no explicit sub-op dag, but have an top-level encoder192 // method, the single encoder will multiple sub-ops, itself.193 OpInfo.EncoderMethodNames[0] = EncoderMethod;194 }195 196 MIOperandNo += NumOps;197 }198 199 if (VariadicOuts)200 --NumDefs;201}202 203/// getOperandNamed - Return the index of the operand with the specified204/// non-empty name. If the instruction does not have an operand with the205/// specified name, abort.206///207unsigned CGIOperandList::getOperandNamed(StringRef Name) const {208 std::optional<unsigned> OpIdx = findOperandNamed(Name);209 if (OpIdx)210 return *OpIdx;211 PrintFatalError(TheDef->getLoc(), "'" + TheDef->getName() +212 "' does not have an operand named '$" +213 Name + "'!");214}215 216/// findOperandNamed - Query whether the instruction has an operand of the217/// given name. If so, the index of the operand. Otherwise, return std::nullopt.218std::optional<unsigned> CGIOperandList::findOperandNamed(StringRef Name) const {219 assert(!Name.empty() && "Cannot search for operand with no name!");220 for (const auto &[Index, Opnd] : enumerate(OperandList))221 if (Opnd.Name == Name)222 return Index;223 return std::nullopt;224}225 226std::optional<std::pair<unsigned, unsigned>>227CGIOperandList::findSubOperandAlias(StringRef Name) const {228 assert(!Name.empty() && "Cannot search for operand with no name!");229 auto SubOpIter = SubOpAliases.find(Name);230 if (SubOpIter != SubOpAliases.end())231 return SubOpIter->second;232 return std::nullopt;233}234 235std::pair<unsigned, unsigned>236CGIOperandList::parseOperandName(StringRef Op, bool AllowWholeOp) const {237 if (!Op.starts_with("$"))238 PrintFatalError(TheDef->getLoc(),239 TheDef->getName() + ": Illegal operand name: '" + Op + "'");240 241 StringRef OpName = Op.substr(1);242 StringRef SubOpName;243 244 // Check to see if this is $foo.bar.245 StringRef::size_type DotIdx = OpName.find_first_of('.');246 if (DotIdx != StringRef::npos) {247 SubOpName = OpName.substr(DotIdx + 1);248 if (SubOpName.empty())249 PrintFatalError(TheDef->getLoc(),250 TheDef->getName() +251 ": illegal empty suboperand name in '" + Op + "'");252 OpName = OpName.substr(0, DotIdx);253 }254 255 if (auto SubOp = findSubOperandAlias(OpName)) {256 // Found a name for a piece of an operand, just return it directly.257 if (!SubOpName.empty()) {258 PrintFatalError(259 TheDef->getLoc(),260 TheDef->getName() +261 ": Cannot use dotted suboperand name within suboperand '" +262 OpName + "'");263 }264 return *SubOp;265 }266 267 unsigned OpIdx = getOperandNamed(OpName);268 269 if (SubOpName.empty()) { // If no suboperand name was specified:270 // If one was needed, throw.271 if (OperandList[OpIdx].MINumOperands > 1 && !AllowWholeOp &&272 SubOpName.empty())273 PrintFatalError(TheDef->getLoc(),274 TheDef->getName() +275 ": Illegal to refer to"276 " whole operand part of complex operand '" +277 Op + "'");278 279 // Otherwise, return the operand.280 return {OpIdx, 0U};281 }282 283 // Find the suboperand number involved.284 const DagInit *MIOpInfo = OperandList[OpIdx].MIOperandInfo;285 if (!MIOpInfo)286 PrintFatalError(TheDef->getLoc(), TheDef->getName() +287 ": unknown suboperand name in '" +288 Op + "'");289 290 // Find the operand with the right name.291 for (unsigned i = 0, e = MIOpInfo->getNumArgs(); i != e; ++i)292 if (MIOpInfo->getArgNameStr(i) == SubOpName)293 return {OpIdx, i};294 295 // Otherwise, didn't find it!296 PrintFatalError(TheDef->getLoc(), TheDef->getName() +297 ": unknown suboperand name in '" + Op +298 "'");299 return {0U, 0U};300}301 302static void ParseConstraint(StringRef CStr, CGIOperandList &Ops,303 const Record *Rec) {304 // EARLY_CLOBBER: @early $reg305 StringRef::size_type wpos = CStr.find_first_of(" \t");306 StringRef::size_type start = CStr.find_first_not_of(" \t");307 StringRef Tok = CStr.substr(start, wpos - start);308 if (Tok == "@earlyclobber") {309 StringRef Name = CStr.substr(wpos + 1);310 wpos = Name.find_first_not_of(" \t");311 if (wpos == StringRef::npos)312 PrintFatalError(Rec->getLoc(),313 "Illegal format for @earlyclobber constraint in '" +314 Rec->getName() + "': '" + CStr + "'");315 Name = Name.substr(wpos);316 std::pair<unsigned, unsigned> Op = Ops.parseOperandName(Name, false);317 318 // Build the string for the operand319 if (!Ops[Op.first].Constraints[Op.second].isNone())320 PrintFatalError(Rec->getLoc(), "Operand '" + Name + "' of '" +321 Rec->getName() +322 "' cannot have multiple constraints!");323 Ops[Op.first].Constraints[Op.second] =324 CGIOperandList::ConstraintInfo::getEarlyClobber();325 return;326 }327 328 // Only other constraint is "TIED_TO" for now.329 StringRef::size_type pos = CStr.find_first_of('=');330 if (pos == StringRef::npos || pos == 0 ||331 CStr.find_first_of(" \t", pos) != (pos + 1) ||332 CStr.find_last_of(" \t", pos) != (pos - 1))333 PrintFatalError(Rec->getLoc(), "Unrecognized constraint '" + CStr +334 "' in '" + Rec->getName() + "'");335 start = CStr.find_first_not_of(" \t");336 337 // TIED_TO: $src1 = $dst338 wpos = CStr.find_first_of(" \t", start);339 if (wpos == StringRef::npos || wpos > pos)340 PrintFatalError(Rec->getLoc(),341 "Illegal format for tied-to constraint in '" +342 Rec->getName() + "': '" + CStr + "'");343 StringRef LHSOpName = CStr.substr(start, wpos - start);344 std::pair<unsigned, unsigned> LHSOp = Ops.parseOperandName(LHSOpName, false);345 346 wpos = CStr.find_first_not_of(" \t", pos + 1);347 if (wpos == StringRef::npos)348 PrintFatalError(Rec->getLoc(),349 "Illegal format for tied-to constraint: '" + CStr + "'");350 351 StringRef RHSOpName = CStr.substr(wpos);352 std::pair<unsigned, unsigned> RHSOp = Ops.parseOperandName(RHSOpName, false);353 354 // Sort the operands into order, which should put the output one355 // first. But keep the original order, for use in diagnostics.356 bool FirstIsDest = (LHSOp < RHSOp);357 std::pair<unsigned, unsigned> DestOp = (FirstIsDest ? LHSOp : RHSOp);358 StringRef DestOpName = (FirstIsDest ? LHSOpName : RHSOpName);359 std::pair<unsigned, unsigned> SrcOp = (FirstIsDest ? RHSOp : LHSOp);360 StringRef SrcOpName = (FirstIsDest ? RHSOpName : LHSOpName);361 362 // Ensure one operand is a def and the other is a use.363 if (DestOp.first >= Ops.NumDefs)364 PrintFatalError(Rec->getLoc(), "Input operands '" + LHSOpName + "' and '" +365 RHSOpName + "' of '" + Rec->getName() +366 "' cannot be tied!");367 if (SrcOp.first < Ops.NumDefs)368 PrintFatalError(Rec->getLoc(), "Output operands '" + LHSOpName + "' and '" +369 RHSOpName + "' of '" + Rec->getName() +370 "' cannot be tied!");371 372 // The constraint has to go on the operand with higher index, i.e.373 // the source one. Check there isn't another constraint there374 // already.375 if (!Ops[SrcOp.first].Constraints[SrcOp.second].isNone())376 PrintFatalError(Rec->getLoc(), "Operand '" + SrcOpName + "' of '" +377 Rec->getName() +378 "' cannot have multiple constraints!");379 380 unsigned DestFlatOpNo = Ops.getFlattenedOperandNumber(DestOp);381 auto NewConstraint = CGIOperandList::ConstraintInfo::getTied(DestFlatOpNo);382 383 // Check that the earlier operand is not the target of another tie384 // before making it the target of this one.385 for (const CGIOperandList::OperandInfo &Op : Ops) {386 for (unsigned i = 0; i < Op.MINumOperands; i++)387 if (Op.Constraints[i] == NewConstraint)388 PrintFatalError(Rec->getLoc(),389 "Operand '" + DestOpName + "' of '" + Rec->getName() +390 "' cannot have multiple operands tied to it!");391 }392 393 Ops[SrcOp.first].Constraints[SrcOp.second] = NewConstraint;394}395 396static void ParseConstraints(StringRef CStr, CGIOperandList &Ops,397 const Record *Rec) {398 if (CStr.empty())399 return;400 401 StringRef delims(",");402 StringRef::size_type bidx, eidx;403 404 bidx = CStr.find_first_not_of(delims);405 while (bidx != StringRef::npos) {406 eidx = CStr.find_first_of(delims, bidx);407 if (eidx == StringRef::npos)408 eidx = CStr.size();409 410 ParseConstraint(CStr.substr(bidx, eidx - bidx), Ops, Rec);411 bidx = CStr.find_first_not_of(delims, eidx);412 }413}414 415//===----------------------------------------------------------------------===//416// CodeGenInstruction Implementation417//===----------------------------------------------------------------------===//418 419CodeGenInstruction::CodeGenInstruction(const Record *R)420 : TheDef(R), Operands(R), InferredFrom(nullptr) {421 Namespace = R->getValueAsString("Namespace");422 AsmString = R->getValueAsString("AsmString");423 424 isPreISelOpcode = R->getValueAsBit("isPreISelOpcode");425 isReturn = R->getValueAsBit("isReturn");426 isEHScopeReturn = R->getValueAsBit("isEHScopeReturn");427 isBranch = R->getValueAsBit("isBranch");428 isIndirectBranch = R->getValueAsBit("isIndirectBranch");429 isCompare = R->getValueAsBit("isCompare");430 isMoveImm = R->getValueAsBit("isMoveImm");431 isMoveReg = R->getValueAsBit("isMoveReg");432 isBitcast = R->getValueAsBit("isBitcast");433 isSelect = R->getValueAsBit("isSelect");434 isBarrier = R->getValueAsBit("isBarrier");435 isCall = R->getValueAsBit("isCall");436 isAdd = R->getValueAsBit("isAdd");437 isTrap = R->getValueAsBit("isTrap");438 canFoldAsLoad = R->getValueAsBit("canFoldAsLoad");439 isPredicable = !R->getValueAsBit("isUnpredicable") &&440 (Operands.isPredicable || R->getValueAsBit("isPredicable"));441 isConvertibleToThreeAddress = R->getValueAsBit("isConvertibleToThreeAddress");442 isCommutable = R->getValueAsBit("isCommutable");443 isTerminator = R->getValueAsBit("isTerminator");444 isReMaterializable = R->getValueAsBit("isReMaterializable");445 hasDelaySlot = R->getValueAsBit("hasDelaySlot");446 usesCustomInserter = R->getValueAsBit("usesCustomInserter");447 hasPostISelHook = R->getValueAsBit("hasPostISelHook");448 hasCtrlDep = R->getValueAsBit("hasCtrlDep");449 isNotDuplicable = R->getValueAsBit("isNotDuplicable");450 isRegSequence = R->getValueAsBit("isRegSequence");451 isExtractSubreg = R->getValueAsBit("isExtractSubreg");452 isInsertSubreg = R->getValueAsBit("isInsertSubreg");453 isConvergent = R->getValueAsBit("isConvergent");454 hasNoSchedulingInfo = R->getValueAsBit("hasNoSchedulingInfo");455 FastISelShouldIgnore = R->getValueAsBit("FastISelShouldIgnore");456 variadicOpsAreDefs = R->getValueAsBit("variadicOpsAreDefs");457 isAuthenticated = R->getValueAsBit("isAuthenticated");458 459 bool Unset;460 mayLoad = R->getValueAsBitOrUnset("mayLoad", Unset);461 mayLoad_Unset = Unset;462 mayStore = R->getValueAsBitOrUnset("mayStore", Unset);463 mayStore_Unset = Unset;464 mayRaiseFPException = R->getValueAsBit("mayRaiseFPException");465 hasSideEffects = R->getValueAsBitOrUnset("hasSideEffects", Unset);466 hasSideEffects_Unset = Unset;467 468 isAsCheapAsAMove = R->getValueAsBit("isAsCheapAsAMove");469 hasExtraSrcRegAllocReq = R->getValueAsBit("hasExtraSrcRegAllocReq");470 hasExtraDefRegAllocReq = R->getValueAsBit("hasExtraDefRegAllocReq");471 isCodeGenOnly = R->getValueAsBit("isCodeGenOnly");472 isPseudo = R->getValueAsBit("isPseudo");473 isMeta = R->getValueAsBit("isMeta");474 ImplicitDefs = R->getValueAsListOfDefs("Defs");475 ImplicitUses = R->getValueAsListOfDefs("Uses");476 477 // This flag is only inferred from the pattern.478 hasChain = false;479 hasChain_Inferred = false;480 481 // Parse Constraints.482 ParseConstraints(R->getValueAsString("Constraints"), Operands, R);483 484 // First check for a ComplexDeprecationPredicate.485 if (R->getValue("ComplexDeprecationPredicate")) {486 HasComplexDeprecationPredicate = true;487 DeprecatedReason = R->getValueAsString("ComplexDeprecationPredicate").str();488 } else if (const RecordVal *Dep = R->getValue("DeprecatedFeatureMask")) {489 // Check if we have a Subtarget feature mask.490 HasComplexDeprecationPredicate = false;491 DeprecatedReason = Dep->getValue()->getAsString();492 } else {493 // This instruction isn't deprecated.494 HasComplexDeprecationPredicate = false;495 DeprecatedReason = "";496 }497}498 499/// HasOneImplicitDefWithKnownVT - If the instruction has at least one500/// implicit def and it has a known VT, return the VT, otherwise return501/// MVT::Other.502MVT CodeGenInstruction::HasOneImplicitDefWithKnownVT(503 const CodeGenTarget &TargetInfo) const {504 if (ImplicitDefs.empty())505 return MVT::Other;506 507 // Check to see if the first implicit def has a resolvable type.508 const Record *FirstImplicitDef = ImplicitDefs[0];509 assert(FirstImplicitDef->isSubClassOf("Register"));510 const std::vector<ValueTypeByHwMode> &RegVTs =511 TargetInfo.getRegisterVTs(FirstImplicitDef);512 if (RegVTs.size() == 1 && RegVTs[0].isSimple())513 return RegVTs[0].getSimple();514 return MVT::Other;515}516 517/// FlattenAsmStringVariants - Flatten the specified AsmString to only518/// include text from the specified variant, returning the new string.519std::string CodeGenInstruction::FlattenAsmStringVariants(StringRef Cur,520 unsigned Variant) {521 std::string Res;522 523 for (;;) {524 // Find the start of the next variant string.525 size_t VariantsStart = 0;526 for (size_t e = Cur.size(); VariantsStart != e; ++VariantsStart)527 if (Cur[VariantsStart] == '{' &&528 (VariantsStart == 0 ||529 (Cur[VariantsStart - 1] != '$' && Cur[VariantsStart - 1] != '\\')))530 break;531 532 // Add the prefix to the result.533 Res += Cur.slice(0, VariantsStart);534 if (VariantsStart == Cur.size())535 break;536 537 ++VariantsStart; // Skip the '{'.538 539 // Scan to the end of the variants string.540 size_t VariantsEnd = VariantsStart;541 unsigned NestedBraces = 1;542 for (size_t e = Cur.size(); VariantsEnd != e; ++VariantsEnd) {543 if (Cur[VariantsEnd] == '}' && Cur[VariantsEnd - 1] != '\\') {544 if (--NestedBraces == 0)545 break;546 } else if (Cur[VariantsEnd] == '{')547 ++NestedBraces;548 }549 550 // Select the Nth variant (or empty).551 StringRef Selection =552 Cur.substr(VariantsStart, VariantsEnd - VariantsStart);553 for (unsigned i = 0; i != Variant; ++i)554 Selection = Selection.split('|').second;555 Res += Selection.split('|').first;556 557 assert(VariantsEnd != Cur.size() &&558 "Unterminated variants in assembly string!");559 Cur = Cur.substr(VariantsEnd + 1);560 }561 562 return Res;563}564 565bool CodeGenInstruction::isOperandImpl(StringRef OpListName, unsigned i,566 StringRef PropertyName) const {567 const DagInit *ConstraintList = TheDef->getValueAsDag(OpListName);568 if (!ConstraintList || i >= ConstraintList->getNumArgs())569 return false;570 571 const DefInit *Constraint = dyn_cast<DefInit>(ConstraintList->getArg(i));572 if (!Constraint)573 return false;574 575 return Constraint->getDef()->isSubClassOf("TypedOperand") &&576 Constraint->getDef()->getValueAsBit(PropertyName);577}578