CIRCT  19.0.0git
ArcOps.cpp
Go to the documentation of this file.
1 //===- ArcOps.cpp ---------------------------------------------------------===//
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 
11 #include "mlir/IR/Builders.h"
12 #include "mlir/IR/OpImplementation.h"
13 #include "mlir/IR/PatternMatch.h"
14 #include "mlir/IR/SymbolTable.h"
15 #include "mlir/Interfaces/FunctionImplementation.h"
16 #include "mlir/Interfaces/SideEffectInterfaces.h"
17 #include "llvm/ADT/SmallPtrSet.h"
18 #include "llvm/ADT/TypeSwitch.h"
19 
20 using namespace circt;
21 using namespace arc;
22 using namespace mlir;
23 
24 //===----------------------------------------------------------------------===//
25 // Helpers
26 //===----------------------------------------------------------------------===//
27 
28 static LogicalResult verifyTypeListEquivalence(Operation *op,
29  TypeRange expectedTypeList,
30  TypeRange actualTypeList,
31  StringRef elementName) {
32  if (expectedTypeList.size() != actualTypeList.size())
33  return op->emitOpError("incorrect number of ")
34  << elementName << "s: expected " << expectedTypeList.size()
35  << ", but got " << actualTypeList.size();
36 
37  for (unsigned i = 0, e = expectedTypeList.size(); i != e; ++i) {
38  if (expectedTypeList[i] != actualTypeList[i]) {
39  auto diag = op->emitOpError(elementName)
40  << " type mismatch: " << elementName << " #" << i;
41  diag.attachNote() << "expected type: " << expectedTypeList[i];
42  diag.attachNote() << " actual type: " << actualTypeList[i];
43  return diag;
44  }
45  }
46 
47  return success();
48 }
49 
50 static LogicalResult verifyArcSymbolUse(Operation *op, TypeRange inputs,
51  TypeRange results,
52  SymbolTableCollection &symbolTable) {
53  // Check that the arc attribute was specified.
54  auto arcName = op->getAttrOfType<FlatSymbolRefAttr>("arc");
55  // The arc attribute is verified by the tablegen generated verifier as it is
56  // an ODS defined attribute.
57  assert(arcName && "FlatSymbolRefAttr called 'arc' missing");
58  DefineOp arc = symbolTable.lookupNearestSymbolFrom<DefineOp>(op, arcName);
59  if (!arc)
60  return op->emitOpError() << "`" << arcName.getValue()
61  << "` does not reference a valid `arc.define`";
62 
63  // Verify that the operand and result types match the arc.
64  auto type = arc.getFunctionType();
65  if (failed(
66  verifyTypeListEquivalence(op, type.getInputs(), inputs, "operand")))
67  return failure();
68 
69  if (failed(
70  verifyTypeListEquivalence(op, type.getResults(), results, "result")))
71  return failure();
72 
73  return success();
74 }
75 
76 static bool isSupportedModuleOp(Operation *moduleOp) {
77  return llvm::isa<arc::ModelOp, hw::HWModuleLike>(moduleOp);
78 }
79 
80 /// Fetches the operation pointed to by `pointing` with name `symbol`, checking
81 /// that it is a supported model operation for simulation.
82 static Operation *getSupportedModuleOp(SymbolTableCollection &symbolTable,
83  Operation *pointing, StringAttr symbol) {
84  Operation *moduleOp = symbolTable.lookupNearestSymbolFrom(pointing, symbol);
85  if (!moduleOp) {
86  pointing->emitOpError("model not found");
87  return nullptr;
88  }
89 
90  if (!isSupportedModuleOp(moduleOp)) {
91  pointing->emitOpError("model symbol does not point to a supported model "
92  "operation, points to ")
93  << moduleOp->getName() << " instead";
94  return nullptr;
95  }
96 
97  return moduleOp;
98 }
99 
100 static std::optional<hw::ModulePort> getModulePort(Operation *moduleOp,
101  StringRef portName) {
102  auto findRightPort = [&](auto ports) -> std::optional<hw::ModulePort> {
103  const hw::ModulePort *port = llvm::find_if(
104  ports, [&](hw::ModulePort port) { return port.name == portName; });
105  if (port == ports.end())
106  return std::nullopt;
107  return *port;
108  };
109 
110  return TypeSwitch<Operation *, std::optional<hw::ModulePort>>(moduleOp)
111  .Case<arc::ModelOp>(
112  [&](arc::ModelOp modelOp) -> std::optional<hw::ModulePort> {
113  return findRightPort(modelOp.getIo().getPorts());
114  })
115  .Case<hw::HWModuleLike>(
116  [&](hw::HWModuleLike moduleLike) -> std::optional<hw::ModulePort> {
117  return findRightPort(moduleLike.getPortList());
118  })
119  .Default([](Operation *) { return std::nullopt; });
120 }
121 
122 //===----------------------------------------------------------------------===//
123 // DefineOp
124 //===----------------------------------------------------------------------===//
125 
126 ParseResult DefineOp::parse(OpAsmParser &parser, OperationState &result) {
127  auto buildFuncType =
128  [](Builder &builder, ArrayRef<Type> argTypes, ArrayRef<Type> results,
129  function_interface_impl::VariadicFlag,
130  std::string &) { return builder.getFunctionType(argTypes, results); };
131 
132  return function_interface_impl::parseFunctionOp(
133  parser, result, /*allowVariadic=*/false,
134  getFunctionTypeAttrName(result.name), buildFuncType,
135  getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
136 }
137 
138 void DefineOp::print(OpAsmPrinter &p) {
139  function_interface_impl::printFunctionOp(
140  p, *this, /*isVariadic=*/false, "function_type", getArgAttrsAttrName(),
141  getResAttrsAttrName());
142 }
143 
144 LogicalResult DefineOp::verifyRegions() {
145  // Check that the body does not contain any side-effecting operations. We can
146  // simply iterate over the ops directly within the body; operations with
147  // regions, like scf::IfOp, implement the `HasRecursiveMemoryEffects` trait
148  // which causes the `isMemoryEffectFree` check to already recur into their
149  // regions.
150  for (auto &op : getBodyBlock()) {
151  if (isMemoryEffectFree(&op))
152  continue;
153 
154  // We don't use a op-error here because that leads to the whole arc being
155  // printed. This can be switched of when creating the context, but one
156  // might not want to switch that off for other error messages. Here it's
157  // definitely not desirable as arcs can be very big and would fill up the
158  // error log, making it hard to read. Currently, only the signature (first
159  // line) of the arc is printed.
160  auto diag = mlir::emitError(getLoc(), "body contains non-pure operation");
161  diag.attachNote(op.getLoc()).append("first non-pure operation here: ");
162  return diag;
163  }
164  return success();
165 }
166 
167 bool DefineOp::isPassthrough() {
168  if (getNumArguments() != getNumResults())
169  return false;
170 
171  return llvm::all_of(
172  llvm::zip(getArguments(), getBodyBlock().getTerminator()->getOperands()),
173  [](const auto &argAndRes) {
174  return std::get<0>(argAndRes) == std::get<1>(argAndRes);
175  });
176 }
177 
178 //===----------------------------------------------------------------------===//
179 // OutputOp
180 //===----------------------------------------------------------------------===//
181 
182 LogicalResult OutputOp::verify() {
183  auto *parent = (*this)->getParentOp();
184  TypeRange expectedTypes = parent->getResultTypes();
185  if (auto defOp = dyn_cast<DefineOp>(parent))
186  expectedTypes = defOp.getResultTypes();
187 
188  TypeRange actualTypes = getOperands().getTypes();
189  return verifyTypeListEquivalence(*this, expectedTypes, actualTypes, "output");
190 }
191 
192 //===----------------------------------------------------------------------===//
193 // StateOp
194 //===----------------------------------------------------------------------===//
195 
196 LogicalResult StateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
197  return verifyArcSymbolUse(*this, getInputs().getTypes(),
198  getResults().getTypes(), symbolTable);
199 }
200 
201 LogicalResult StateOp::verify() {
202  if (getLatency() < 1)
203  return emitOpError("latency must be a positive integer");
204 
205  if (!getOperation()->getParentOfType<ClockDomainOp>() && !getClock())
206  return emitOpError("outside a clock domain requires a clock");
207 
208  if (getOperation()->getParentOfType<ClockDomainOp>() && getClock())
209  return emitOpError("inside a clock domain cannot have a clock");
210 
211  return success();
212 }
213 
214 //===----------------------------------------------------------------------===//
215 // CallOp
216 //===----------------------------------------------------------------------===//
217 
218 LogicalResult CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
219  return verifyArcSymbolUse(*this, getInputs().getTypes(),
220  getResults().getTypes(), symbolTable);
221 }
222 
223 bool CallOp::isClocked() { return false; }
224 
225 Value CallOp::getClock() { return Value{}; }
226 
227 void CallOp::eraseClock() {}
228 
229 uint32_t CallOp::getLatency() { return 0; }
230 
231 //===----------------------------------------------------------------------===//
232 // MemoryWritePortOp
233 //===----------------------------------------------------------------------===//
234 
235 SmallVector<Type> MemoryWritePortOp::getArcResultTypes() {
236  auto memType = cast<MemoryType>(getMemory().getType());
237  SmallVector<Type> resultTypes{memType.getAddressType(),
238  memType.getWordType()};
239  if (getEnable())
240  resultTypes.push_back(IntegerType::get(getContext(), 1));
241  if (getMask())
242  resultTypes.push_back(memType.getWordType());
243  return resultTypes;
244 }
245 
246 LogicalResult
247 MemoryWritePortOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
248  return verifyArcSymbolUse(*this, getInputs().getTypes(), getArcResultTypes(),
249  symbolTable);
250 }
251 
252 LogicalResult MemoryWritePortOp::verify() {
253  if (getLatency() < 1)
254  return emitOpError("latency must be at least 1");
255 
256  if (!getOperation()->getParentOfType<ClockDomainOp>() && !getClock())
257  return emitOpError("outside a clock domain requires a clock");
258 
259  if (getOperation()->getParentOfType<ClockDomainOp>() && getClock())
260  return emitOpError("inside a clock domain cannot have a clock");
261 
262  return success();
263 }
264 
265 //===----------------------------------------------------------------------===//
266 // ClockDomainOp
267 //===----------------------------------------------------------------------===//
268 
269 LogicalResult ClockDomainOp::verifyRegions() {
270  return verifyTypeListEquivalence(*this, getBodyBlock().getArgumentTypes(),
271  getInputs().getTypes(), "input");
272 }
273 
274 //===----------------------------------------------------------------------===//
275 // RootInputOp
276 //===----------------------------------------------------------------------===//
277 
279  SmallString<32> buf("in_");
280  buf += getName();
281  setNameFn(getState(), buf);
282 }
283 
284 //===----------------------------------------------------------------------===//
285 // RootOutputOp
286 //===----------------------------------------------------------------------===//
287 
289  SmallString<32> buf("out_");
290  buf += getName();
291  setNameFn(getState(), buf);
292 }
293 
294 //===----------------------------------------------------------------------===//
295 // ModelOp
296 //===----------------------------------------------------------------------===//
297 
298 LogicalResult ModelOp::verify() {
299  if (getBodyBlock().getArguments().size() != 1)
300  return emitOpError("must have exactly one argument");
301  if (auto type = getBodyBlock().getArgument(0).getType();
302  !isa<StorageType>(type))
303  return emitOpError("argument must be of storage type");
304  for (const hw::ModulePort &port : getIo().getPorts())
305  if (port.dir == hw::ModulePort::Direction::InOut)
306  return emitOpError("inout ports are not supported");
307  return success();
308 }
309 
310 //===----------------------------------------------------------------------===//
311 // LutOp
312 //===----------------------------------------------------------------------===//
313 
314 LogicalResult LutOp::verify() {
315  Location firstSideEffectOpLoc = UnknownLoc::get(getContext());
316  const WalkResult result = getBody().walk([&](Operation *op) {
317  if (auto memOp = dyn_cast<MemoryEffectOpInterface>(op)) {
318  SmallVector<SideEffects::EffectInstance<MemoryEffects::Effect>> effects;
319  memOp.getEffects(effects);
320 
321  if (!effects.empty()) {
322  firstSideEffectOpLoc = memOp->getLoc();
323  return WalkResult::interrupt();
324  }
325  }
326 
327  return WalkResult::advance();
328  });
329 
330  if (result.wasInterrupted())
331  return emitOpError("no operations with side-effects allowed inside a LUT")
332  .attachNote(firstSideEffectOpLoc)
333  << "first operation with side-effects here";
334 
335  return success();
336 }
337 
338 //===----------------------------------------------------------------------===//
339 // VectorizeOp
340 //===----------------------------------------------------------------------===//
341 
342 LogicalResult VectorizeOp::verify() {
343  if (getInputs().empty())
344  return emitOpError("there has to be at least one input vector");
345 
346  if (!llvm::all_equal(llvm::map_range(
347  getInputs(), [](OperandRange range) { return range.size(); })))
348  return emitOpError("all input vectors must have the same size");
349 
350  for (OperandRange range : getInputs()) {
351  if (!llvm::all_equal(range.getTypes()))
352  return emitOpError("all input vector lane types must match");
353 
354  if (range.empty())
355  return emitOpError("input vector must have at least one element");
356  }
357 
358  if (getInputs().front().size() > 1 &&
359  !isa<IntegerType>(getInputs().front().front().getType()))
360  return emitOpError("input vector element type must be a signless integer");
361 
362  if (getResults().empty())
363  return emitOpError("must have at least one result");
364 
365  if (!llvm::all_equal(getResults().getTypes()))
366  return emitOpError("all result types must match");
367 
368  if (getResults().size() != getInputs().front().size())
369  return emitOpError("number results must match input vector size");
370 
371  if (getResults().size() > 1 &&
372  !isa<IntegerType>(getResults().front().getType()))
373  return emitError(
374  "may only return a vector type if boundary is already vectorized");
375 
376  return success();
377 }
378 
379 static FailureOr<unsigned> getVectorWidth(Type base, Type vectorized) {
380  if (isa<VectorType>(base))
381  return failure();
382 
383  if (auto vectorTy = dyn_cast<VectorType>(vectorized)) {
384  if (vectorTy.getElementType() != base)
385  return failure();
386 
387  return vectorTy.getDimSize(0);
388  }
389 
390  if (vectorized.getIntOrFloatBitWidth() < base.getIntOrFloatBitWidth())
391  return failure();
392 
393  if (vectorized.getIntOrFloatBitWidth() % base.getIntOrFloatBitWidth() == 0)
394  return vectorized.getIntOrFloatBitWidth() / base.getIntOrFloatBitWidth();
395 
396  return failure();
397 }
398 
399 LogicalResult VectorizeOp::verifyRegions() {
400  auto returnOp = cast<VectorizeReturnOp>(getBody().front().getTerminator());
401  TypeRange bodyArgTypes = getBody().front().getArgumentTypes();
402 
403  if (bodyArgTypes.size() != getInputs().size())
404  return emitOpError(
405  "number of block arguments must match number of input vectors");
406 
407  // Boundary and body are vectorized, or both are not vectorized
408  if (returnOp.getValue().getType() == getResultTypes().front()) {
409  for (auto [i, argTy] : llvm::enumerate(bodyArgTypes))
410  if (argTy != getInputs()[i].getTypes().front())
411  return emitOpError("if terminator type matches result type the "
412  "argument types must match the input types");
413 
414  return success();
415  }
416 
417  // Boundary is vectorized, body is not
418  if (auto width = getVectorWidth(returnOp.getValue().getType(),
419  getResultTypes().front());
420  succeeded(width)) {
421  for (auto [i, argTy] : llvm::enumerate(bodyArgTypes)) {
422  Type inputTy = getInputs()[i].getTypes().front();
423  FailureOr<unsigned> argWidth = getVectorWidth(argTy, inputTy);
424  if (failed(argWidth))
425  return emitOpError("block argument must be a scalar variant of the "
426  "vectorized operand");
427 
428  if (*argWidth != width)
429  return emitOpError("input and output vector width must match");
430  }
431 
432  return success();
433  }
434 
435  // Body is vectorized, boundary is not
436  if (auto width = getVectorWidth(getResultTypes().front(),
437  returnOp.getValue().getType());
438  succeeded(width)) {
439  for (auto [i, argTy] : llvm::enumerate(bodyArgTypes)) {
440  Type inputTy = getInputs()[i].getTypes().front();
441  FailureOr<unsigned> argWidth = getVectorWidth(inputTy, argTy);
442  if (failed(argWidth))
443  return emitOpError(
444  "block argument must be a vectorized variant of the operand");
445 
446  if (*argWidth != width)
447  return emitOpError("input and output vector width must match");
448 
449  if (getInputs()[i].size() > 1 && argWidth != getInputs()[i].size())
450  return emitOpError(
451  "when boundary not vectorized the number of vector element "
452  "operands must match the width of the vectorized body");
453  }
454 
455  return success();
456  }
457 
458  return returnOp.emitOpError(
459  "operand type must match parent op's result value or be a vectorized or "
460  "non-vectorized variant of it");
461 }
462 
463 bool VectorizeOp::isBoundaryVectorized() {
464  return getInputs().front().size() == 1;
465 }
466 bool VectorizeOp::isBodyVectorized() {
467  auto returnOp = cast<VectorizeReturnOp>(getBody().front().getTerminator());
468  if (isBoundaryVectorized() &&
469  returnOp.getValue().getType() == getResultTypes().front())
470  return true;
471 
472  if (auto width = getVectorWidth(getResultTypes().front(),
473  returnOp.getValue().getType());
474  succeeded(width))
475  return *width > 1;
476 
477  return false;
478 }
479 
480 //===----------------------------------------------------------------------===//
481 // SimInstantiateOp
482 //===----------------------------------------------------------------------===//
483 
484 void SimInstantiateOp::print(OpAsmPrinter &p) {
485  BlockArgument modelArg = getBody().getArgument(0);
486  auto modelType = cast<SimModelInstanceType>(modelArg.getType());
487 
488  p << " " << modelType.getModel() << " as ";
489  p.printRegionArgument(modelArg, {}, true);
490 
491  p.printOptionalAttrDictWithKeyword(getOperation()->getAttrs());
492 
493  p << " ";
494 
495  p.printRegion(getBody(), false);
496 }
497 
498 ParseResult SimInstantiateOp::parse(OpAsmParser &parser,
499  OperationState &result) {
500  StringAttr modelName;
501  if (failed(parser.parseSymbolName(modelName)))
502  return failure();
503 
504  if (failed(parser.parseKeyword("as")))
505  return failure();
506 
507  OpAsmParser::Argument modelArg;
508  if (failed(parser.parseArgument(modelArg, false, false)))
509  return failure();
510 
511  if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
512  return failure();
513 
514  MLIRContext *ctxt = result.getContext();
515  modelArg.type =
517 
518  std::unique_ptr<Region> body = std::make_unique<Region>();
519  if (failed(parser.parseRegion(*body, {modelArg})))
520  return failure();
521 
522  result.addRegion(std::move(body));
523  return success();
524 }
525 
526 LogicalResult SimInstantiateOp::verifyRegions() {
527  Region &body = getBody();
528  if (body.getNumArguments() != 1)
529  return emitError("entry block of body region must have the model instance "
530  "as a single argument");
531  if (!llvm::isa<SimModelInstanceType>(body.getArgument(0).getType()))
532  return emitError("entry block argument type is not a model instance");
533  return success();
534 }
535 
536 LogicalResult
537 SimInstantiateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
538  Operation *moduleOp = getSupportedModuleOp(
539  symbolTable, getOperation(),
540  llvm::cast<SimModelInstanceType>(getBody().getArgument(0).getType())
541  .getModel()
542  .getAttr());
543  if (!moduleOp)
544  return failure();
545 
546  return success();
547 }
548 
549 //===----------------------------------------------------------------------===//
550 // SimSetInputOp
551 //===----------------------------------------------------------------------===//
552 
553 LogicalResult
554 SimSetInputOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
555  Operation *moduleOp = getSupportedModuleOp(
556  symbolTable, getOperation(),
557  llvm::cast<SimModelInstanceType>(getInstance().getType())
558  .getModel()
559  .getAttr());
560  if (!moduleOp)
561  return failure();
562 
563  std::optional<hw::ModulePort> port = getModulePort(moduleOp, getInput());
564  if (!port)
565  return emitOpError("port not found on model");
566 
567  if (port->dir != hw::ModulePort::Direction::Input &&
569  return emitOpError("port is not an input port");
570 
571  if (port->type != getValue().getType())
572  return emitOpError(
573  "mismatched types between value and model port, port expects ")
574  << port->type;
575 
576  return success();
577 }
578 
579 //===----------------------------------------------------------------------===//
580 // SimGetPortOp
581 //===----------------------------------------------------------------------===//
582 
583 LogicalResult
584 SimGetPortOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
585  Operation *moduleOp = getSupportedModuleOp(
586  symbolTable, getOperation(),
587  llvm::cast<SimModelInstanceType>(getInstance().getType())
588  .getModel()
589  .getAttr());
590  if (!moduleOp)
591  return failure();
592 
593  std::optional<hw::ModulePort> port = getModulePort(moduleOp, getPort());
594  if (!port)
595  return emitOpError("port not found on model");
596 
597  if (port->type != getValue().getType())
598  return emitOpError(
599  "mismatched types between value and model port, port expects ")
600  << port->type;
601 
602  return success();
603 }
604 
605 //===----------------------------------------------------------------------===//
606 // SimStepOp
607 //===----------------------------------------------------------------------===//
608 
609 LogicalResult SimStepOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
610  Operation *moduleOp = getSupportedModuleOp(
611  symbolTable, getOperation(),
612  llvm::cast<SimModelInstanceType>(getInstance().getType())
613  .getModel()
614  .getAttr());
615  if (!moduleOp)
616  return failure();
617 
618  return success();
619 }
620 
621 #include "circt/Dialect/Arc/ArcInterfaces.cpp.inc"
622 
623 #define GET_OP_CLASSES
624 #include "circt/Dialect/Arc/Arc.cpp.inc"
static bool isSupportedModuleOp(Operation *moduleOp)
Definition: ArcOps.cpp:76
static LogicalResult verifyArcSymbolUse(Operation *op, TypeRange inputs, TypeRange results, SymbolTableCollection &symbolTable)
Definition: ArcOps.cpp:50
static LogicalResult verifyTypeListEquivalence(Operation *op, TypeRange expectedTypeList, TypeRange actualTypeList, StringRef elementName)
Definition: ArcOps.cpp:28
static FailureOr< unsigned > getVectorWidth(Type base, Type vectorized)
Definition: ArcOps.cpp:379
static Operation * getSupportedModuleOp(SymbolTableCollection &symbolTable, Operation *pointing, StringAttr symbol)
Fetches the operation pointed to by pointing with name symbol, checking that it is a supported model ...
Definition: ArcOps.cpp:82
static std::optional< hw::ModulePort > getModulePort(Operation *moduleOp, StringRef portName)
Definition: ArcOps.cpp:100
assert(baseType &&"element must be base type")
static Attribute getAttr(ArrayRef< NamedAttribute > attrs, StringRef name)
Get an attribute by name from a list of named attributes.
Definition: FIRRTLOps.cpp:3852
int32_t width
Definition: FIRRTL.cpp:36
static PortInfo getPort(ModuleTy &mod, size_t idx)
Definition: HWOps.cpp:1434
@ Input
Definition: HW.h:35
@ InOut
Definition: HW.h:35
static InstancePath empty
llvm::SmallVector< StringAttr > inputs
Builder builder
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:186