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