MLIR  20.0.0git
DebugImporter.cpp
Go to the documentation of this file.
1 //===- DebugImporter.cpp - LLVM to MLIR Debug conversion ------------------===//
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 #include "DebugImporter.h"
11 #include "mlir/IR/Attributes.h"
13 #include "mlir/IR/Location.h"
14 #include "llvm/ADT/STLExtras.h"
15 #include "llvm/ADT/ScopeExit.h"
16 #include "llvm/ADT/SetOperations.h"
17 #include "llvm/ADT/TypeSwitch.h"
18 #include "llvm/BinaryFormat/Dwarf.h"
19 #include "llvm/IR/Constants.h"
20 #include "llvm/IR/DebugInfoMetadata.h"
21 #include "llvm/IR/Metadata.h"
22 #include "llvm/Support/Casting.h"
23 #include "llvm/Support/ErrorHandling.h"
24 
25 using namespace mlir;
26 using namespace mlir::LLVM;
27 using namespace mlir::LLVM::detail;
28 
29 DebugImporter::DebugImporter(ModuleOp mlirModule,
30  bool dropDICompositeTypeElements)
31  : cache([&](llvm::DINode *node) { return createRecSelf(node); }),
32  context(mlirModule.getContext()), mlirModule(mlirModule),
33  dropDICompositeTypeElements(dropDICompositeTypeElements) {}
34 
36  llvm::DISubprogram *subprogram = func->getSubprogram();
37  if (!subprogram)
38  return UnknownLoc::get(context);
39 
40  // Add a fused location to link the subprogram information.
41  StringAttr funcName = StringAttr::get(context, subprogram->getName());
42  StringAttr fileName = StringAttr::get(context, subprogram->getFilename());
44  {NameLoc::get(funcName),
45  FileLineColLoc::get(fileName, subprogram->getLine(), /*column=*/0)},
46  translate(subprogram), context);
47 }
48 
49 //===----------------------------------------------------------------------===//
50 // Attributes
51 //===----------------------------------------------------------------------===//
52 
53 DIBasicTypeAttr DebugImporter::translateImpl(llvm::DIBasicType *node) {
54  return DIBasicTypeAttr::get(context, node->getTag(), node->getName(),
55  node->getSizeInBits(), node->getEncoding());
56 }
57 
58 DICompileUnitAttr DebugImporter::translateImpl(llvm::DICompileUnit *node) {
59  std::optional<DIEmissionKind> emissionKind =
60  symbolizeDIEmissionKind(node->getEmissionKind());
61  std::optional<DINameTableKind> nameTableKind = symbolizeDINameTableKind(
62  static_cast<
63  std::underlying_type_t<llvm::DICompileUnit::DebugNameTableKind>>(
64  node->getNameTableKind()));
66  context, getOrCreateDistinctID(node), node->getSourceLanguage(),
67  translate(node->getFile()), getStringAttrOrNull(node->getRawProducer()),
68  node->isOptimized(), emissionKind.value(), nameTableKind.value());
69 }
70 
71 DICompositeTypeAttr DebugImporter::translateImpl(llvm::DICompositeType *node) {
72  std::optional<DIFlags> flags = symbolizeDIFlags(node->getFlags());
73  SmallVector<DINodeAttr> elements;
74 
75  // A vector always requires an element.
76  bool isVectorType = flags && bitEnumContainsAll(*flags, DIFlags::Vector);
77  if (isVectorType || !dropDICompositeTypeElements) {
78  for (llvm::DINode *element : node->getElements()) {
79  assert(element && "expected a non-null element type");
80  elements.push_back(translate(element));
81  }
82  }
83  // Drop the elements parameter if any of the elements are invalid.
84  if (llvm::is_contained(elements, nullptr))
85  elements.clear();
86  DITypeAttr baseType = translate(node->getBaseType());
87  // Arrays require a base type, otherwise the debug metadata is considered to
88  // be malformed.
89  if (node->getTag() == llvm::dwarf::DW_TAG_array_type && !baseType)
90  return nullptr;
92  context, node->getTag(), getStringAttrOrNull(node->getRawName()),
93  translate(node->getFile()), node->getLine(), translate(node->getScope()),
94  baseType, flags.value_or(DIFlags::Zero), node->getSizeInBits(),
95  node->getAlignInBits(), elements,
96  translateExpression(node->getDataLocationExp()),
97  translateExpression(node->getRankExp()),
98  translateExpression(node->getAllocatedExp()),
99  translateExpression(node->getAssociatedExp()));
100 }
101 
102 DIDerivedTypeAttr DebugImporter::translateImpl(llvm::DIDerivedType *node) {
103  // Return nullptr if the base type is invalid.
104  DITypeAttr baseType = translate(node->getBaseType());
105  if (node->getBaseType() && !baseType)
106  return nullptr;
107  DINodeAttr extraData =
108  translate(dyn_cast_or_null<llvm::DINode>(node->getExtraData()));
109  return DIDerivedTypeAttr::get(
110  context, node->getTag(), getStringAttrOrNull(node->getRawName()),
111  baseType, node->getSizeInBits(), node->getAlignInBits(),
112  node->getOffsetInBits(), node->getDWARFAddressSpace(), extraData);
113 }
114 
115 DIStringTypeAttr DebugImporter::translateImpl(llvm::DIStringType *node) {
116  return DIStringTypeAttr::get(
117  context, node->getTag(), getStringAttrOrNull(node->getRawName()),
118  node->getSizeInBits(), node->getAlignInBits(),
119  translate(node->getStringLength()),
120  translateExpression(node->getStringLengthExp()),
121  translateExpression(node->getStringLocationExp()), node->getEncoding());
122 }
123 
124 DIFileAttr DebugImporter::translateImpl(llvm::DIFile *node) {
125  return DIFileAttr::get(context, node->getFilename(), node->getDirectory());
126 }
127 
128 DILabelAttr DebugImporter::translateImpl(llvm::DILabel *node) {
129  // Return nullptr if the scope or type is a cyclic dependency.
130  DIScopeAttr scope = translate(node->getScope());
131  if (node->getScope() && !scope)
132  return nullptr;
133  return DILabelAttr::get(context, scope,
134  getStringAttrOrNull(node->getRawName()),
135  translate(node->getFile()), node->getLine());
136 }
137 
138 DILexicalBlockAttr DebugImporter::translateImpl(llvm::DILexicalBlock *node) {
139  // Return nullptr if the scope or type is a cyclic dependency.
140  DIScopeAttr scope = translate(node->getScope());
141  if (node->getScope() && !scope)
142  return nullptr;
143  return DILexicalBlockAttr::get(context, scope, translate(node->getFile()),
144  node->getLine(), node->getColumn());
145 }
146 
147 DILexicalBlockFileAttr
148 DebugImporter::translateImpl(llvm::DILexicalBlockFile *node) {
149  // Return nullptr if the scope or type is a cyclic dependency.
150  DIScopeAttr scope = translate(node->getScope());
151  if (node->getScope() && !scope)
152  return nullptr;
153  return DILexicalBlockFileAttr::get(context, scope, translate(node->getFile()),
154  node->getDiscriminator());
155 }
156 
157 DIGlobalVariableAttr
158 DebugImporter::translateImpl(llvm::DIGlobalVariable *node) {
159  // Names of DIGlobalVariables can be empty. MLIR models them as null, instead
160  // of empty strings, so this special handling is necessary.
161  auto convertToStringAttr = [&](StringRef name) -> StringAttr {
162  if (name.empty())
163  return {};
164  return StringAttr::get(context, node->getName());
165  };
167  context, translate(node->getScope()),
168  convertToStringAttr(node->getName()),
169  convertToStringAttr(node->getLinkageName()), translate(node->getFile()),
170  node->getLine(), translate(node->getType()), node->isLocalToUnit(),
171  node->isDefinition(), node->getAlignInBits());
172 }
173 
174 DILocalVariableAttr DebugImporter::translateImpl(llvm::DILocalVariable *node) {
175  // Return nullptr if the scope or type is a cyclic dependency.
176  DIScopeAttr scope = translate(node->getScope());
177  if (node->getScope() && !scope)
178  return nullptr;
180  context, scope, getStringAttrOrNull(node->getRawName()),
181  translate(node->getFile()), node->getLine(), node->getArg(),
182  node->getAlignInBits(), translate(node->getType()),
183  symbolizeDIFlags(node->getFlags()).value_or(DIFlags::Zero));
184 }
185 
186 DIVariableAttr DebugImporter::translateImpl(llvm::DIVariable *node) {
187  return cast<DIVariableAttr>(translate(static_cast<llvm::DINode *>(node)));
188 }
189 
190 DIScopeAttr DebugImporter::translateImpl(llvm::DIScope *node) {
191  return cast<DIScopeAttr>(translate(static_cast<llvm::DINode *>(node)));
192 }
193 
194 DIModuleAttr DebugImporter::translateImpl(llvm::DIModule *node) {
195  return DIModuleAttr::get(
196  context, translate(node->getFile()), translate(node->getScope()),
197  getStringAttrOrNull(node->getRawName()),
198  getStringAttrOrNull(node->getRawConfigurationMacros()),
199  getStringAttrOrNull(node->getRawIncludePath()),
200  getStringAttrOrNull(node->getRawAPINotesFile()), node->getLineNo(),
201  node->getIsDecl());
202 }
203 
204 DINamespaceAttr DebugImporter::translateImpl(llvm::DINamespace *node) {
205  return DINamespaceAttr::get(context, getStringAttrOrNull(node->getRawName()),
206  translate(node->getScope()),
207  node->getExportSymbols());
208 }
209 
210 DIImportedEntityAttr
211 DebugImporter::translateImpl(llvm::DIImportedEntity *node) {
212  SmallVector<DINodeAttr> elements;
213  for (llvm::DINode *element : node->getElements()) {
214  assert(element && "expected a non-null element type");
215  elements.push_back(translate(element));
216  }
217 
219  context, node->getTag(), translate(node->getScope()),
220  translate(node->getEntity()), translate(node->getFile()), node->getLine(),
221  getStringAttrOrNull(node->getRawName()), elements);
222 }
223 
224 DISubprogramAttr DebugImporter::translateImpl(llvm::DISubprogram *node) {
225  // Only definitions require a distinct identifier.
227  if (node->isDistinct())
228  id = getOrCreateDistinctID(node);
229 
230  // Return nullptr if the scope or type is invalid.
231  DIScopeAttr scope = translate(node->getScope());
232  if (node->getScope() && !scope)
233  return nullptr;
234  std::optional<DISubprogramFlags> subprogramFlags =
235  symbolizeDISubprogramFlags(node->getSubprogram()->getSPFlags());
236  assert(subprogramFlags && "expected valid subprogram flags");
237  DISubroutineTypeAttr type = translate(node->getType());
238  if (node->getType() && !type)
239  return nullptr;
240 
241  // Convert the retained nodes but drop all of them if one of them is invalid.
242  SmallVector<DINodeAttr> retainedNodes;
243  for (llvm::DINode *retainedNode : node->getRetainedNodes())
244  retainedNodes.push_back(translate(retainedNode));
245  if (llvm::is_contained(retainedNodes, nullptr))
246  retainedNodes.clear();
247 
248  SmallVector<DINodeAttr> annotations;
249  // We currently only support `string` values for annotations on the MLIR side.
250  // Theoretically we could support other primitives, but LLVM is not using
251  // other types in practice.
252  if (llvm::DINodeArray rawAnns = node->getAnnotations(); rawAnns) {
253  for (size_t i = 0, e = rawAnns->getNumOperands(); i < e; ++i) {
254  const llvm::MDTuple *tuple = cast<llvm::MDTuple>(rawAnns->getOperand(i));
255  if (tuple->getNumOperands() != 2)
256  continue;
257  const llvm::MDString *name = cast<llvm::MDString>(tuple->getOperand(0));
258  const llvm::MDString *value =
259  dyn_cast<llvm::MDString>(tuple->getOperand(1));
260  if (name && value) {
261  annotations.push_back(DIAnnotationAttr::get(
262  context, StringAttr::get(context, name->getString()),
263  StringAttr::get(context, value->getString())));
264  }
265  }
266  }
267 
268  return DISubprogramAttr::get(context, id, translate(node->getUnit()), scope,
269  getStringAttrOrNull(node->getRawName()),
270  getStringAttrOrNull(node->getRawLinkageName()),
271  translate(node->getFile()), node->getLine(),
272  node->getScopeLine(), *subprogramFlags, type,
273  retainedNodes, annotations);
274 }
275 
276 DISubrangeAttr DebugImporter::translateImpl(llvm::DISubrange *node) {
277  auto getAttrOrNull = [&](llvm::DISubrange::BoundType data) -> Attribute {
278  if (data.isNull())
279  return nullptr;
280  if (auto *constInt = dyn_cast<llvm::ConstantInt *>(data))
281  return IntegerAttr::get(IntegerType::get(context, 64),
282  constInt->getSExtValue());
283  if (auto *expr = dyn_cast<llvm::DIExpression *>(data))
284  return translateExpression(expr);
285  if (auto *var = dyn_cast<llvm::DIVariable *>(data)) {
286  if (auto *local = dyn_cast<llvm::DILocalVariable>(var))
287  return translate(local);
288  if (auto *global = dyn_cast<llvm::DIGlobalVariable>(var))
289  return translate(global);
290  return nullptr;
291  }
292  return nullptr;
293  };
294  Attribute count = getAttrOrNull(node->getCount());
295  Attribute upperBound = getAttrOrNull(node->getUpperBound());
296  // Either count or the upper bound needs to be present. Otherwise, the
297  // metadata is invalid. The conversion might fail due to unsupported DI nodes.
298  if (!count && !upperBound)
299  return {};
300  return DISubrangeAttr::get(context, count,
301  getAttrOrNull(node->getLowerBound()), upperBound,
302  getAttrOrNull(node->getStride()));
303 }
304 
305 DICommonBlockAttr DebugImporter::translateImpl(llvm::DICommonBlock *node) {
306  return DICommonBlockAttr::get(context, translate(node->getScope()),
307  translate(node->getDecl()),
308  getStringAttrOrNull(node->getRawName()),
309  translate(node->getFile()), node->getLineNo());
310 }
311 
312 DIGenericSubrangeAttr
313 DebugImporter::translateImpl(llvm::DIGenericSubrange *node) {
314  auto getAttrOrNull =
316  if (data.isNull())
317  return nullptr;
318  if (auto *expr = dyn_cast<llvm::DIExpression *>(data))
319  return translateExpression(expr);
320  if (auto *var = dyn_cast<llvm::DIVariable *>(data)) {
321  if (auto *local = dyn_cast<llvm::DILocalVariable>(var))
322  return translate(local);
323  if (auto *global = dyn_cast<llvm::DIGlobalVariable>(var))
324  return translate(global);
325  return nullptr;
326  }
327  return nullptr;
328  };
329  Attribute count = getAttrOrNull(node->getCount());
330  Attribute upperBound = getAttrOrNull(node->getUpperBound());
331  Attribute lowerBound = getAttrOrNull(node->getLowerBound());
332  Attribute stride = getAttrOrNull(node->getStride());
333  // Either count or the upper bound needs to be present. Otherwise, the
334  // metadata is invalid.
335  if (!count && !upperBound)
336  return {};
337  return DIGenericSubrangeAttr::get(context, count, lowerBound, upperBound,
338  stride);
339 }
340 
341 DISubroutineTypeAttr
342 DebugImporter::translateImpl(llvm::DISubroutineType *node) {
344  for (llvm::DIType *type : node->getTypeArray()) {
345  if (!type) {
346  // A nullptr entry may appear at the beginning or the end of the
347  // subroutine types list modeling either a void result type or the type of
348  // a variadic argument. Translate the nullptr to an explicit
349  // DINullTypeAttr since the attribute list cannot contain a nullptr entry.
350  types.push_back(DINullTypeAttr::get(context));
351  continue;
352  }
353  types.push_back(translate(type));
354  }
355  // Return nullptr if any of the types is invalid.
356  if (llvm::is_contained(types, nullptr))
357  return nullptr;
358  return DISubroutineTypeAttr::get(context, node->getCC(), types);
359 }
360 
361 DITypeAttr DebugImporter::translateImpl(llvm::DIType *node) {
362  return cast<DITypeAttr>(translate(static_cast<llvm::DINode *>(node)));
363 }
364 
366  if (!node)
367  return nullptr;
368 
369  // Check for a cached instance.
370  auto cacheEntry = cache.lookupOrInit(node);
371  if (std::optional<DINodeAttr> result = cacheEntry.get())
372  return *result;
373 
374  // Convert the debug metadata if possible.
375  auto translateNode = [this](llvm::DINode *node) -> DINodeAttr {
376  if (auto *casted = dyn_cast<llvm::DIBasicType>(node))
377  return translateImpl(casted);
378  if (auto *casted = dyn_cast<llvm::DICommonBlock>(node))
379  return translateImpl(casted);
380  if (auto *casted = dyn_cast<llvm::DICompileUnit>(node))
381  return translateImpl(casted);
382  if (auto *casted = dyn_cast<llvm::DICompositeType>(node))
383  return translateImpl(casted);
384  if (auto *casted = dyn_cast<llvm::DIDerivedType>(node))
385  return translateImpl(casted);
386  if (auto *casted = dyn_cast<llvm::DIStringType>(node))
387  return translateImpl(casted);
388  if (auto *casted = dyn_cast<llvm::DIFile>(node))
389  return translateImpl(casted);
390  if (auto *casted = dyn_cast<llvm::DIGlobalVariable>(node))
391  return translateImpl(casted);
392  if (auto *casted = dyn_cast<llvm::DIImportedEntity>(node))
393  return translateImpl(casted);
394  if (auto *casted = dyn_cast<llvm::DILabel>(node))
395  return translateImpl(casted);
396  if (auto *casted = dyn_cast<llvm::DILexicalBlock>(node))
397  return translateImpl(casted);
398  if (auto *casted = dyn_cast<llvm::DILexicalBlockFile>(node))
399  return translateImpl(casted);
400  if (auto *casted = dyn_cast<llvm::DILocalVariable>(node))
401  return translateImpl(casted);
402  if (auto *casted = dyn_cast<llvm::DIModule>(node))
403  return translateImpl(casted);
404  if (auto *casted = dyn_cast<llvm::DINamespace>(node))
405  return translateImpl(casted);
406  if (auto *casted = dyn_cast<llvm::DISubprogram>(node))
407  return translateImpl(casted);
408  if (auto *casted = dyn_cast<llvm::DISubrange>(node))
409  return translateImpl(casted);
410  if (auto *casted = dyn_cast<llvm::DIGenericSubrange>(node))
411  return translateImpl(casted);
412  if (auto *casted = dyn_cast<llvm::DISubroutineType>(node))
413  return translateImpl(casted);
414  return nullptr;
415  };
416  if (DINodeAttr attr = translateNode(node)) {
417  // If this node was repeated, lookup its recursive ID and assign it to the
418  // base result.
419  if (cacheEntry.wasRepeated()) {
420  DistinctAttr recId = nodeToRecId.lookup(node);
421  auto recType = cast<DIRecursiveTypeAttrInterface>(attr);
422  attr = cast<DINodeAttr>(recType.withRecId(recId));
423  }
424  cacheEntry.resolve(attr);
425  return attr;
426  }
427  cacheEntry.resolve(nullptr);
428  return nullptr;
429 }
430 
431 /// Get the `getRecSelf` constructor for the translated type of `node` if its
432 /// translated DITypeAttr supports recursion. Otherwise, returns nullptr.
433 static function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>
434 getRecSelfConstructor(llvm::DINode *node) {
435  using CtorType = function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>;
437  .Case([&](llvm::DICompositeType *) {
438  return CtorType(DICompositeTypeAttr::getRecSelf);
439  })
440  .Case([&](llvm::DISubprogram *) {
441  return CtorType(DISubprogramAttr::getRecSelf);
442  })
443  .Default(CtorType());
444 }
445 
446 std::optional<DINodeAttr> DebugImporter::createRecSelf(llvm::DINode *node) {
447  auto recSelfCtor = getRecSelfConstructor(node);
448  if (!recSelfCtor)
449  return std::nullopt;
450 
451  // The original node may have already been assigned a recursive ID from
452  // a different self-reference. Use that if possible.
453  DistinctAttr recId = nodeToRecId.lookup(node);
454  if (!recId) {
455  recId = DistinctAttr::create(UnitAttr::get(context));
456  nodeToRecId[node] = recId;
457  }
458  DIRecursiveTypeAttrInterface recSelf = recSelfCtor(recId);
459  return cast<DINodeAttr>(recSelf);
460 }
461 
462 //===----------------------------------------------------------------------===//
463 // Locations
464 //===----------------------------------------------------------------------===//
465 
466 Location DebugImporter::translateLoc(llvm::DILocation *loc) {
467  if (!loc)
468  return UnknownLoc::get(context);
469 
470  // Get the file location of the instruction.
471  Location result = FileLineColLoc::get(context, loc->getFilename(),
472  loc->getLine(), loc->getColumn());
473 
474  // Add scope information.
475  assert(loc->getScope() && "expected non-null scope");
476  result = FusedLocWith<DIScopeAttr>::get({result}, translate(loc->getScope()),
477  context);
478 
479  // Add call site information, if available.
480  if (llvm::DILocation *inlinedAt = loc->getInlinedAt())
481  result = CallSiteLoc::get(result, translateLoc(inlinedAt));
482 
483  return result;
484 }
485 
486 DIExpressionAttr DebugImporter::translateExpression(llvm::DIExpression *node) {
487  if (!node)
488  return nullptr;
489 
491 
492  // Begin processing the operations.
493  for (const llvm::DIExpression::ExprOperand &op : node->expr_ops()) {
494  SmallVector<uint64_t> operands;
495  operands.reserve(op.getNumArgs());
496  for (const auto &i : llvm::seq(op.getNumArgs()))
497  operands.push_back(op.getArg(i));
498  const auto attr = DIExpressionElemAttr::get(context, op.getOp(), operands);
499  ops.push_back(attr);
500  }
501  return DIExpressionAttr::get(context, ops);
502 }
503 
505  llvm::DIGlobalVariableExpression *node) {
507  context, translate(node->getVariable()),
508  translateExpression(node->getExpression()));
509 }
510 
511 StringAttr DebugImporter::getStringAttrOrNull(llvm::MDString *stringNode) {
512  if (!stringNode)
513  return StringAttr();
514  return StringAttr::get(context, stringNode->getString());
515 }
516 
517 DistinctAttr DebugImporter::getOrCreateDistinctID(llvm::DINode *node) {
518  DistinctAttr &id = nodeToDistinctAttr[node];
519  if (!id)
520  id = DistinctAttr::create(UnitAttr::get(context));
521  return id;
522 }
static function_ref< DIRecursiveTypeAttrInterface(DistinctAttr)> getRecSelfConstructor(llvm::DINode *node)
Get the getRecSelf constructor for the translated type of node if its translated DITypeAttr supports ...
Attributes are known-constant values of operations.
Definition: Attributes.h:25
An attribute that associates a referenced attribute with a unique identifier.
static DistinctAttr create(Attribute referencedAttr)
Creates a distinct attribute that associates a referenced attribute with a unique identifier.
This class represents the base attribute for all debug info attributes.
Definition: LLVMAttrs.h:27
This class represents a LLVM attribute that describes a debug info scope.
Definition: LLVMAttrs.h:36
This class represents a LLVM attribute that describes a debug info type.
Definition: LLVMAttrs.h:55
This class represents a LLVM attribute that describes a debug info variable.
Definition: LLVMAttrs.h:64
DINodeAttr translate(llvm::DINode *node)
Translates the given LLVM debug metadata to MLIR.
DIExpressionAttr translateExpression(llvm::DIExpression *node)
Translates the LLVM DWARF expression metadata to MLIR.
DIGlobalVariableExpressionAttr translateGlobalVariableExpression(llvm::DIGlobalVariableExpression *node)
Translates the LLVM DWARF global variable expression metadata to MLIR.
Location translateLoc(llvm::DILocation *loc)
Translates the given LLVM debug location to an MLIR location.
Location translateFuncLocation(llvm::Function *func)
Translates the debug information for the given function into a Location.
DebugImporter(ModuleOp mlirModule, bool dropDICompositeTypeElements)
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:66
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition: CallGraph.h:229
BoundType
The type of bound: equal, lower bound or upper bound.
Include the generated interface declarations.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...