MLIR  22.0.0git
LLVMAttrs.cpp
Go to the documentation of this file.
1 //===- LLVMAttrs.cpp - LLVM Attributes registration -----------------------===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines the attribute details for the LLVM IR dialect in MLIR.
10 //
11 //===----------------------------------------------------------------------===//
12 
17 #include "mlir/IR/Builders.h"
20 #include "llvm/ADT/StringExtras.h"
21 #include "llvm/ADT/TypeSwitch.h"
22 #include "llvm/BinaryFormat/Dwarf.h"
23 
24 using namespace mlir;
25 using namespace mlir::LLVM;
26 
27 /// Parses DWARF expression arguments with respect to the DWARF operation
28 /// opcode. Some DWARF expression operations have a specific number of operands
29 /// and may appear in a textual form.
30 static ParseResult parseExpressionArg(AsmParser &parser, uint64_t opcode,
31  SmallVector<uint64_t> &args);
32 
33 /// Prints DWARF expression arguments with respect to the specific DWARF
34 /// operation. Some operands are printed in their textual form.
35 static void printExpressionArg(AsmPrinter &printer, uint64_t opcode,
36  ArrayRef<uint64_t> args);
37 
38 #include "mlir/Dialect/LLVMIR/LLVMAttrInterfaces.cpp.inc"
39 #include "mlir/Dialect/LLVMIR/LLVMOpsEnums.cpp.inc"
40 #define GET_ATTRDEF_CLASSES
41 #include "mlir/Dialect/LLVMIR/LLVMOpsAttrDefs.cpp.inc"
42 
43 //===----------------------------------------------------------------------===//
44 // LLVMDialect registration
45 //===----------------------------------------------------------------------===//
46 
47 void LLVMDialect::registerAttributes() {
48  addAttributes<
49 #define GET_ATTRDEF_LIST
50 #include "mlir/Dialect/LLVMIR/LLVMOpsAttrDefs.cpp.inc"
51 
52  >();
53 }
54 
55 //===----------------------------------------------------------------------===//
56 // AddressSpaceAttr
57 //===----------------------------------------------------------------------===//
58 
59 /// Checks whether the given type is an LLVM type that can be loaded or stored.
61  Type type, ptr::AtomicOrdering ordering, std::optional<int64_t> alignment,
62  const ::mlir::DataLayout *dataLayout,
64  if (!isLoadableType(type)) {
65  if (emitError)
66  emitError() << "type must be LLVM type with size, but got " << type;
67  return false;
68  }
69  if (ordering == ptr::AtomicOrdering::not_atomic)
70  return true;
71 
72  // To check atomic validity we need a datalayout.
73  if (!dataLayout) {
74  if (emitError)
75  emitError() << "expected a valid data layout";
76  return false;
77  }
78  if (!isTypeCompatibleWithAtomicOp(type, *dataLayout)) {
79  if (emitError)
80  emitError() << "unsupported type " << type << " for atomic access";
81  return false;
82  }
83  return true;
84 }
85 
86 bool AddressSpaceAttr::isValidLoad(
87  Type type, ptr::AtomicOrdering ordering, std::optional<int64_t> alignment,
88  const ::mlir::DataLayout *dataLayout,
90  return detail::isValidLoadStoreImpl(type, ordering, alignment, dataLayout,
91  emitError);
92 }
93 
94 bool AddressSpaceAttr::isValidStore(
95  Type type, ptr::AtomicOrdering ordering, std::optional<int64_t> alignment,
96  const ::mlir::DataLayout *dataLayout,
98  return detail::isValidLoadStoreImpl(type, ordering, alignment, dataLayout,
99  emitError);
100 }
101 
102 bool AddressSpaceAttr::isValidAtomicOp(
103  ptr::AtomicBinOp op, Type type, ptr::AtomicOrdering ordering,
104  std::optional<int64_t> alignment, const ::mlir::DataLayout *dataLayout,
106  // TODO: update this method once `ptr.atomic_rmw` is implemented.
107  assert(false && "unimplemented, see TODO in the source.");
108  return false;
109 }
110 
111 bool AddressSpaceAttr::isValidAtomicXchg(
112  Type type, ptr::AtomicOrdering successOrdering,
113  ptr::AtomicOrdering failureOrdering, std::optional<int64_t> alignment,
114  const ::mlir::DataLayout *dataLayout,
116  // TODO: update this method once `ptr.atomic_cmpxchg` is implemented.
117  assert(false && "unimplemented, see TODO in the source.");
118  return false;
119 }
120 
121 bool AddressSpaceAttr::isValidAddrSpaceCast(
122  Type tgt, Type src, function_ref<InFlightDiagnostic()> emitError) const {
123  // TODO: update this method once the `ptr.addrspace_cast` op is added to the
124  // dialect.
125  assert(false && "unimplemented, see TODO in the source.");
126  return false;
127 }
128 
129 bool AddressSpaceAttr::isValidPtrIntCast(
130  Type intLikeTy, Type ptrLikeTy,
132  // TODO: update this method once the int-cast ops are added to the `ptr`
133  // dialect.
134  assert(false && "unimplemented, see TODO in the source.");
135  return false;
136 }
137 
138 //===----------------------------------------------------------------------===//
139 // AliasScopeAttr
140 //===----------------------------------------------------------------------===//
141 
142 LogicalResult
144  Attribute id, AliasScopeDomainAttr domain,
145  StringAttr description) {
146  (void)domain;
147  (void)description;
148  if (!llvm::isa<StringAttr, DistinctAttr>(id))
149  return emitError()
150  << "id of an alias scope must be a StringAttr or a DistrinctAttr";
151 
152  return success();
153 }
154 
155 //===----------------------------------------------------------------------===//
156 // DINodeAttr
157 //===----------------------------------------------------------------------===//
158 
160  return llvm::isa<
161  DIBasicTypeAttr, DICommonBlockAttr, DICompileUnitAttr,
162  DICompositeTypeAttr, DIDerivedTypeAttr, DIFileAttr, DIGenericSubrangeAttr,
163  DIGlobalVariableAttr, DIImportedEntityAttr, DILabelAttr,
164  DILexicalBlockAttr, DILexicalBlockFileAttr, DILocalVariableAttr,
165  DIModuleAttr, DINamespaceAttr, DINullTypeAttr, DIAnnotationAttr,
166  DIStringTypeAttr, DISubprogramAttr, DISubrangeAttr, DISubroutineTypeAttr>(
167  attr);
168 }
169 
170 //===----------------------------------------------------------------------===//
171 // DIScopeAttr
172 //===----------------------------------------------------------------------===//
173 
175  return llvm::isa<DICommonBlockAttr, DICompileUnitAttr, DICompositeTypeAttr,
176  DIDerivedTypeAttr, DIFileAttr, DILocalScopeAttr,
177  DIModuleAttr, DINamespaceAttr>(attr);
178 }
179 
180 //===----------------------------------------------------------------------===//
181 // DILocalScopeAttr
182 //===----------------------------------------------------------------------===//
183 
185  return llvm::isa<DILexicalBlockAttr, DILexicalBlockFileAttr,
186  DISubprogramAttr>(attr);
187 }
188 
189 //===----------------------------------------------------------------------===//
190 // DIVariableAttr
191 //===----------------------------------------------------------------------===//
192 
194  return llvm::isa<DILocalVariableAttr, DIGlobalVariableAttr>(attr);
195 }
196 
197 //===----------------------------------------------------------------------===//
198 // DITypeAttr
199 //===----------------------------------------------------------------------===//
200 
202  return llvm::isa<DINullTypeAttr, DIBasicTypeAttr, DICompositeTypeAttr,
203  DIDerivedTypeAttr, DIStringTypeAttr, DISubroutineTypeAttr>(
204  attr);
205 }
206 
207 //===----------------------------------------------------------------------===//
208 // TBAANodeAttr
209 //===----------------------------------------------------------------------===//
210 
212  return llvm::isa<TBAATypeDescriptorAttr, TBAARootAttr>(attr);
213 }
214 
215 //===----------------------------------------------------------------------===//
216 // MemoryEffectsAttr
217 //===----------------------------------------------------------------------===//
218 
219 MemoryEffectsAttr MemoryEffectsAttr::get(MLIRContext *context,
220  ArrayRef<ModRefInfo> memInfoArgs) {
221  if (memInfoArgs.empty())
222  return MemoryEffectsAttr::get(context, ModRefInfo::ModRef,
223  ModRefInfo::ModRef, ModRefInfo::ModRef);
224  if (memInfoArgs.size() == 3)
225  return MemoryEffectsAttr::get(context, memInfoArgs[0], memInfoArgs[1],
226  memInfoArgs[2]);
227  return {};
228 }
229 
230 bool MemoryEffectsAttr::isReadWrite() {
231  if (this->getArgMem() != ModRefInfo::ModRef)
232  return false;
233  if (this->getInaccessibleMem() != ModRefInfo::ModRef)
234  return false;
235  if (this->getOther() != ModRefInfo::ModRef)
236  return false;
237  return true;
238 }
239 
240 //===----------------------------------------------------------------------===//
241 // DIExpression
242 //===----------------------------------------------------------------------===//
243 
244 DIExpressionAttr DIExpressionAttr::get(MLIRContext *context) {
245  return get(context, ArrayRef<DIExpressionElemAttr>({}));
246 }
247 
248 ParseResult parseExpressionArg(AsmParser &parser, uint64_t opcode,
249  SmallVector<uint64_t> &args) {
250  auto operandParser = [&]() -> LogicalResult {
251  uint64_t operand = 0;
252  if (!args.empty() && opcode == llvm::dwarf::DW_OP_LLVM_convert) {
253  // Attempt to parse a keyword.
254  StringRef keyword;
255  if (succeeded(parser.parseOptionalKeyword(&keyword))) {
256  operand = llvm::dwarf::getAttributeEncoding(keyword);
257  if (operand == 0) {
258  // The keyword is invalid.
259  return parser.emitError(parser.getCurrentLocation())
260  << "encountered unknown attribute encoding \"" << keyword
261  << "\"";
262  }
263  }
264  }
265 
266  // operand should be non-zero if a keyword was parsed. Otherwise, the
267  // operand MUST be an integer.
268  if (operand == 0) {
269  // Parse the next operand as an integer.
270  if (parser.parseInteger(operand)) {
271  return parser.emitError(parser.getCurrentLocation())
272  << "expected integer operand";
273  }
274  }
275 
276  args.push_back(operand);
277  return success();
278  };
279 
280  // Parse operands as a comma-separated list.
281  return parser.parseCommaSeparatedList(operandParser);
282 }
283 
284 void printExpressionArg(AsmPrinter &printer, uint64_t opcode,
285  ArrayRef<uint64_t> args) {
286  size_t i = 0;
287  llvm::interleaveComma(args, printer, [&](uint64_t operand) {
288  if (i > 0 && opcode == llvm::dwarf::DW_OP_LLVM_convert) {
289  if (const StringRef keyword =
290  llvm::dwarf::AttributeEncodingString(operand);
291  !keyword.empty()) {
292  printer << keyword;
293  return;
294  }
295  }
296  // All operands are expected to be printed as integers.
297  printer << operand;
298  i++;
299  });
300 }
301 
302 //===----------------------------------------------------------------------===//
303 // DICompositeTypeAttr
304 //===----------------------------------------------------------------------===//
305 
306 DIRecursiveTypeAttrInterface
307 DICompositeTypeAttr::withRecId(DistinctAttr recId) {
309  getContext(), recId, getIsRecSelf(), getTag(), getName(), getFile(),
310  getLine(), getScope(), getBaseType(), getFlags(), getSizeInBits(),
311  getAlignInBits(), getDataLocation(), getRank(), getAllocated(),
312  getAssociated(), getElements());
313 }
314 
315 DIRecursiveTypeAttrInterface
316 DICompositeTypeAttr::getRecSelf(DistinctAttr recId) {
317  return DICompositeTypeAttr::get(recId.getContext(), recId, /*isRecSelf=*/true,
318  0, {}, {}, 0, {}, {}, DIFlags(), 0, 0, {}, {},
319  {}, {}, {});
320 }
321 
322 //===----------------------------------------------------------------------===//
323 // DISubprogramAttr
324 //===----------------------------------------------------------------------===//
325 
326 DIRecursiveTypeAttrInterface DISubprogramAttr::withRecId(DistinctAttr recId) {
327  return DISubprogramAttr::get(getContext(), recId, getIsRecSelf(), getId(),
328  getCompileUnit(), getScope(), getName(),
329  getLinkageName(), getFile(), getLine(),
330  getScopeLine(), getSubprogramFlags(), getType(),
331  getRetainedNodes(), getAnnotations());
332 }
333 
334 DIRecursiveTypeAttrInterface DISubprogramAttr::getRecSelf(DistinctAttr recId) {
335  return DISubprogramAttr::get(recId.getContext(), recId, /*isRecSelf=*/true,
336  {}, {}, {}, {}, {}, {}, 0, 0, {}, {}, {}, {});
337 }
338 
339 //===----------------------------------------------------------------------===//
340 // ConstantRangeAttr
341 //===----------------------------------------------------------------------===//
342 
344  llvm::SMLoc loc = parser.getCurrentLocation();
345  IntegerType widthType;
346  if (parser.parseLess() || parser.parseType(widthType) ||
347  parser.parseComma()) {
348  return Attribute{};
349  }
350  unsigned bitWidth = widthType.getWidth();
351  APInt lower(bitWidth, 0);
352  APInt upper(bitWidth, 0);
353  if (parser.parseInteger(lower) || parser.parseComma() ||
354  parser.parseInteger(upper) || parser.parseGreater())
355  return Attribute{};
356  // Non-positive numbers may use more bits than `bitWidth`
357  lower = lower.sextOrTrunc(bitWidth);
358  upper = upper.sextOrTrunc(bitWidth);
359  return parser.getChecked<ConstantRangeAttr>(loc, parser.getContext(), lower,
360  upper);
361 }
362 
363 void ConstantRangeAttr::print(AsmPrinter &printer) const {
364  printer << "<i" << getLower().getBitWidth() << ", " << getLower() << ", "
365  << getUpper() << ">";
366 }
367 
368 LogicalResult
370  APInt lower, APInt upper) {
371  if (lower.getBitWidth() != upper.getBitWidth())
372  return emitError()
373  << "expected lower and upper to have matching bitwidths but got "
374  << lower.getBitWidth() << " vs. " << upper.getBitWidth();
375  return success();
376 }
377 
378 //===----------------------------------------------------------------------===//
379 // TargetFeaturesAttr
380 //===----------------------------------------------------------------------===//
381 
382 TargetFeaturesAttr TargetFeaturesAttr::get(MLIRContext *context,
383  llvm::ArrayRef<StringRef> features) {
384  return Base::get(context,
385  llvm::map_to_vector(features, [&](StringRef feature) {
386  return StringAttr::get(context, feature);
387  }));
388 }
389 
390 TargetFeaturesAttr
391 TargetFeaturesAttr::getChecked(function_ref<InFlightDiagnostic()> emitError,
392  MLIRContext *context,
393  llvm::ArrayRef<StringRef> features) {
394  return Base::getChecked(emitError, context,
395  llvm::map_to_vector(features, [&](StringRef feature) {
396  return StringAttr::get(context, feature);
397  }));
398 }
399 
400 TargetFeaturesAttr TargetFeaturesAttr::get(MLIRContext *context,
401  StringRef targetFeatures) {
402  SmallVector<StringRef> features;
403  targetFeatures.split(features, ',', /*MaxSplit=*/-1,
404  /*KeepEmpty=*/false);
405  return get(context, features);
406 }
407 
408 TargetFeaturesAttr
409 TargetFeaturesAttr::getChecked(function_ref<InFlightDiagnostic()> emitError,
410  MLIRContext *context, StringRef targetFeatures) {
411  SmallVector<StringRef> features;
412  targetFeatures.split(features, ',', /*MaxSplit=*/-1,
413  /*KeepEmpty=*/false);
414  ArrayRef featuresRef(features);
415  return getChecked(emitError, context, featuresRef);
416 }
417 
418 LogicalResult
420  llvm::ArrayRef<StringAttr> features) {
421  for (StringAttr featureAttr : features) {
422  if (!featureAttr || featureAttr.empty())
423  return emitError() << "target features can not be null or empty";
424  auto feature = featureAttr.strref();
425  if (feature[0] != '+' && feature[0] != '-')
426  return emitError() << "target features must start with '+' or '-'";
427  if (feature.contains(','))
428  return emitError() << "target features can not contain ','";
429  }
430  return success();
431 }
432 
433 bool TargetFeaturesAttr::contains(StringAttr feature) const {
434  if (nullOrEmpty())
435  return false;
436  // Note: Using StringAttr does pointer comparisons.
437  return llvm::is_contained(getFeatures(), feature);
438 }
439 
440 bool TargetFeaturesAttr::contains(StringRef feature) const {
441  if (nullOrEmpty())
442  return false;
443  return llvm::is_contained(getFeatures(), feature);
444 }
445 
446 std::string TargetFeaturesAttr::getFeaturesString() const {
447  std::string featuresString;
448  llvm::raw_string_ostream ss(featuresString);
449  llvm::interleave(
450  getFeatures(), ss, [&](auto &feature) { ss << feature.strref(); }, ",");
451  return featuresString;
452 }
453 
454 TargetFeaturesAttr TargetFeaturesAttr::featuresAt(Operation *op) {
455  auto parentFunction = op->getParentOfType<FunctionOpInterface>();
456  if (!parentFunction)
457  return {};
458  return parentFunction.getOperation()->getAttrOfType<TargetFeaturesAttr>(
459  getAttributeName());
460 }
461 
462 FailureOr<Attribute> TargetFeaturesAttr::query(DataLayoutEntryKey key) {
463  auto stringKey = dyn_cast<StringAttr>(key);
464  if (!stringKey)
465  return failure();
466 
467  if (contains(stringKey))
468  return UnitAttr::get(getContext());
469 
470  if (contains((std::string("+") + stringKey.strref()).str()))
471  return BoolAttr::get(getContext(), true);
472 
473  if (contains((std::string("-") + stringKey.strref()).str()))
474  return BoolAttr::get(getContext(), false);
475 
476  return failure();
477 }
478 
479 //===----------------------------------------------------------------------===//
480 // TargetAttr
481 //===----------------------------------------------------------------------===//
482 
483 FailureOr<::mlir::Attribute> TargetAttr::query(DataLayoutEntryKey key) {
484  if (auto stringAttrKey = dyn_cast<StringAttr>(key)) {
485  if (stringAttrKey.getValue() == "triple")
486  return getTriple();
487  if (stringAttrKey.getValue() == "chip")
488  return getChip();
489  if (stringAttrKey.getValue() == "features" && getFeatures())
490  return getFeatures();
491  }
492  return failure();
493 }
494 
495 //===----------------------------------------------------------------------===//
496 // ModuleFlagAttr
497 //===----------------------------------------------------------------------===//
498 
499 LogicalResult
501  LLVM::ModFlagBehavior flagBehavior, StringAttr key,
502  Attribute value) {
503  if (key == LLVMDialect::getModuleFlagKeyCGProfileName()) {
504  auto arrayAttr = dyn_cast<ArrayAttr>(value);
505  if ((!arrayAttr) || (!llvm::all_of(arrayAttr, [](Attribute attr) {
506  return isa<ModuleFlagCGProfileEntryAttr>(attr);
507  })))
508  return emitError()
509  << "'CG Profile' key expects an array of '#llvm.cgprofile_entry'";
510  return success();
511  }
512 
513  if (key == LLVMDialect::getModuleFlagKeyProfileSummaryName()) {
514  if (!isa<ModuleFlagProfileSummaryAttr>(value))
515  return emitError() << "'ProfileSummary' key expects a "
516  "'#llvm.profile_summary' attribute";
517  return success();
518  }
519 
520  if (isa<IntegerAttr, StringAttr>(value))
521  return success();
522 
523  return emitError() << "only integer and string values are currently "
524  "supported for unknown key '"
525  << key << "'";
526 }
static MLIRContext * getContext(OpFoldResult val)
static void printExpressionArg(AsmPrinter &printer, uint64_t opcode, ArrayRef< uint64_t > args)
Prints DWARF expression arguments with respect to the specific DWARF operation.
Definition: LLVMAttrs.cpp:284
static ParseResult parseExpressionArg(AsmParser &parser, uint64_t opcode, SmallVector< uint64_t > &args)
Parses DWARF expression arguments with respect to the DWARF operation opcode.
Definition: LLVMAttrs.cpp:248
static bool contains(SMRange range, SMLoc loc)
Returns true if the given range contains the given source location.
Definition: MLIRServer.cpp:111
static void print(spirv::VerCapExtAttr triple, DialectAsmPrinter &printer)
This base class exposes generic asm parser hooks, usable across the various derived parsers.
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
Definition: AsmPrinter.cpp:72
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual ParseResult parseLess()=0
Parse a '<' token.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
auto getChecked(SMLoc loc, ParamsT &&...params)
Invoke the getChecked method of the given Attribute or Type class, using the provided location to emi...
virtual ParseResult parseGreater()=0
Parse a '>' token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
This base class exposes generic asm printer hooks, usable across the various derived printers.
Attributes are known-constant values of operations.
Definition: Attributes.h:25
MLIRContext * getContext() const
Return the context this attribute belongs to.
Definition: Attributes.cpp:37
static BoolAttr get(MLIRContext *context, bool value)
An attribute that associates a referenced attribute with a unique identifier.
This class represents a diagnostic that is inflight and set to be reported.
Definition: Diagnostics.h:314
This class represents a LLVM attribute that describes a local debug info scope.
Definition: LLVMAttrs.h:48
static bool classof(Attribute attr)
Support LLVM type casting.
Definition: LLVMAttrs.cpp:184
static bool classof(Attribute attr)
Definition: LLVMAttrs.cpp:159
static bool classof(Attribute attr)
Support LLVM type casting.
Definition: LLVMAttrs.cpp:174
static bool classof(Attribute attr)
Support LLVM type casting.
Definition: LLVMAttrs.cpp:201
static bool classof(Attribute attr)
Support LLVM type casting.
Definition: LLVMAttrs.cpp:193
static bool classof(Attribute attr)
Support LLVM type casting.
Definition: LLVMAttrs.cpp:211
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:63
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition: Operation.h:238
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
bool isValidLoadStoreImpl(Type type, ptr::AtomicOrdering ordering, std::optional< int64_t > alignment, const ::mlir::DataLayout *dataLayout, function_ref< InFlightDiagnostic()> emitError)
Checks whether the given type is an LLVM type that can be loaded or stored.
Definition: LLVMAttrs.cpp:60
bool isLoadableType(Type type)
Returns true if the given type is a loadable type compatible with the LLVM dialect.
Definition: LLVMTypes.cpp:822
bool isTypeCompatibleWithAtomicOp(Type type, const DataLayout &dataLayout)
Returns true if the given type is supported by atomic operations.
FailureOr< Attribute > query(Operation *op, ArrayRef< DataLayoutEntryKey > keys, bool emitError=false)
Perform a DLTI-query at op, recursively querying each key of keys on query interface-implementing att...
Definition: DLTI.cpp:537
QueryRef parse(llvm::StringRef line, const QuerySession &qs)
Definition: Query.cpp:21
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition: Utils.cpp:304
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
LogicalResult verify(Operation *op, bool verifyRecursively=true)
Perform (potentially expensive) checks of invariants, used to detect compiler bugs,...
Definition: Verifier.cpp:423