CIRCT 20.0.0git
Loading...
Searching...
No Matches
MooreOps.cpp
Go to the documentation of this file.
1//===- MooreOps.cpp - Implement the Moore operations ----------------------===//
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 implements the Moore dialect operations.
10//
11//===----------------------------------------------------------------------===//
12
18#include "mlir/IR/Builders.h"
19#include "llvm/ADT/SmallString.h"
20#include "llvm/ADT/TypeSwitch.h"
21
22using namespace circt;
23using namespace circt::moore;
24using namespace mlir;
25
26//===----------------------------------------------------------------------===//
27// SVModuleOp
28//===----------------------------------------------------------------------===//
29
30void SVModuleOp::build(mlir::OpBuilder &builder, mlir::OperationState &state,
31 llvm::StringRef name, hw::ModuleType type) {
32 state.addAttribute(SymbolTable::getSymbolAttrName(),
33 builder.getStringAttr(name));
34 state.addAttribute(getModuleTypeAttrName(state.name), TypeAttr::get(type));
35 state.addRegion();
36}
37
38void SVModuleOp::print(OpAsmPrinter &p) {
39 p << " ";
40
41 // Print the visibility of the module.
42 StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
43 if (auto visibility = (*this)->getAttrOfType<StringAttr>(visibilityAttrName))
44 p << visibility.getValue() << ' ';
45
46 p.printSymbolName(SymbolTable::getSymbolName(*this).getValue());
48 getModuleType(), {}, {});
49 p << " ";
50 p.printRegion(getBodyRegion(), /*printEntryBlockArgs=*/false,
51 /*printBlockTerminators=*/true);
52
53 p.printOptionalAttrDictWithKeyword(getOperation()->getAttrs(),
54 getAttributeNames());
55}
56
57ParseResult SVModuleOp::parse(OpAsmParser &parser, OperationState &result) {
58 // Parse the visibility attribute.
59 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
60
61 // Parse the module name.
62 StringAttr nameAttr;
63 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
64 result.attributes))
65 return failure();
66
67 // Parse the ports.
68 SmallVector<hw::module_like_impl::PortParse> ports;
69 TypeAttr modType;
70 if (failed(
71 hw::module_like_impl::parseModuleSignature(parser, ports, modType)))
72 return failure();
73 result.addAttribute(getModuleTypeAttrName(result.name), modType);
74
75 // Parse the attributes.
76 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
77 return failure();
78
79 // Add the entry block arguments.
80 SmallVector<OpAsmParser::Argument, 4> entryArgs;
81 for (auto &port : ports)
82 if (port.direction != hw::ModulePort::Direction::Output)
83 entryArgs.push_back(port);
84
85 // Parse the optional function body.
86 auto &bodyRegion = *result.addRegion();
87 if (parser.parseRegion(bodyRegion, entryArgs))
88 return failure();
89
90 ensureTerminator(bodyRegion, parser.getBuilder(), result.location);
91 return success();
92}
93
94void SVModuleOp::getAsmBlockArgumentNames(mlir::Region &region,
95 mlir::OpAsmSetValueNameFn setNameFn) {
96 if (&region != &getBodyRegion())
97 return;
98 auto moduleType = getModuleType();
99 for (auto [index, arg] : llvm::enumerate(region.front().getArguments()))
100 setNameFn(arg, moduleType.getInputNameAttr(index));
101}
102
103OutputOp SVModuleOp::getOutputOp() {
104 return cast<OutputOp>(getBody()->getTerminator());
105}
106
107OperandRange SVModuleOp::getOutputs() { return getOutputOp().getOperands(); }
108
109//===----------------------------------------------------------------------===//
110// OutputOp
111//===----------------------------------------------------------------------===//
112
113LogicalResult OutputOp::verify() {
114 auto module = getParentOp();
115
116 // Check that the number of operands matches the number of output ports.
117 auto outputTypes = module.getModuleType().getOutputTypes();
118 if (outputTypes.size() != getNumOperands())
119 return emitOpError("has ")
120 << getNumOperands() << " operands, but enclosing module @"
121 << module.getSymName() << " has " << outputTypes.size()
122 << " outputs";
123
124 // Check that the operand types match the output ports.
125 for (unsigned i = 0, e = outputTypes.size(); i != e; ++i)
126 if (outputTypes[i] != getOperand(i).getType())
127 return emitOpError() << "operand " << i << " (" << getOperand(i).getType()
128 << ") does not match output type (" << outputTypes[i]
129 << ") of module @" << module.getSymName();
130
131 return success();
132}
133
134//===----------------------------------------------------------------------===//
135// InstanceOp
136//===----------------------------------------------------------------------===//
137
138LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
139 // Resolve the target symbol.
140 auto *symbol =
141 symbolTable.lookupNearestSymbolFrom(*this, getModuleNameAttr());
142 if (!symbol)
143 return emitOpError("references unknown symbol @") << getModuleName();
144
145 // Check that the symbol is a SVModuleOp.
146 auto module = dyn_cast<SVModuleOp>(symbol);
147 if (!module)
148 return emitOpError("must reference a 'moore.module', but @")
149 << getModuleName() << " is a '" << symbol->getName() << "'";
150
151 // Check that the input ports match.
152 auto moduleType = module.getModuleType();
153 auto inputTypes = moduleType.getInputTypes();
154
155 if (inputTypes.size() != getNumOperands())
156 return emitOpError("has ")
157 << getNumOperands() << " operands, but target module @"
158 << module.getSymName() << " has " << inputTypes.size() << " inputs";
159
160 for (unsigned i = 0, e = inputTypes.size(); i != e; ++i)
161 if (inputTypes[i] != getOperand(i).getType())
162 return emitOpError() << "operand " << i << " (" << getOperand(i).getType()
163 << ") does not match input type (" << inputTypes[i]
164 << ") of module @" << module.getSymName();
165
166 // Check that the output ports match.
167 auto outputTypes = moduleType.getOutputTypes();
168
169 if (outputTypes.size() != getNumResults())
170 return emitOpError("has ")
171 << getNumOperands() << " results, but target module @"
172 << module.getSymName() << " has " << outputTypes.size()
173 << " outputs";
174
175 for (unsigned i = 0, e = outputTypes.size(); i != e; ++i)
176 if (outputTypes[i] != getResult(i).getType())
177 return emitOpError() << "result " << i << " (" << getResult(i).getType()
178 << ") does not match output type (" << outputTypes[i]
179 << ") of module @" << module.getSymName();
180
181 return success();
182}
183
184void InstanceOp::print(OpAsmPrinter &p) {
185 p << " ";
186 p.printAttributeWithoutType(getInstanceNameAttr());
187 p << " ";
188 p.printAttributeWithoutType(getModuleNameAttr());
189 printInputPortList(p, getOperation(), getInputs(), getInputs().getTypes(),
190 getInputNames());
191 p << " -> ";
192 printOutputPortList(p, getOperation(), getOutputs().getTypes(),
193 getOutputNames());
194 p.printOptionalAttrDict(getOperation()->getAttrs(), getAttributeNames());
195}
196
197ParseResult InstanceOp::parse(OpAsmParser &parser, OperationState &result) {
198 // Parse the instance name.
199 StringAttr instanceName;
200 if (parser.parseAttribute(instanceName, "instanceName", result.attributes))
201 return failure();
202
203 // Parse the module name.
204 FlatSymbolRefAttr moduleName;
205 if (parser.parseAttribute(moduleName, "moduleName", result.attributes))
206 return failure();
207
208 // Parse the input port list.
209 auto loc = parser.getCurrentLocation();
210 SmallVector<OpAsmParser::UnresolvedOperand> inputs;
211 SmallVector<Type> types;
212 ArrayAttr names;
213 if (parseInputPortList(parser, inputs, types, names))
214 return failure();
215 if (parser.resolveOperands(inputs, types, loc, result.operands))
216 return failure();
217 result.addAttribute("inputNames", names);
218
219 // Parse `->`.
220 if (parser.parseArrow())
221 return failure();
222
223 // Parse the output port list.
224 types.clear();
225 if (parseOutputPortList(parser, types, names))
226 return failure();
227 result.addAttribute("outputNames", names);
228 result.addTypes(types);
229
230 // Parse the attributes.
231 if (parser.parseOptionalAttrDict(result.attributes))
232 return failure();
233
234 return success();
235}
236
237void InstanceOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
238 SmallString<32> name;
239 name += getInstanceName();
240 name += '.';
241 auto baseLen = name.size();
242
243 for (auto [result, portName] :
244 llvm::zip(getOutputs(), getOutputNames().getAsRange<StringAttr>())) {
245 if (!portName || portName.empty())
246 continue;
247 name.resize(baseLen);
248 name += portName.getValue();
249 setNameFn(result, name);
250 }
251}
252
253//===----------------------------------------------------------------------===//
254// VariableOp
255//===----------------------------------------------------------------------===//
256
257void VariableOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
258 if (getName() && !getName()->empty())
259 setNameFn(getResult(), *getName());
260}
261
262LogicalResult VariableOp::canonicalize(VariableOp op,
263 PatternRewriter &rewriter) {
264 // If the variable is embedded in an SSACFG region, move the initial value
265 // into an assignment immediately after the variable op. This allows the
266 // mem2reg pass which cannot handle variables with initial values.
267 auto initial = op.getInitial();
268 if (initial && mlir::mayHaveSSADominance(*op->getParentRegion())) {
269 rewriter.modifyOpInPlace(op, [&] { op.getInitialMutable().clear(); });
270 rewriter.setInsertionPointAfter(op);
271 rewriter.create<BlockingAssignOp>(initial.getLoc(), op, initial);
272 return success();
273 }
274
275 // Check if the variable has one unique continuous assignment to it, all other
276 // uses are reads, and that all uses are in the same block as the variable
277 // itself.
278 auto *block = op->getBlock();
279 ContinuousAssignOp uniqueAssignOp;
280 for (auto *user : op->getUsers()) {
281 // Ensure that all users of the variable are in the same block.
282 if (user->getBlock() != block)
283 return failure();
284
285 // Ensure there is at most one unique continuous assignment to the variable.
286 if (auto assignOp = dyn_cast<ContinuousAssignOp>(user)) {
287 if (uniqueAssignOp)
288 return failure();
289 uniqueAssignOp = assignOp;
290 continue;
291 }
292
293 // Ensure all other users are reads.
294 if (!isa<ReadOp>(user))
295 return failure();
296 }
297 if (!uniqueAssignOp)
298 return failure();
299
300 // If the original variable had a name, create an `AssignedVariableOp` as a
301 // replacement. Otherwise substitute the assigned value directly.
302 Value assignedValue = uniqueAssignOp.getSrc();
303 if (auto name = op.getNameAttr(); name && !name.empty())
304 assignedValue = rewriter.create<AssignedVariableOp>(
305 op.getLoc(), name, uniqueAssignOp.getSrc());
306
307 // Remove the assign op and replace all reads with the new assigned var op.
308 rewriter.eraseOp(uniqueAssignOp);
309 for (auto *user : llvm::make_early_inc_range(op->getUsers())) {
310 auto readOp = cast<ReadOp>(user);
311 rewriter.replaceOp(readOp, assignedValue);
312 }
313
314 // Remove the original variable.
315 rewriter.eraseOp(op);
316 return success();
317}
318
319SmallVector<MemorySlot> VariableOp::getPromotableSlots() {
320 // We cannot promote variables with an initial value, since that value may not
321 // dominate the location where the default value needs to be constructed.
322 if (mlir::mayBeGraphRegion(*getOperation()->getParentRegion()) ||
323 getInitial())
324 return {};
325
326 // Ensure that `getDefaultValue` can conjure up a default value for the
327 // variable's type.
328 if (!isa<PackedType>(getType().getNestedType()))
329 return {};
330
331 return {MemorySlot{getResult(), getType().getNestedType()}};
332}
333
334Value VariableOp::getDefaultValue(const MemorySlot &slot, OpBuilder &builder) {
335 auto packedType = dyn_cast<PackedType>(slot.elemType);
336 if (!packedType)
337 return {};
338 auto bitWidth = packedType.getBitSize();
339 if (!bitWidth)
340 return {};
341 auto fvint = packedType.getDomain() == Domain::FourValued
342 ? FVInt::getAllX(*bitWidth)
343 : FVInt::getZero(*bitWidth);
344 Value value = builder.create<ConstantOp>(
345 getLoc(), IntType::get(getContext(), *bitWidth, packedType.getDomain()),
346 fvint);
347 if (value.getType() != packedType)
348 builder.create<ConversionOp>(getLoc(), packedType, value);
349 return value;
350}
351
352void VariableOp::handleBlockArgument(const MemorySlot &slot,
353 BlockArgument argument,
354 OpBuilder &builder) {}
355
356std::optional<mlir::PromotableAllocationOpInterface>
357VariableOp::handlePromotionComplete(const MemorySlot &slot, Value defaultValue,
358 OpBuilder &builder) {
359 if (defaultValue && defaultValue.use_empty())
360 defaultValue.getDefiningOp()->erase();
361 this->erase();
362 return {};
363}
364
365SmallVector<DestructurableMemorySlot> VariableOp::getDestructurableSlots() {
366 if (isa<SVModuleOp>(getOperation()->getParentOp()))
367 return {};
368 if (getInitial())
369 return {};
370
371 auto refType = getType();
372 auto destructurable = llvm::dyn_cast<DestructurableTypeInterface>(refType);
373 if (!destructurable)
374 return {};
375
376 auto destructuredType = destructurable.getSubelementIndexMap();
377 if (!destructuredType)
378 return {};
379
380 return {DestructurableMemorySlot{{getResult(), refType}, *destructuredType}};
381}
382
383DenseMap<Attribute, MemorySlot> VariableOp::destructure(
384 const DestructurableMemorySlot &slot,
385 const SmallPtrSetImpl<Attribute> &usedIndices, OpBuilder &builder,
386 SmallVectorImpl<DestructurableAllocationOpInterface> &newAllocators) {
387 assert(slot.ptr == getResult());
388 assert(!getInitial());
389 builder.setInsertionPointAfter(*this);
390
391 auto destructurableType = cast<DestructurableTypeInterface>(getType());
392 DenseMap<Attribute, MemorySlot> slotMap;
393 for (Attribute index : usedIndices) {
394 auto elemType = cast<RefType>(destructurableType.getTypeAtIndex(index));
395 assert(elemType && "used index must exist");
396 StringAttr varName;
397 if (auto name = getName(); name && !name->empty())
398 varName = StringAttr::get(
399 getContext(), (*name) + "." + cast<StringAttr>(index).getValue());
400 auto varOp =
401 builder.create<VariableOp>(getLoc(), elemType, varName, Value());
402 newAllocators.push_back(varOp);
403 slotMap.try_emplace<MemorySlot>(index, {varOp.getResult(), elemType});
404 }
405
406 return slotMap;
407}
408
409std::optional<DestructurableAllocationOpInterface>
410VariableOp::handleDestructuringComplete(const DestructurableMemorySlot &slot,
411 OpBuilder &builder) {
412 assert(slot.ptr == getResult());
413 this->erase();
414 return std::nullopt;
415}
416
417//===----------------------------------------------------------------------===//
418// NetOp
419//===----------------------------------------------------------------------===//
420
421void NetOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
422 if (getName() && !getName()->empty())
423 setNameFn(getResult(), *getName());
424}
425
426LogicalResult NetOp::canonicalize(NetOp op, PatternRewriter &rewriter) {
427 bool modified = false;
428
429 // Check if the net has one unique continuous assignment to it, and
430 // additionally if all other users are reads.
431 auto *block = op->getBlock();
432 ContinuousAssignOp uniqueAssignOp;
433 bool allUsesAreReads = true;
434 for (auto *user : op->getUsers()) {
435 // Ensure that all users of the net are in the same block.
436 if (user->getBlock() != block)
437 return failure();
438
439 // Ensure there is at most one unique continuous assignment to the net.
440 if (auto assignOp = dyn_cast<ContinuousAssignOp>(user)) {
441 if (uniqueAssignOp)
442 return failure();
443 uniqueAssignOp = assignOp;
444 continue;
445 }
446
447 // Ensure all other users are reads.
448 if (!isa<ReadOp>(user))
449 allUsesAreReads = false;
450 }
451
452 // If there was one unique assignment, and the `NetOp` does not yet have an
453 // assigned value set, fold the assignment into the net.
454 if (uniqueAssignOp && !op.getAssignment()) {
455 rewriter.modifyOpInPlace(
456 op, [&] { op.getAssignmentMutable().assign(uniqueAssignOp.getSrc()); });
457 rewriter.eraseOp(uniqueAssignOp);
458 modified = true;
459 uniqueAssignOp = {};
460 }
461
462 // If all users of the net op are reads, and any potential unique assignment
463 // has been folded into the net op itself, directly replace the reads with the
464 // net's assigned value.
465 if (!uniqueAssignOp && allUsesAreReads && op.getAssignment()) {
466 // If the original net had a name, create an `AssignedVariableOp` as a
467 // replacement. Otherwise substitute the assigned value directly.
468 auto assignedValue = op.getAssignment();
469 if (auto name = op.getNameAttr(); name && !name.empty())
470 assignedValue =
471 rewriter.create<AssignedVariableOp>(op.getLoc(), name, assignedValue);
472
473 // Replace all reads with the new assigned var op and remove the original
474 // net op.
475 for (auto *user : llvm::make_early_inc_range(op->getUsers())) {
476 auto readOp = cast<ReadOp>(user);
477 rewriter.replaceOp(readOp, assignedValue);
478 }
479 rewriter.eraseOp(op);
480 modified = true;
481 }
482
483 return success(modified);
484}
485
486//===----------------------------------------------------------------------===//
487// AssignedVariableOp
488//===----------------------------------------------------------------------===//
489
490void AssignedVariableOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
491 if (getName() && !getName()->empty())
492 setNameFn(getResult(), *getName());
493}
494
495LogicalResult AssignedVariableOp::canonicalize(AssignedVariableOp op,
496 PatternRewriter &rewriter) {
497 // Eliminate chained variables with the same name.
498 // var(name, var(name, x)) -> var(name, x)
499 if (auto otherOp = op.getInput().getDefiningOp<AssignedVariableOp>()) {
500 if (otherOp.getNameAttr() == op.getNameAttr()) {
501 rewriter.replaceOp(op, otherOp);
502 return success();
503 }
504 }
505
506 // Eliminate variables that alias an input port of the same name.
507 if (auto blockArg = dyn_cast<BlockArgument>(op.getInput())) {
508 if (auto moduleOp =
509 dyn_cast<SVModuleOp>(blockArg.getOwner()->getParentOp())) {
510 auto moduleType = moduleOp.getModuleType();
511 auto portName = moduleType.getInputNameAttr(blockArg.getArgNumber());
512 if (portName == op.getNameAttr()) {
513 rewriter.replaceOp(op, blockArg);
514 return success();
515 }
516 }
517 }
518
519 // Eliminate variables that feed an output port of the same name.
520 for (auto &use : op->getUses()) {
521 auto *useOwner = use.getOwner();
522 if (auto outputOp = dyn_cast<OutputOp>(useOwner)) {
523 if (auto moduleOp = dyn_cast<SVModuleOp>(outputOp->getParentOp())) {
524 auto moduleType = moduleOp.getModuleType();
525 auto portName = moduleType.getOutputNameAttr(use.getOperandNumber());
526 if (portName == op.getNameAttr()) {
527 rewriter.replaceOp(op, op.getInput());
528 return success();
529 }
530 } else
531 break;
532 }
533 }
534
535 return failure();
536}
537
538//===----------------------------------------------------------------------===//
539// ConstantOp
540//===----------------------------------------------------------------------===//
541
542void ConstantOp::print(OpAsmPrinter &p) {
543 p << " ";
544 printFVInt(p, getValue());
545 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
546 p << " : ";
547 p.printStrippedAttrOrType(getType());
548}
549
550ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) {
551 // Parse the constant value.
552 FVInt value;
553 auto valueLoc = parser.getCurrentLocation();
554 if (parseFVInt(parser, value))
555 return failure();
556
557 // Parse any optional attributes and the `:`.
558 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon())
559 return failure();
560
561 // Parse the result type.
562 IntType type;
563 if (parser.parseCustomTypeWithFallback(type))
564 return failure();
565
566 // Extend or truncate the constant value to match the size of the type.
567 if (type.getWidth() > value.getBitWidth()) {
568 // sext is always safe here, even for unsigned values, because the
569 // parseOptionalInteger method will return something with a zero in the
570 // top bits if it is a positive number.
571 value = value.sext(type.getWidth());
572 } else if (type.getWidth() < value.getBitWidth()) {
573 // The parser can return an unnecessarily wide result with leading
574 // zeros. This isn't a problem, but truncating off bits is bad.
575 unsigned neededBits =
576 value.isNegative() ? value.getSignificantBits() : value.getActiveBits();
577 if (type.getWidth() < neededBits)
578 return parser.emitError(valueLoc)
579 << "value requires " << neededBits
580 << " bits, but result type only has " << type.getWidth();
581 value = value.trunc(type.getWidth());
582 }
583
584 // If the constant contains any X or Z bits, the result type must be
585 // four-valued.
586 if (value.hasUnknown() && type.getDomain() != Domain::FourValued)
587 return parser.emitError(valueLoc)
588 << "value contains X or Z bits, but result type " << type
589 << " only allows two-valued bits";
590
591 // Build the attribute and op.
592 auto attrValue = FVIntegerAttr::get(parser.getContext(), value);
593 result.addAttribute("value", attrValue);
594 result.addTypes(type);
595 return success();
596}
597
598LogicalResult ConstantOp::verify() {
599 auto attrWidth = getValue().getBitWidth();
600 auto typeWidth = getType().getWidth();
601 if (attrWidth != typeWidth)
602 return emitError("attribute width ")
603 << attrWidth << " does not match return type's width " << typeWidth;
604 return success();
605}
606
607void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
608 const FVInt &value) {
609 assert(type.getWidth() == value.getBitWidth() &&
610 "FVInt width must match type width");
611 build(builder, result, type, FVIntegerAttr::get(builder.getContext(), value));
612}
613
614void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
615 const APInt &value) {
616 assert(type.getWidth() == value.getBitWidth() &&
617 "APInt width must match type width");
618 build(builder, result, type, FVInt(value));
619}
620
621/// This builder allows construction of small signed integers like 0, 1, -1
622/// matching a specified MLIR type. This shouldn't be used for general constant
623/// folding because it only works with values that can be expressed in an
624/// `int64_t`.
625void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
626 int64_t value) {
627 build(builder, result, type,
628 APInt(type.getWidth(), (uint64_t)value, /*isSigned=*/true));
629}
630
631OpFoldResult ConstantOp::fold(FoldAdaptor adaptor) {
632 assert(adaptor.getOperands().empty() && "constant has no operands");
633 return getValueAttr();
634}
635
636//===----------------------------------------------------------------------===//
637// ConcatOp
638//===----------------------------------------------------------------------===//
639
640LogicalResult ConcatOp::inferReturnTypes(
641 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
642 DictionaryAttr attrs, mlir::OpaqueProperties properties,
643 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
644 Domain domain = Domain::TwoValued;
645 unsigned width = 0;
646 for (auto operand : operands) {
647 auto type = cast<IntType>(operand.getType());
648 if (type.getDomain() == Domain::FourValued)
649 domain = Domain::FourValued;
650 width += type.getWidth();
651 }
652 results.push_back(IntType::get(context, width, domain));
653 return success();
654}
655
656//===----------------------------------------------------------------------===//
657// ConcatRefOp
658//===----------------------------------------------------------------------===//
659
660LogicalResult ConcatRefOp::inferReturnTypes(
661 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
662 DictionaryAttr attrs, mlir::OpaqueProperties properties,
663 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
664 Domain domain = Domain::TwoValued;
665 unsigned width = 0;
666 for (auto operand : operands) {
667 auto type = cast<IntType>(cast<RefType>(operand.getType()).getNestedType());
668 if (type.getDomain() == Domain::FourValued)
669 domain = Domain::FourValued;
670 width += type.getWidth();
671 }
672 results.push_back(RefType::get(IntType::get(context, width, domain)));
673 return success();
674}
675
676//===----------------------------------------------------------------------===//
677// ArrayCreateOp
678//===----------------------------------------------------------------------===//
679
680static std::pair<unsigned, UnpackedType> getArrayElements(Type type) {
681 if (auto arrayType = dyn_cast<ArrayType>(type))
682 return {arrayType.getSize(), arrayType.getElementType()};
683 if (auto arrayType = dyn_cast<UnpackedArrayType>(type))
684 return {arrayType.getSize(), arrayType.getElementType()};
685 assert(0 && "expected ArrayType or UnpackedArrayType");
686 return {};
687}
688
689LogicalResult ArrayCreateOp::verify() {
690 auto [size, elementType] = getArrayElements(getType());
691
692 // Check that the number of operands matches the array size.
693 if (getElements().size() != size)
694 return emitOpError() << "has " << getElements().size()
695 << " operands, but result type requires " << size;
696
697 // Check that the operand types match the array element type. We only need to
698 // check one of the operands, since the `SameTypeOperands` trait ensures all
699 // operands have the same type.
700 if (size > 0) {
701 auto value = getElements()[0];
702 if (value.getType() != elementType)
703 return emitOpError() << "operands have type " << value.getType()
704 << ", but array requires " << elementType;
705 }
706 return success();
707}
708
709//===----------------------------------------------------------------------===//
710// StructCreateOp
711//===----------------------------------------------------------------------===//
712
713static std::optional<uint32_t> getStructFieldIndex(Type type, StringAttr name) {
714 if (auto structType = dyn_cast<StructType>(type))
715 return structType.getFieldIndex(name);
716 if (auto structType = dyn_cast<UnpackedStructType>(type))
717 return structType.getFieldIndex(name);
718 assert(0 && "expected StructType or UnpackedStructType");
719 return {};
720}
721
722static ArrayRef<StructLikeMember> getStructMembers(Type type) {
723 if (auto structType = dyn_cast<StructType>(type))
724 return structType.getMembers();
725 if (auto structType = dyn_cast<UnpackedStructType>(type))
726 return structType.getMembers();
727 assert(0 && "expected StructType or UnpackedStructType");
728 return {};
729}
730
731static UnpackedType getStructFieldType(Type type, StringAttr name) {
732 if (auto index = getStructFieldIndex(type, name))
733 return getStructMembers(type)[*index].type;
734 return {};
735}
736
737LogicalResult StructCreateOp::verify() {
738 auto members = getStructMembers(getType());
739
740 // Check that the number of operands matches the number of struct fields.
741 if (getFields().size() != members.size())
742 return emitOpError() << "has " << getFields().size()
743 << " operands, but result type requires "
744 << members.size();
745
746 // Check that the operand types match the struct field types.
747 for (auto [index, pair] : llvm::enumerate(llvm::zip(getFields(), members))) {
748 auto [value, member] = pair;
749 if (value.getType() != member.type)
750 return emitOpError() << "operand #" << index << " has type "
751 << value.getType() << ", but struct field "
752 << member.name << " requires " << member.type;
753 }
754 return success();
755}
756
757OpFoldResult StructCreateOp::fold(FoldAdaptor adaptor) {
758 SmallVector<NamedAttribute> fields;
759 for (auto [member, field] :
760 llvm::zip(getStructMembers(getType()), adaptor.getFields())) {
761 if (!field)
762 return {};
763 fields.push_back(NamedAttribute(member.name, field));
764 }
765 return DictionaryAttr::get(getContext(), fields);
766}
767
768//===----------------------------------------------------------------------===//
769// StructExtractOp
770//===----------------------------------------------------------------------===//
771
772LogicalResult StructExtractOp::verify() {
773 auto type = getStructFieldType(getInput().getType(), getFieldNameAttr());
774 if (!type)
775 return emitOpError() << "extracts field " << getFieldNameAttr()
776 << " which does not exist in " << getInput().getType();
777 if (type != getType())
778 return emitOpError() << "result type " << getType()
779 << " must match struct field type " << type;
780 return success();
781}
782
783OpFoldResult StructExtractOp::fold(FoldAdaptor adaptor) {
784 // Extract on a constant struct input.
785 if (auto fields = dyn_cast_or_null<DictionaryAttr>(adaptor.getInput()))
786 if (auto value = fields.get(getFieldNameAttr()))
787 return value;
788
789 // extract(inject(s, "field", v), "field") -> v
790 if (auto inject = getInput().getDefiningOp<StructInjectOp>()) {
791 if (inject.getFieldNameAttr() == getFieldNameAttr())
792 return inject.getNewValue();
793 return {};
794 }
795
796 // extract(create({"field": v, ...}), "field") -> v
797 if (auto create = getInput().getDefiningOp<StructCreateOp>()) {
798 if (auto index = getStructFieldIndex(create.getType(), getFieldNameAttr()))
799 return create.getFields()[*index];
800 return {};
801 }
802
803 return {};
804}
805
806//===----------------------------------------------------------------------===//
807// StructExtractRefOp
808//===----------------------------------------------------------------------===//
809
810LogicalResult StructExtractRefOp::verify() {
811 auto type = getStructFieldType(
812 cast<RefType>(getInput().getType()).getNestedType(), getFieldNameAttr());
813 if (!type)
814 return emitOpError() << "extracts field " << getFieldNameAttr()
815 << " which does not exist in " << getInput().getType();
816 if (type != getType().getNestedType())
817 return emitOpError() << "result ref of type " << getType().getNestedType()
818 << " must match struct field type " << type;
819 return success();
820}
821
822bool StructExtractRefOp::canRewire(
823 const DestructurableMemorySlot &slot,
824 SmallPtrSetImpl<Attribute> &usedIndices,
825 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
826 const DataLayout &dataLayout) {
827 if (slot.ptr != getInput())
828 return false;
829 auto index = getFieldNameAttr();
830 if (!index || !slot.subelementTypes.contains(index))
831 return false;
832 usedIndices.insert(index);
833 return true;
834}
835
836DeletionKind
837StructExtractRefOp::rewire(const DestructurableMemorySlot &slot,
838 DenseMap<Attribute, MemorySlot> &subslots,
839 OpBuilder &builder, const DataLayout &dataLayout) {
840 auto index = getFieldNameAttr();
841 const MemorySlot &memorySlot = subslots.at(index);
842 replaceAllUsesWith(memorySlot.ptr);
843 getInputMutable().drop();
844 erase();
845 return DeletionKind::Keep;
846}
847
848//===----------------------------------------------------------------------===//
849// StructInjectOp
850//===----------------------------------------------------------------------===//
851
852LogicalResult StructInjectOp::verify() {
853 auto type = getStructFieldType(getInput().getType(), getFieldNameAttr());
854 if (!type)
855 return emitOpError() << "injects field " << getFieldNameAttr()
856 << " which does not exist in " << getInput().getType();
857 if (type != getNewValue().getType())
858 return emitOpError() << "injected value " << getNewValue().getType()
859 << " must match struct field type " << type;
860 return success();
861}
862
863OpFoldResult StructInjectOp::fold(FoldAdaptor adaptor) {
864 auto input = adaptor.getInput();
865 auto newValue = adaptor.getNewValue();
866 if (!input || !newValue)
867 return {};
868 NamedAttrList fields(cast<DictionaryAttr>(input));
869 fields.set(getFieldNameAttr(), newValue);
870 return fields.getDictionary(getContext());
871}
872
873LogicalResult StructInjectOp::canonicalize(StructInjectOp op,
874 PatternRewriter &rewriter) {
875 auto members = getStructMembers(op.getType());
876
877 // Chase a chain of `struct_inject` ops, with an optional final
878 // `struct_create`, and take note of the values assigned to each field.
879 SmallPtrSet<Operation *, 4> injectOps;
880 DenseMap<StringAttr, Value> fieldValues;
881 Value input = op;
882 while (auto injectOp = input.getDefiningOp<StructInjectOp>()) {
883 if (!injectOps.insert(injectOp).second)
884 return failure();
885 fieldValues.insert({injectOp.getFieldNameAttr(), injectOp.getNewValue()});
886 input = injectOp.getInput();
887 }
888 if (auto createOp = input.getDefiningOp<StructCreateOp>())
889 for (auto [value, member] : llvm::zip(createOp.getFields(), members))
890 fieldValues.insert({member.name, value});
891
892 // If the inject chain sets all fields, canonicalize to a `struct_create`.
893 if (fieldValues.size() == members.size()) {
894 SmallVector<Value> values;
895 values.reserve(fieldValues.size());
896 for (auto member : members)
897 values.push_back(fieldValues.lookup(member.name));
898 rewriter.replaceOpWithNewOp<StructCreateOp>(op, op.getType(), values);
899 return success();
900 }
901
902 // If each inject op in the chain assigned to a unique field, there is nothing
903 // to canonicalize.
904 if (injectOps.size() == fieldValues.size())
905 return failure();
906
907 // Otherwise we can eliminate overwrites by creating new injects. The hash map
908 // of field values contains the last assigned value for each field.
909 for (auto member : members)
910 if (auto value = fieldValues.lookup(member.name))
911 input = rewriter.create<StructInjectOp>(op.getLoc(), op.getType(), input,
912 member.name, value);
913 rewriter.replaceOp(op, input);
914 return success();
915}
916
917//===----------------------------------------------------------------------===//
918// UnionCreateOp
919//===----------------------------------------------------------------------===//
920
921LogicalResult UnionCreateOp::verify() {
922 /// checks if the types of the input is exactly equal to the union field
923 /// type
924 return TypeSwitch<Type, LogicalResult>(getType())
925 .Case<UnionType, UnpackedUnionType>([this](auto &type) {
926 auto members = type.getMembers();
927 auto resultType = getType();
928 auto fieldName = getFieldName();
929 for (const auto &member : members)
930 if (member.name == fieldName && member.type == resultType)
931 return success();
932 emitOpError("input type must match the union field type");
933 return failure();
934 })
935 .Default([this](auto &) {
936 emitOpError("input type must be UnionType or UnpackedUnionType");
937 return failure();
938 });
939}
940
941//===----------------------------------------------------------------------===//
942// UnionExtractOp
943//===----------------------------------------------------------------------===//
944
945LogicalResult UnionExtractOp::verify() {
946 /// checks if the types of the input is exactly equal to the one of the
947 /// types of the result union fields
948 return TypeSwitch<Type, LogicalResult>(getInput().getType())
949 .Case<UnionType, UnpackedUnionType>([this](auto &type) {
950 auto members = type.getMembers();
951 auto fieldName = getFieldName();
952 auto resultType = getType();
953 for (const auto &member : members)
954 if (member.name == fieldName && member.type == resultType)
955 return success();
956 emitOpError("result type must match the union field type");
957 return failure();
958 })
959 .Default([this](auto &) {
960 emitOpError("input type must be UnionType or UnpackedUnionType");
961 return failure();
962 });
963}
964
965//===----------------------------------------------------------------------===//
966// UnionExtractOp
967//===----------------------------------------------------------------------===//
968
969LogicalResult UnionExtractRefOp::verify() {
970 /// checks if the types of the result is exactly equal to the type of the
971 /// refe union field
972 return TypeSwitch<Type, LogicalResult>(getInput().getType().getNestedType())
973 .Case<UnionType, UnpackedUnionType>([this](auto &type) {
974 auto members = type.getMembers();
975 auto fieldName = getFieldName();
976 auto resultType = getType().getNestedType();
977 for (const auto &member : members)
978 if (member.name == fieldName && member.type == resultType)
979 return success();
980 emitOpError("result type must match the union field type");
981 return failure();
982 })
983 .Default([this](auto &) {
984 emitOpError("input type must be UnionType or UnpackedUnionType");
985 return failure();
986 });
987}
988
989//===----------------------------------------------------------------------===//
990// YieldOp
991//===----------------------------------------------------------------------===//
992
993LogicalResult YieldOp::verify() {
994 // Check that YieldOp's parent operation is ConditionalOp.
995 auto cond = dyn_cast<ConditionalOp>(*(*this).getParentOp());
996 if (!cond) {
997 emitOpError("must have a conditional parent");
998 return failure();
999 }
1000
1001 // Check that the operand matches the parent operation's result.
1002 auto condType = cond.getType();
1003 auto yieldType = getOperand().getType();
1004 if (condType != yieldType) {
1005 emitOpError("yield type must match conditional. Expected ")
1006 << condType << ", but got " << yieldType << ".";
1007 return failure();
1008 }
1009
1010 return success();
1011}
1012
1013//===----------------------------------------------------------------------===//
1014// ConversionOp
1015//===----------------------------------------------------------------------===//
1016
1017OpFoldResult ConversionOp::fold(FoldAdaptor adaptor) {
1018 // Fold away no-op casts.
1019 if (getInput().getType() == getResult().getType())
1020 return getInput();
1021
1022 // Convert domains of constant integer inputs.
1023 auto intInput = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput());
1024 auto fromIntType = dyn_cast<IntType>(getInput().getType());
1025 auto toIntType = dyn_cast<IntType>(getResult().getType());
1026 if (intInput && fromIntType && toIntType &&
1027 fromIntType.getWidth() == toIntType.getWidth()) {
1028 // If we are going *to* a four-valued type, simply pass through the
1029 // constant.
1030 if (toIntType.getDomain() == Domain::FourValued)
1031 return intInput;
1032
1033 // Otherwise map all unknown bits to zero (the default in SystemVerilog) and
1034 // return a new constant.
1035 return FVIntegerAttr::get(getContext(), intInput.getValue().toAPInt(false));
1036 }
1037
1038 return {};
1039}
1040
1041//===----------------------------------------------------------------------===//
1042// BoolCastOp
1043//===----------------------------------------------------------------------===//
1044
1045OpFoldResult BoolCastOp::fold(FoldAdaptor adaptor) {
1046 // Fold away no-op casts.
1047 if (getInput().getType() == getResult().getType())
1048 return getInput();
1049 return {};
1050}
1051
1052//===----------------------------------------------------------------------===//
1053// BlockingAssignOp
1054//===----------------------------------------------------------------------===//
1055
1056bool BlockingAssignOp::loadsFrom(const MemorySlot &slot) { return false; }
1057
1058bool BlockingAssignOp::storesTo(const MemorySlot &slot) {
1059 return getDst() == slot.ptr;
1060}
1061
1062Value BlockingAssignOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1063 Value reachingDef,
1064 const DataLayout &dataLayout) {
1065 return getSrc();
1066}
1067
1068bool BlockingAssignOp::canUsesBeRemoved(
1069 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1070 SmallVectorImpl<OpOperand *> &newBlockingUses,
1071 const DataLayout &dataLayout) {
1072
1073 if (blockingUses.size() != 1)
1074 return false;
1075 Value blockingUse = (*blockingUses.begin())->get();
1076 return blockingUse == slot.ptr && getDst() == slot.ptr &&
1077 getSrc() != slot.ptr && getSrc().getType() == slot.elemType;
1078}
1079
1080DeletionKind BlockingAssignOp::removeBlockingUses(
1081 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1082 OpBuilder &builder, Value reachingDefinition,
1083 const DataLayout &dataLayout) {
1084 return DeletionKind::Delete;
1085}
1086
1087//===----------------------------------------------------------------------===//
1088// ReadOp
1089//===----------------------------------------------------------------------===//
1090
1091bool ReadOp::loadsFrom(const MemorySlot &slot) {
1092 return getInput() == slot.ptr;
1093}
1094
1095bool ReadOp::storesTo(const MemorySlot &slot) { return false; }
1096
1097Value ReadOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1098 Value reachingDef, const DataLayout &dataLayout) {
1099 llvm_unreachable("getStored should not be called on ReadOp");
1100}
1101
1102bool ReadOp::canUsesBeRemoved(const MemorySlot &slot,
1103 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1104 SmallVectorImpl<OpOperand *> &newBlockingUses,
1105 const DataLayout &dataLayout) {
1106
1107 if (blockingUses.size() != 1)
1108 return false;
1109 Value blockingUse = (*blockingUses.begin())->get();
1110 return blockingUse == slot.ptr && getOperand() == slot.ptr &&
1111 getResult().getType() == slot.elemType;
1112}
1113
1114DeletionKind
1115ReadOp::removeBlockingUses(const MemorySlot &slot,
1116 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1117 OpBuilder &builder, Value reachingDefinition,
1118 const DataLayout &dataLayout) {
1119 getResult().replaceAllUsesWith(reachingDefinition);
1120 return DeletionKind::Delete;
1121}
1122
1123//===----------------------------------------------------------------------===//
1124// PowSOp
1125//===----------------------------------------------------------------------===//
1126
1127static OpFoldResult powCommonFolding(MLIRContext *ctxt, Attribute lhs,
1128 Attribute rhs) {
1129 auto lhsValue = dyn_cast_or_null<FVIntegerAttr>(lhs);
1130 if (lhsValue && lhsValue.getValue() == 1)
1131 return lhs;
1132
1133 auto rhsValue = dyn_cast_or_null<FVIntegerAttr>(rhs);
1134 if (rhsValue && rhsValue.getValue().isZero())
1135 return FVIntegerAttr::get(ctxt,
1136 FVInt(rhsValue.getValue().getBitWidth(), 1));
1137
1138 return {};
1139}
1140
1141OpFoldResult PowSOp::fold(FoldAdaptor adaptor) {
1142 return powCommonFolding(getContext(), adaptor.getLhs(), adaptor.getRhs());
1143}
1144
1145LogicalResult PowSOp::canonicalize(PowSOp op, PatternRewriter &rewriter) {
1146 Location loc = op.getLoc();
1147 auto intType = cast<IntType>(op.getRhs().getType());
1148 if (auto baseOp = op.getLhs().getDefiningOp<ConstantOp>()) {
1149 if (baseOp.getValue() == 2) {
1150 Value constOne = rewriter.create<ConstantOp>(loc, intType, 1);
1151 Value constZero = rewriter.create<ConstantOp>(loc, intType, 0);
1152 Value shift = rewriter.create<ShlOp>(loc, constOne, op.getRhs());
1153 Value isNegative = rewriter.create<SltOp>(loc, op.getRhs(), constZero);
1154 auto condOp = rewriter.replaceOpWithNewOp<ConditionalOp>(
1155 op, op.getLhs().getType(), isNegative);
1156 Block *thenBlock = rewriter.createBlock(&condOp.getTrueRegion());
1157 rewriter.setInsertionPointToStart(thenBlock);
1158 rewriter.create<YieldOp>(loc, constZero);
1159 Block *elseBlock = rewriter.createBlock(&condOp.getFalseRegion());
1160 rewriter.setInsertionPointToStart(elseBlock);
1161 rewriter.create<YieldOp>(loc, shift);
1162 return success();
1163 }
1164 }
1165
1166 return failure();
1167}
1168
1169//===----------------------------------------------------------------------===//
1170// PowUOp
1171//===----------------------------------------------------------------------===//
1172
1173OpFoldResult PowUOp::fold(FoldAdaptor adaptor) {
1174 return powCommonFolding(getContext(), adaptor.getLhs(), adaptor.getRhs());
1175}
1176
1177LogicalResult PowUOp::canonicalize(PowUOp op, PatternRewriter &rewriter) {
1178 Location loc = op.getLoc();
1179 auto intType = cast<IntType>(op.getRhs().getType());
1180 if (auto baseOp = op.getLhs().getDefiningOp<ConstantOp>()) {
1181 if (baseOp.getValue() == 2) {
1182 Value constOne = rewriter.create<ConstantOp>(loc, intType, 1);
1183 rewriter.replaceOpWithNewOp<ShlOp>(op, constOne, op.getRhs());
1184 return success();
1185 }
1186 }
1187
1188 return failure();
1189}
1190
1191//===----------------------------------------------------------------------===//
1192// SubOp
1193//===----------------------------------------------------------------------===//
1194
1195OpFoldResult SubOp::fold(FoldAdaptor adaptor) {
1196 if (auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs()))
1197 if (intAttr.getValue().isZero())
1198 return getLhs();
1199
1200 return {};
1201}
1202
1203//===----------------------------------------------------------------------===//
1204// TableGen generated logic.
1205//===----------------------------------------------------------------------===//
1206
1207// Provide the autogenerated implementation guts for the Op classes.
1208#define GET_OP_CLASSES
1209#include "circt/Dialect/Moore/Moore.cpp.inc"
1210#include "circt/Dialect/Moore/MooreEnums.cpp.inc"
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
@ Output
Definition HW.h:35
static bool getFieldName(const FieldRef &fieldRef, SmallString< 32 > &string)
static InstancePath empty
static OpFoldResult powCommonFolding(MLIRContext *ctxt, Attribute lhs, Attribute rhs)
static ArrayRef< StructLikeMember > getStructMembers(Type type)
Definition MooreOps.cpp:722
static std::optional< uint32_t > getStructFieldIndex(Type type, StringAttr name)
Definition MooreOps.cpp:713
static UnpackedType getStructFieldType(Type type, StringAttr name)
Definition MooreOps.cpp:731
static std::pair< unsigned, UnpackedType > getArrayElements(Type type)
Definition MooreOps.cpp:680
Four-valued arbitrary precision integers.
Definition FVInt.h:37
bool isNegative() const
Determine whether the integer interpreted as a signed number would be negative.
Definition FVInt.h:181
FVInt sext(unsigned bitWidth) const
Sign-extend the integer to a new bit width.
Definition FVInt.h:144
unsigned getSignificantBits() const
Compute the minimum bit width necessary to accurately represent this integer's value and sign.
Definition FVInt.h:98
static FVInt getAllX(unsigned numBits)
Construct an FVInt with all bits set to X.
Definition FVInt.h:71
bool hasUnknown() const
Determine if any bits are X or Z.
Definition FVInt.h:164
unsigned getActiveBits() const
Compute the number of active bits in the value.
Definition FVInt.h:88
unsigned getBitWidth() const
Return the number of bits this integer has.
Definition FVInt.h:81
FVInt trunc(unsigned bitWidth) const
Truncate the integer to a smaller bit width.
Definition FVInt.h:128
An unpacked SystemVerilog type.
Definition MooreTypes.h:82
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:55
Direction
The direction of a Component or Cell port.
Definition CalyxOps.h:76
std::string getInstanceName(mlir::func::CallOp callOp)
A helper function to get the instance name.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
ParseResult parseModuleSignature(OpAsmParser &parser, SmallVectorImpl< PortParse > &args, TypeAttr &modType)
New Style parsing.
void printModuleSignatureNew(OpAsmPrinter &p, Region &body, hw::ModuleType modType, ArrayRef< Attribute > portAttrs, ArrayRef< Location > locAttrs)
FunctionType getModuleType(Operation *module)
Return the signature for the specified module as a function type.
Definition HWOps.cpp:521
Domain
The number of values each bit of a type can assume.
Definition MooreTypes.h:47
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
ParseResult parseInputPortList(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &inputs, SmallVectorImpl< Type > &inputTypes, ArrayAttr &inputNames)
Parse a list of instance input ports.
void printOutputPortList(OpAsmPrinter &p, Operation *op, TypeRange resultTypes, ArrayAttr resultNames)
Print a list of instance output ports.
void printFVInt(AsmPrinter &p, const FVInt &value)
Print a four-valued integer usign an AsmPrinter.
Definition FVInt.cpp:147
ParseResult parseFVInt(AsmParser &p, FVInt &result)
Parse a four-valued integer using an AsmParser.
Definition FVInt.cpp:162
void printInputPortList(OpAsmPrinter &p, Operation *op, OperandRange inputs, TypeRange inputTypes, ArrayAttr inputNames)
Print a list of instance input ports.
ParseResult parseOutputPortList(OpAsmParser &parser, SmallVectorImpl< Type > &resultTypes, ArrayAttr &resultNames)
Parse a list of instance output ports.
Definition hw.py:1
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:182