CIRCT 24.0.0git
Loading...
Searching...
No Matches
HWOps.cpp
Go to the documentation of this file.
1//===- HWOps.cpp - Implement the HW 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 implement the HW ops.
10//
11//===----------------------------------------------------------------------===//
12
23#include "mlir/IR/Builders.h"
24#include "mlir/IR/Matchers.h"
25#include "mlir/IR/PatternMatch.h"
26#include "mlir/Interfaces/FunctionImplementation.h"
27#include "llvm/ADT/BitVector.h"
28#include "llvm/ADT/SmallPtrSet.h"
29#include "llvm/ADT/StringSet.h"
30
31using namespace circt;
32using namespace hw;
33using mlir::TypedAttr;
34
35/// Flip a port direction.
37 switch (direction) {
38 case ModulePort::Direction::Input:
39 return ModulePort::Direction::Output;
40 case ModulePort::Direction::Output:
41 return ModulePort::Direction::Input;
42 case ModulePort::Direction::InOut:
43 return ModulePort::Direction::InOut;
44 }
45 llvm_unreachable("unknown PortDirection");
46}
47
48bool hw::isValidIndexBitWidth(Value index, Value array) {
49 hw::ArrayType arrayType =
50 dyn_cast<hw::ArrayType>(hw::getCanonicalType(array.getType()));
51 assert(arrayType && "expected array type");
52 unsigned indexWidth = index.getType().getIntOrFloatBitWidth();
53 auto requiredWidth = llvm::Log2_64_Ceil(arrayType.getNumElements());
54 return requiredWidth == 0 ? (indexWidth == 0 || indexWidth == 1)
55 : indexWidth == requiredWidth;
56}
57
58/// Return true if the specified operation is a combinational logic op.
59bool hw::isCombinational(Operation *op) {
60 struct IsCombClassifier : public TypeOpVisitor<IsCombClassifier, bool> {
61 bool visitInvalidTypeOp(Operation *op) { return false; }
62 bool visitUnhandledTypeOp(Operation *op) { return true; }
63 };
64
65 return (op->getDialect() && op->getDialect()->getNamespace() == "comb") ||
66 IsCombClassifier().dispatchTypeOpVisitor(op);
67}
68
69static Value foldStructExtract(Operation *inputOp, uint32_t fieldIndex) {
70 // A struct extract of a struct create -> corresponding struct create operand.
71 if (auto structCreate = dyn_cast_or_null<StructCreateOp>(inputOp)) {
72 return structCreate.getOperand(fieldIndex);
73 }
74
75 // Extracting injected field -> corresponding field
76 if (auto structInject = dyn_cast_or_null<StructInjectOp>(inputOp)) {
77 if (structInject.getFieldIndex() != fieldIndex)
78 return {};
79 return structInject.getNewValue();
80 }
81 return {};
82}
83
84static ArrayAttr arrayOrEmpty(mlir::MLIRContext *context,
85 ArrayRef<Attribute> attrs) {
86 if (attrs.empty())
87 return ArrayAttr::get(context, {});
88 bool empty = true;
89 for (auto a : attrs)
90 if (a && !cast<DictionaryAttr>(a).empty()) {
91 empty = false;
92 break;
93 }
94 if (empty)
95 return ArrayAttr::get(context, {});
96 return ArrayAttr::get(context, attrs);
97}
98
99/// Get a special name to use when printing the entry block arguments of the
100/// region contained by an operation in this dialect.
101static void getAsmBlockArgumentNamesImpl(mlir::Region &region,
102 OpAsmSetValueNameFn setNameFn) {
103 if (region.empty())
104 return;
105 // Assign port names to the bbargs.
106 auto module = cast<HWModuleOp>(region.getParentOp());
107
108 auto *block = &region.front();
109 for (size_t i = 0, e = block->getNumArguments(); i != e; ++i) {
110 auto name = module.getInputName(i);
111 // Let mlir deterministically convert names to valid identifiers
112 setNameFn(block->getArgument(i), name);
113 }
114}
115
116enum class Delimiter {
117 None,
118 Paren, // () enclosed list
119 OptionalLessGreater, // <> enclosed list or absent
120};
121
122/// Check parameter specified by `value` to see if it is valid according to the
123/// module's parameters. If not, emit an error to the diagnostic provided as an
124/// argument to the lambda 'instanceError' and return failure, otherwise return
125/// success.
126///
127/// If `disallowParamRefs` is true, then parameter references are not allowed.
128LogicalResult hw::checkParameterInContext(
129 Attribute value, ArrayAttr moduleParameters,
130 const instance_like_impl::EmitErrorFn &instanceError,
131 bool disallowParamRefs) {
132 // Literals are always ok. Their types are already known to match
133 // expectations.
134 if (isa<IntegerAttr>(value) || isa<FloatAttr>(value) ||
135 isa<StringAttr>(value) || isa<ParamVerbatimAttr>(value))
136 return success();
137
138 // Check both subexpressions of an expression.
139 if (auto expr = dyn_cast<ParamExprAttr>(value)) {
140 for (auto op : expr.getOperands())
141 if (failed(checkParameterInContext(op, moduleParameters, instanceError,
142 disallowParamRefs)))
143 return failure();
144 return success();
145 }
146
147 // Parameter references need more analysis to make sure they are valid within
148 // this module.
149 if (auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
150 auto nameAttr = parameterRef.getName();
151
152 // Don't allow references to parameters from the default values of a
153 // parameter list.
154 if (disallowParamRefs) {
155 instanceError([&](auto &diag) {
156 diag << "parameter " << nameAttr
157 << " cannot be used as a default value for a parameter";
158 return false;
159 });
160 return failure();
161 }
162
163 // Find the corresponding attribute in the module.
164 for (auto param : moduleParameters) {
165 auto paramAttr = cast<ParamDeclAttr>(param);
166 if (paramAttr.getName() != nameAttr)
167 continue;
168
169 // If the types match then the reference is ok.
170 if (paramAttr.getType() == parameterRef.getType())
171 return success();
172
173 instanceError([&](auto &diag) {
174 diag << "parameter " << nameAttr << " used with type "
175 << parameterRef.getType() << "; should have type "
176 << paramAttr.getType();
177 return true;
178 });
179 return failure();
180 }
181
182 instanceError([&](auto &diag) {
183 diag << "use of unknown parameter " << nameAttr;
184 return true;
185 });
186 return failure();
187 }
188
189 instanceError([&](auto &diag) {
190 diag << "invalid parameter value " << value;
191 return false;
192 });
193 return failure();
194}
195
196/// Check parameter specified by `value` to see if it is valid within the scope
197/// of the specified module `module`. If not, emit an error at the location of
198/// `usingOp` and return failure, otherwise return success. If `usingOp` is
199/// null, then no diagnostic is generated.
200///
201/// If `disallowParamRefs` is true, then parameter references are not allowed.
202LogicalResult hw::checkParameterInContext(Attribute value, Operation *module,
203 Operation *usingOp,
204 bool disallowParamRefs) {
206 [&](const std::function<bool(InFlightDiagnostic &)> &fn) {
207 if (usingOp) {
208 auto diag = usingOp->emitOpError();
209 if (fn(diag))
210 diag.attachNote(module->getLoc()) << "module declared here";
211 }
212 };
213
214 return checkParameterInContext(value,
215 module->getAttrOfType<ArrayAttr>("parameters"),
216 emitError, disallowParamRefs);
217}
218
219/// Return true if the specified attribute tree is made up of nodes that are
220/// valid in a parameter expression.
221bool hw::isValidParameterExpression(Attribute attr, Operation *module) {
222 return succeeded(checkParameterInContext(attr, module, nullptr, false));
223}
224
226 const ModulePortInfo &info,
227 Region &bodyRegion)
228 : info(info) {
229 inputArgs.resize(info.sizeInputs());
230 for (auto [i, barg] : llvm::enumerate(bodyRegion.getArguments())) {
231 inputIdx[info.at(i).name.str()] = i;
232 inputArgs[i] = barg;
233 }
234
236 for (auto [i, outputInfo] : llvm::enumerate(info.getOutputs())) {
237 outputIdx[outputInfo.name.str()] = i;
238 }
239}
240
241void HWModulePortAccessor::setOutput(unsigned i, Value v) {
242 assert(outputOperands.size() > i && "invalid output index");
243 assert(outputOperands[i] == Value() && "output already set");
244 outputOperands[i] = v;
245}
246
248 assert(inputArgs.size() > i && "invalid input index");
249 return inputArgs[i];
250}
251Value HWModulePortAccessor::getInput(StringRef name) {
252 return getInput(inputIdx.find(name.str())->second);
253}
254void HWModulePortAccessor::setOutput(StringRef name, Value v) {
255 setOutput(outputIdx.find(name.str())->second, v);
256}
257
258//===----------------------------------------------------------------------===//
259// Declarative Canonicalization Patterns
260//===----------------------------------------------------------------------===//
261
262namespace {
263#include "circt/Dialect/HW/HWCanonicalization.cpp.inc"
264} // namespace
265
266//===----------------------------------------------------------------------===//
267// ConstantOp
268//===----------------------------------------------------------------------===//
269
270void ConstantOp::print(OpAsmPrinter &p) {
271 p << " ";
272 if (getType() == getValueAttr().getType()) {
273 p.printAttribute(getValueAttr());
274 } else {
275 // The result is a type alias of the value's type.
276 p.printAttributeWithoutType(getValueAttr());
277 p << " : " << getType();
278 }
279 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
280}
281
282ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) {
283 auto loc = parser.getCurrentLocation();
284 APInt value;
285 Type type;
286 // Booleans may omit their type, in which case they are `i1`.
287 bool isTrue = succeeded(parser.parseOptionalKeyword("true"));
288 if (isTrue || succeeded(parser.parseOptionalKeyword("false"))) {
289 value = APInt(1, isTrue);
290 if (failed(parser.parseOptionalColon()))
291 type = parser.getBuilder().getI1Type();
292 } else if (parser.parseInteger(value) || parser.parseColon()) {
293 return failure();
294 }
295 if (!type && parser.parseType(type))
296 return failure();
297 if (parser.parseOptionalAttrDict(result.attributes))
298 return failure();
299
300 // The type may be a type alias of an integer type, which determines the
301 // value's type.
302 auto intType = type_dyn_cast<IntegerType>(type);
303 if (!intType)
304 return parser.emitError(loc, "expected an integer type, but got ") << type;
305 unsigned width = intType.getWidth();
306 if (value.isNegative() ? value.getSignificantBits() > width
307 : value.getActiveBits() > width)
308 return parser.emitError(loc, "constant out of range for type ") << type;
309
310 result.addAttribute("value",
311 IntegerAttr::get(intType, value.sextOrTrunc(width)));
312 result.addTypes(type);
313 return success();
314}
315
316LogicalResult ConstantOp::verify() {
317 // The result type may be a type alias of an integer type.
318 auto intType = type_dyn_cast<IntegerType>(getType());
319 if (!intType)
320 return emitError("hw.constant must have an integer result type");
321
322 // The result type must be the same as the attribute's type.
323 if (getValueAttr().getType() != intType)
324 return emitError("hw.constant attribute type ")
325 << getValueAttr().getType() << " doesn't match the result type "
326 << intType;
327
328 return success();
329}
330
331/// Build a ConstantOp from an APInt, infering the result type from the
332/// width of the APInt.
333void ConstantOp::build(OpBuilder &builder, OperationState &result,
334 const APInt &value) {
335
336 auto type = IntegerType::get(builder.getContext(), value.getBitWidth());
337 auto attr = builder.getIntegerAttr(type, value);
338 return build(builder, result, type, attr);
339}
340
341/// Build a ConstantOp from an APInt, infering the result type from the
342/// width of the APInt.
343void ConstantOp::build(OpBuilder &builder, OperationState &result,
344 IntegerAttr value) {
345 return build(builder, result, value.getType(), value);
346}
347
348/// This builder allows construction of small signed integers like 0, 1, -1
349/// matching a specified MLIR IntegerType. This shouldn't be used for general
350/// constant folding because it only works with values that can be expressed in
351/// an int64_t. Use APInt's instead.
352void ConstantOp::build(OpBuilder &builder, OperationState &result, Type type,
353 int64_t value) {
354 auto intType = type_cast<IntegerType>(type);
355 build(
356 builder, result, type,
357 builder.getIntegerAttr(intType, APInt(intType.getWidth(), (uint64_t)value,
358 /*isSigned=*/true,
359 /*implicitTrunc=*/true)));
360}
361
362void ConstantOp::getAsmResultNames(
363 function_ref<void(Value, StringRef)> setNameFn) {
364 Type intTy = getType();
365 auto intCst = getValue();
366
367 // Sugar i1 constants with 'true' and 'false'.
368 if (type_cast<IntegerType>(intTy).getWidth() == 1)
369 return setNameFn(getResult(), intCst.isZero() ? "false" : "true");
370
371 // Otherwise, build a complex name with the value and type, using the alias
372 // name for aliased types.
373 SmallVector<char, 32> specialNameBuffer;
374 llvm::raw_svector_ostream specialName(specialNameBuffer);
375 specialName << 'c' << intCst << '_';
376 if (auto alias = dyn_cast<TypeAliasType>(intTy))
377 specialName << alias.getRef().getLeafReference().getValue();
378 else
379 specialName << intTy;
380 setNameFn(getResult(), specialName.str());
381}
382
383OpFoldResult ConstantOp::fold(FoldAdaptor adaptor) {
384 assert(adaptor.getOperands().empty() && "constant has no operands");
385 return getValueAttr();
386}
387
388//===----------------------------------------------------------------------===//
389// WireOp
390//===----------------------------------------------------------------------===//
391
392/// Check whether an operation has any additional attributes set beyond its
393/// standard list of attributes returned by `getAttributeNames`.
394template <class Op>
395static bool hasAdditionalAttributes(Op op,
396 ArrayRef<StringRef> ignoredAttrs = {}) {
397 auto names = op.getAttributeNames();
398 llvm::SmallDenseSet<StringRef> nameSet;
399 nameSet.reserve(names.size() + ignoredAttrs.size());
400 nameSet.insert(names.begin(), names.end());
401 nameSet.insert(ignoredAttrs.begin(), ignoredAttrs.end());
402 return llvm::any_of(op->getAttrs(), [&](auto namedAttr) {
403 return !nameSet.contains(namedAttr.getName());
404 });
405}
406
407void WireOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
408 // If the wire has an optional 'name' attribute, use it.
409 auto nameAttr = (*this)->getAttrOfType<StringAttr>("name");
410 if (nameAttr && !nameAttr.getValue().empty())
411 setNameFn(getResult(), nameAttr.getValue());
412}
413
414std::optional<size_t> WireOp::getTargetResultIndex() { return 0; }
415
416OpFoldResult WireOp::fold(FoldAdaptor adaptor) {
417 // If the wire has no additional attributes, no name, and no symbol, just
418 // forward its input.
419 if (!hasAdditionalAttributes(*this, {"sv.namehint"}) && !getNameAttr() &&
420 !getInnerSymAttr())
421 return getInput();
422 return {};
423}
424
425LogicalResult WireOp::canonicalize(WireOp wire, PatternRewriter &rewriter) {
426 // Block if the wire has any attributes.
427 if (hasAdditionalAttributes(wire, {"sv.namehint"}))
428 return failure();
429
430 // If the wire has a symbol, then we can't delete it.
431 if (wire.getInnerSymAttr())
432 return failure();
433
434 // If the wire is self-referential (its input is itself), we can't remove it.
435 if (wire.getInput() == wire.getResult())
436 return failure();
437
438 // If the wire has a name or an `sv.namehint` attribute, propagate it as an
439 // `sv.namehint` to the expression.
440 if (auto *inputOp = wire.getInput().getDefiningOp())
441 if (auto name = chooseName(wire, inputOp))
442 rewriter.modifyOpInPlace(inputOp,
443 [&] { inputOp->setAttr("sv.namehint", name); });
444
445 rewriter.replaceOp(wire, wire.getInput());
446 return success();
447}
448
449//===----------------------------------------------------------------------===//
450// AggregateConstantOp
451//===----------------------------------------------------------------------===//
452
453static LogicalResult checkAttributes(Operation *op, Attribute attr, Type type) {
454 // If this is a type alias, get the underlying type.
455 if (auto typeAlias = dyn_cast<TypeAliasType>(type))
456 type = typeAlias.getCanonicalType();
457
458 if (auto structType = dyn_cast<StructType>(type)) {
459 auto arrayAttr = dyn_cast<ArrayAttr>(attr);
460 if (!arrayAttr)
461 return op->emitOpError("expected array attribute for constant of type ")
462 << type;
463 if (structType.getElements().size() != arrayAttr.size())
464 return op->emitOpError("array attribute (")
465 << arrayAttr.size() << ") has wrong size for struct constant ("
466 << structType.getElements().size() << ")";
467
468 for (auto [attr, fieldInfo] :
469 llvm::zip(arrayAttr.getValue(), structType.getElements())) {
470 if (failed(checkAttributes(op, attr, fieldInfo.type)))
471 return failure();
472 }
473 } else if (auto arrayType = dyn_cast<ArrayType>(type)) {
474 auto arrayAttr = dyn_cast<ArrayAttr>(attr);
475 if (!arrayAttr)
476 return op->emitOpError("expected array attribute for constant of type ")
477 << type;
478 if (arrayType.getNumElements() != arrayAttr.size())
479 return op->emitOpError("array attribute (")
480 << arrayAttr.size() << ") has wrong size for array constant ("
481 << arrayType.getNumElements() << ")";
482
483 auto elementType = arrayType.getElementType();
484 for (auto attr : arrayAttr.getValue()) {
485 if (failed(checkAttributes(op, attr, elementType)))
486 return failure();
487 }
488 } else if (auto arrayType = dyn_cast<UnpackedArrayType>(type)) {
489 auto arrayAttr = dyn_cast<ArrayAttr>(attr);
490 if (!arrayAttr)
491 return op->emitOpError("expected array attribute for constant of type ")
492 << type;
493 auto elementType = arrayType.getElementType();
494 if (arrayType.getNumElements() != arrayAttr.size())
495 return op->emitOpError("array attribute (")
496 << arrayAttr.size()
497 << ") has wrong size for unpacked array constant ("
498 << arrayType.getNumElements() << ")";
499
500 for (auto attr : arrayAttr.getValue()) {
501 if (failed(checkAttributes(op, attr, elementType)))
502 return failure();
503 }
504 } else if (auto enumType = dyn_cast<EnumType>(type)) {
505 auto stringAttr = dyn_cast<StringAttr>(attr);
506 if (!stringAttr)
507 return op->emitOpError("expected string attribute for constant of type ")
508 << type;
509 } else if (auto intType = dyn_cast<IntegerType>(type)) {
510 // Check the attribute kind is correct.
511 auto intAttr = dyn_cast<IntegerAttr>(attr);
512 if (!intAttr)
513 return op->emitOpError("expected integer attribute for constant of type ")
514 << type;
515 // Check the bitwidth is correct.
516 if (intAttr.getValue().getBitWidth() != intType.getWidth())
517 return op->emitOpError("hw.constant attribute bitwidth "
518 "doesn't match return type");
519 } else if (auto typedAttr = dyn_cast<TypedAttr>(attr)) {
520 if (typedAttr.getType() != type)
521 return op->emitOpError("typed attr doesn't match the return type ")
522 << type;
523 } else {
524 return op->emitOpError("unknown element type ") << type;
525 }
526 return success();
527}
528
529LogicalResult AggregateConstantOp::verify() {
530 return checkAttributes(*this, getFieldsAttr(), getType());
531}
532
533OpFoldResult AggregateConstantOp::fold(FoldAdaptor) { return getFieldsAttr(); }
534
535//===----------------------------------------------------------------------===//
536// ParamValueOp
537//===----------------------------------------------------------------------===//
538
539static ParseResult parseParamValue(OpAsmParser &p, Attribute &value,
540 Type &resultType) {
541 if (p.parseType(resultType) || p.parseEqual() ||
542 p.parseAttribute(value, resultType))
543 return failure();
544 return success();
545}
546
547static void printParamValue(OpAsmPrinter &p, Operation *, Attribute value,
548 Type resultType) {
549 p << resultType << " = ";
550 p.printAttributeWithoutType(value);
551}
552
553LogicalResult ParamValueOp::verify() {
554 // Check that the attribute expression is valid in this module.
556 getValue(), (*this)->getParentOfType<hw::HWModuleOp>(), *this);
557}
558
559OpFoldResult ParamValueOp::fold(FoldAdaptor adaptor) {
560 assert(adaptor.getOperands().empty() && "hw.param.value has no operands");
561 return getValueAttr();
562}
563
564//===----------------------------------------------------------------------===//
565// HWModuleOp
566//===----------------------------------------------------------------------===/
567
568/// Return true if isAnyModule or instance.
569bool hw::isAnyModuleOrInstance(Operation *moduleOrInstance) {
570 return isa<HWModuleLike, InstanceOp>(moduleOrInstance);
571}
572
573/// Return the signature for a module as a function type from the module itself
574/// or from an hw::InstanceOp.
575FunctionType hw::getModuleType(Operation *moduleOrInstance) {
576 return TypeSwitch<Operation *, FunctionType>(moduleOrInstance)
577 .Case<InstanceOp>([](auto instance) {
578 SmallVector<Type> inputs(instance->getOperandTypes());
579 SmallVector<Type> results(instance->getResultTypes());
580 return FunctionType::get(instance->getContext(), inputs, results);
581 })
582 .Case<HWModuleLike>(
583 [](auto mod) { return mod.getHWModuleType().getFuncType(); })
584 .Default([](Operation *op) {
585 return cast<FunctionType>(
586 cast<mlir::FunctionOpInterface>(op).getFunctionType());
587 });
588}
589
590/// Return the name to use for the Verilog module that we're referencing
591/// here. This is typically the symbol, but can be overridden with the
592/// verilogName attribute.
593StringAttr hw::getVerilogModuleNameAttr(Operation *module) {
594 auto nameAttr = module->getAttrOfType<StringAttr>("verilogName");
595 if (nameAttr)
596 return nameAttr;
597
598 if (auto symbol = dyn_cast<mlir::SymbolOpInterface>(module))
599 return symbol.getNameAttr();
600 return {};
601}
602
603template <typename ModuleTy>
604static void
605buildModule(OpBuilder &builder, OperationState &result, StringAttr name,
606 const ModulePortInfo &ports, ArrayAttr parameters,
607 ArrayRef<NamedAttribute> attributes, StringAttr comment) {
608 using namespace mlir::function_interface_impl;
609
610 // Add an attribute for the name.
611 result.addAttribute(ModuleTy::getSymNameAttrName(result.name), name);
612
613 SmallVector<Attribute> perPortAttrs;
614 SmallVector<ModulePort> portTypes;
615
616 for (auto elt : ports) {
617 portTypes.push_back(elt);
618 llvm::SmallVector<NamedAttribute> portAttrs;
619 if (elt.attrs)
620 llvm::copy(elt.attrs, std::back_inserter(portAttrs));
621 perPortAttrs.push_back(builder.getDictionaryAttr(portAttrs));
622 }
623
624 // Allow clients to pass in null for the parameters list.
625 if (!parameters)
626 parameters = builder.getArrayAttr({});
627
628 // Record the argument and result types as an attribute.
629 auto type = ModuleType::get(builder.getContext(), portTypes);
630 result.addAttribute(ModuleTy::getModuleTypeAttrName(result.name),
631 TypeAttr::get(type));
632 result.addAttribute("per_port_attrs",
633 arrayOrEmpty(builder.getContext(), perPortAttrs));
634 result.addAttribute("parameters", parameters);
635 if (!comment)
636 comment = builder.getStringAttr("");
637 result.addAttribute("comment", comment);
638 result.addAttributes(attributes);
639 result.addRegion();
640}
641
642/// Internal implementation of argument/result insertion and removal on modules.
644 MLIRContext *context, ArrayRef<std::pair<unsigned, PortInfo>> insertArgs,
645 ArrayRef<unsigned> removeArgs, ArrayRef<Attribute> oldArgNames,
646 ArrayRef<Type> oldArgTypes, ArrayRef<Attribute> oldArgAttrs,
647 ArrayRef<Location> oldArgLocs, SmallVector<Attribute> &newArgNames,
648 SmallVector<Type> &newArgTypes, SmallVector<Attribute> &newArgAttrs,
649 SmallVector<Location> &newArgLocs, Block *body = nullptr) {
650
651#ifndef NDEBUG
652 // Check that the `insertArgs` and `removeArgs` indices are in ascending
653 // order.
654 assert(llvm::is_sorted(insertArgs,
655 [](auto &a, auto &b) { return a.first < b.first; }) &&
656 "insertArgs must be in ascending order");
657 assert(llvm::is_sorted(removeArgs, [](auto &a, auto &b) { return a < b; }) &&
658 "removeArgs must be in ascending order");
659#endif
660
661 auto oldArgCount = oldArgTypes.size();
662 auto newArgCount = oldArgCount + insertArgs.size() - removeArgs.size();
663 assert((int)newArgCount >= 0);
664
665 newArgNames.reserve(newArgCount);
666 newArgTypes.reserve(newArgCount);
667 newArgAttrs.reserve(newArgCount);
668 newArgLocs.reserve(newArgCount);
669
670 auto emptyDictAttr = DictionaryAttr::get(context, {});
671 auto unknownLoc = UnknownLoc::get(context);
672
673 BitVector erasedIndices;
674 if (body)
675 erasedIndices.resize(oldArgCount + insertArgs.size());
676
677 for (unsigned argIdx = 0, idx = 0; argIdx <= oldArgCount; ++argIdx, ++idx) {
678 // Insert new ports at this position.
679 while (!insertArgs.empty() && insertArgs[0].first == argIdx) {
680 auto port = insertArgs[0].second;
681 if (port.dir == ModulePort::Direction::InOut &&
682 !isa<InOutType>(port.type))
683 port.type = InOutType::get(port.type);
684 newArgNames.push_back(port.name);
685 newArgTypes.push_back(port.type);
686 newArgAttrs.push_back(port.attrs ? port.attrs : emptyDictAttr);
687 insertArgs = insertArgs.drop_front();
688 LocationAttr loc = port.loc ? port.loc : unknownLoc;
689 newArgLocs.push_back(loc);
690 if (body)
691 body->insertArgument(idx++, port.type, loc);
692 }
693 if (argIdx == oldArgCount)
694 break;
695
696 // Migrate the old port at this position.
697 bool removed = false;
698 while (!removeArgs.empty() && removeArgs[0] == argIdx) {
699 removeArgs = removeArgs.drop_front();
700 removed = true;
701 }
702
703 if (removed) {
704 if (body)
705 erasedIndices.set(idx);
706 } else {
707 newArgNames.push_back(oldArgNames[argIdx]);
708 newArgTypes.push_back(oldArgTypes[argIdx]);
709 newArgAttrs.push_back(oldArgAttrs.empty() ? emptyDictAttr
710 : oldArgAttrs[argIdx]);
711 newArgLocs.push_back(oldArgLocs[argIdx]);
712 }
713 }
714
715 if (body)
716 body->eraseArguments(erasedIndices);
717
718 assert(newArgNames.size() == newArgCount);
719 assert(newArgTypes.size() == newArgCount);
720 assert(newArgAttrs.size() == newArgCount);
721 assert(newArgLocs.size() == newArgCount);
722}
723
724/// Insert and remove ports of a module. The insertion and removal indices must
725/// be in ascending order. The indices refer to the port positions before any
726/// insertion or removal occurs. Ports inserted at the same index will appear in
727/// the module in the same order as they were listed in the `insert*` array.
728///
729/// The operation must be any of the module-like operations.
730///
731/// This is marked deprecated as it's only used from HandshakeToHW and
732/// PortConverter and is likely broken and not currently tested. Users of this
733/// are still written dealing with input and output ports separately, which is
734/// an old and broken style.
735[[deprecated]] static void
736modifyModulePorts(Operation *op,
737 ArrayRef<std::pair<unsigned, PortInfo>> insertInputs,
738 ArrayRef<std::pair<unsigned, PortInfo>> insertOutputs,
739 ArrayRef<unsigned> removeInputs,
740 ArrayRef<unsigned> removeOutputs, Block *body = nullptr) {
741 auto moduleOp = cast<HWModuleLike>(op);
742 auto *context = moduleOp.getContext();
743
744 // Dig up the old argument and result data.
745 auto oldArgNames = moduleOp.getInputNames();
746 auto oldArgTypes = moduleOp.getInputTypes();
747 auto oldArgAttrs = moduleOp.getAllInputAttrs();
748 auto oldArgLocs = moduleOp.getInputLocs();
749
750 auto oldResultNames = moduleOp.getOutputNames();
751 auto oldResultTypes = moduleOp.getOutputTypes();
752 auto oldResultAttrs = moduleOp.getAllOutputAttrs();
753 auto oldResultLocs = moduleOp.getOutputLocs();
754
755 // Modify the ports.
756 SmallVector<Attribute> newArgNames, newResultNames;
757 SmallVector<Type> newArgTypes, newResultTypes;
758 SmallVector<Attribute> newArgAttrs, newResultAttrs;
759 SmallVector<Location> newArgLocs, newResultLocs;
760
761 modifyModuleArgs(context, insertInputs, removeInputs, oldArgNames,
762 oldArgTypes, oldArgAttrs, oldArgLocs, newArgNames,
763 newArgTypes, newArgAttrs, newArgLocs, body);
764
765 modifyModuleArgs(context, insertOutputs, removeOutputs, oldResultNames,
766 oldResultTypes, oldResultAttrs, oldResultLocs,
767 newResultNames, newResultTypes, newResultAttrs,
768 newResultLocs);
769
770 // Update the module operation types and attributes.
771 auto fnty = FunctionType::get(context, newArgTypes, newResultTypes);
772 auto modty = detail::fnToMod(fnty, newArgNames, newResultNames);
773 moduleOp.setHWModuleType(modty);
774 moduleOp.setAllInputAttrs(newArgAttrs);
775 moduleOp.setAllOutputAttrs(newResultAttrs);
776
777 newArgLocs.append(newResultLocs.begin(), newResultLocs.end());
778 moduleOp.setAllPortLocs(newArgLocs);
779}
780
781void HWModuleOp::build(OpBuilder &builder, OperationState &result,
782 StringAttr name, const ModulePortInfo &ports,
783 ArrayAttr parameters,
784 ArrayRef<NamedAttribute> attributes, StringAttr comment,
785 bool shouldEnsureTerminator) {
786 buildModule<HWModuleOp>(builder, result, name, ports, parameters, attributes,
787 comment);
788
789 // Create a region and a block for the body.
790 auto *bodyRegion = result.regions[0].get();
791 Block *body = new Block();
792 bodyRegion->push_back(body);
793
794 // Add arguments to the body block.
795 auto unknownLoc = builder.getUnknownLoc();
796 for (auto port : ports.getInputs()) {
797 auto loc = port.loc ? Location(port.loc) : unknownLoc;
798 auto type = port.type;
799 if (port.isInOut() && !isa<InOutType>(type))
800 type = InOutType::get(type);
801 body->addArgument(type, loc);
802 }
803
804 // Add result ports attribute.
805 auto unknownLocAttr = cast<LocationAttr>(unknownLoc);
806 SmallVector<Attribute> resultLocs;
807 for (auto port : ports.getOutputs())
808 resultLocs.push_back(port.loc ? port.loc : unknownLocAttr);
809 result.addAttribute("result_locs", builder.getArrayAttr(resultLocs));
810
811 if (shouldEnsureTerminator)
812 HWModuleOp::ensureTerminator(*bodyRegion, builder, result.location);
813}
814
815void HWModuleOp::build(OpBuilder &builder, OperationState &result,
816 StringAttr name, ArrayRef<PortInfo> ports,
817 ArrayAttr parameters,
818 ArrayRef<NamedAttribute> attributes,
819 StringAttr comment) {
820 build(builder, result, name, ModulePortInfo(ports), parameters, attributes,
821 comment);
822}
823
824void HWModuleOp::build(OpBuilder &builder, OperationState &odsState,
825 StringAttr name, const ModulePortInfo &ports,
826 HWModuleBuilder modBuilder, ArrayAttr parameters,
827 ArrayRef<NamedAttribute> attributes,
828 StringAttr comment) {
829 build(builder, odsState, name, ports, parameters, attributes, comment,
830 /*shouldEnsureTerminator=*/false);
831 auto *bodyRegion = odsState.regions[0].get();
832 OpBuilder::InsertionGuard guard(builder);
833 auto accessor = HWModulePortAccessor(odsState.location, ports, *bodyRegion);
834 builder.setInsertionPointToEnd(&bodyRegion->front());
835 modBuilder(builder, accessor);
836 // Create output operands.
837 llvm::SmallVector<Value> outputOperands = accessor.getOutputOperands();
838 hw::OutputOp::create(builder, odsState.location, outputOperands);
839}
840
841void HWModuleOp::modifyPorts(
842 ArrayRef<std::pair<unsigned, PortInfo>> insertInputs,
843 ArrayRef<std::pair<unsigned, PortInfo>> insertOutputs,
844 ArrayRef<unsigned> eraseInputs, ArrayRef<unsigned> eraseOutputs) {
845 modifyModulePorts(*this, insertInputs, insertOutputs, eraseInputs,
846 eraseOutputs);
847}
848
849/// Return the name to use for the Verilog module that we're referencing
850/// here. This is typically the symbol, but can be overridden with the
851/// verilogName attribute.
852StringAttr HWModuleExternOp::getVerilogModuleNameAttr() {
853 if (auto vName = getVerilogNameAttr())
854 return vName;
855
856 return getSymNameAttr();
857}
858
859StringAttr HWModuleGeneratedOp::getVerilogModuleNameAttr() {
860 if (auto vName = getVerilogNameAttr()) {
861 return vName;
862 }
863 return getSymNameAttr();
864}
865
866void HWModuleExternOp::build(OpBuilder &builder, OperationState &result,
867 StringAttr name, const ModulePortInfo &ports,
868 StringRef verilogName, ArrayAttr parameters,
869 ArrayRef<NamedAttribute> attributes) {
870 buildModule<HWModuleExternOp>(builder, result, name, ports, parameters,
871 attributes, {});
872
873 // Add the port locations.
874 LocationAttr unknownLoc = builder.getUnknownLoc();
875 SmallVector<Attribute> portLocs;
876 for (auto elt : ports)
877 portLocs.push_back(elt.loc ? elt.loc : unknownLoc);
878 result.addAttribute("port_locs", builder.getArrayAttr(portLocs));
879
880 if (!verilogName.empty())
881 result.addAttribute("verilogName", builder.getStringAttr(verilogName));
882}
883
884void HWModuleExternOp::build(OpBuilder &builder, OperationState &result,
885 StringAttr name, ArrayRef<PortInfo> ports,
886 StringRef verilogName, ArrayAttr parameters,
887 ArrayRef<NamedAttribute> attributes) {
888 build(builder, result, name, ModulePortInfo(ports), verilogName, parameters,
889 attributes);
890}
891
892void HWModuleExternOp::modifyPorts(
893 ArrayRef<std::pair<unsigned, PortInfo>> insertInputs,
894 ArrayRef<std::pair<unsigned, PortInfo>> insertOutputs,
895 ArrayRef<unsigned> eraseInputs, ArrayRef<unsigned> eraseOutputs) {
896 modifyModulePorts(*this, insertInputs, insertOutputs, eraseInputs,
897 eraseOutputs);
898}
899
900void HWModuleExternOp::appendOutputs(
901 ArrayRef<std::pair<StringAttr, Value>> outputs) {}
902
903void HWModuleGeneratedOp::build(OpBuilder &builder, OperationState &result,
904 FlatSymbolRefAttr genKind, StringAttr name,
905 const ModulePortInfo &ports,
906 StringRef verilogName, ArrayAttr parameters,
907 ArrayRef<NamedAttribute> attributes) {
908 buildModule<HWModuleGeneratedOp>(builder, result, name, ports, parameters,
909 attributes, {});
910 // Add the port locations.
911 LocationAttr unknownLoc = builder.getUnknownLoc();
912 SmallVector<Attribute> portLocs;
913 for (auto elt : ports)
914 portLocs.push_back(elt.loc ? elt.loc : unknownLoc);
915 result.addAttribute("port_locs", builder.getArrayAttr(portLocs));
916
917 result.addAttribute("generatorKind", genKind);
918 if (!verilogName.empty())
919 result.addAttribute("verilogName", builder.getStringAttr(verilogName));
920}
921
922void HWModuleGeneratedOp::build(OpBuilder &builder, OperationState &result,
923 FlatSymbolRefAttr genKind, StringAttr name,
924 ArrayRef<PortInfo> ports, StringRef verilogName,
925 ArrayAttr parameters,
926 ArrayRef<NamedAttribute> attributes) {
927 build(builder, result, genKind, name, ModulePortInfo(ports), verilogName,
928 parameters, attributes);
929}
930
931void HWModuleGeneratedOp::modifyPorts(
932 ArrayRef<std::pair<unsigned, PortInfo>> insertInputs,
933 ArrayRef<std::pair<unsigned, PortInfo>> insertOutputs,
934 ArrayRef<unsigned> eraseInputs, ArrayRef<unsigned> eraseOutputs) {
935 modifyModulePorts(*this, insertInputs, insertOutputs, eraseInputs,
936 eraseOutputs);
937}
938
939void HWModuleGeneratedOp::appendOutputs(
940 ArrayRef<std::pair<StringAttr, Value>> outputs) {}
941
942static bool hasAttribute(StringRef name, ArrayRef<NamedAttribute> attrs) {
943 for (auto &argAttr : attrs)
944 if (argAttr.getName() == name)
945 return true;
946 return false;
947}
948
949template <typename ModuleTy>
950static ParseResult parseHWModuleOp(OpAsmParser &parser,
951 OperationState &result) {
952
953 using namespace mlir::function_interface_impl;
954 auto builder = parser.getBuilder();
955 auto loc = parser.getCurrentLocation();
956
957 // Parse the visibility attribute.
958 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
959
960 // Parse the name as a symbol.
961 StringAttr nameAttr;
962 if (parser.parseSymbolName(nameAttr,
963 ModuleTy::getSymNameAttrName(result.name),
964 result.attributes))
965 return failure();
966
967 // Parse the generator information.
968 FlatSymbolRefAttr kindAttr;
969 if constexpr (std::is_same_v<ModuleTy, HWModuleGeneratedOp>) {
970 if (parser.parseComma() ||
971 parser.parseAttribute(kindAttr, "generatorKind", result.attributes)) {
972 return failure();
973 }
974 }
975
976 // Parse the parameters.
977 ArrayAttr parameters;
978 if (parseOptionalParameterList(parser, parameters))
979 return failure();
980
981 SmallVector<module_like_impl::PortParse> ports;
982 TypeAttr modType;
983 if (failed(module_like_impl::parseModuleSignature(parser, ports, modType)))
984 return failure();
985
986 // Parse the attribute dict.
987 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
988 return failure();
989
990 if (hasAttribute("parameters", result.attributes)) {
991 parser.emitError(loc, "explicit `parameters` attributes not allowed");
992 return failure();
993 }
994
995 result.addAttribute("parameters", parameters);
996 result.addAttribute(ModuleTy::getModuleTypeAttrName(result.name), modType);
997
998 // Convert the specified array of dictionary attrs (which may have null
999 // entries) to an ArrayAttr of dictionaries.
1000 SmallVector<Attribute> attrs;
1001 for (auto &port : ports)
1002 attrs.push_back(port.attrs ? port.attrs : builder.getDictionaryAttr({}));
1003 // Add the attributes to the ports.
1004 auto nonEmptyAttrsFn = [](Attribute attr) {
1005 return attr && !cast<DictionaryAttr>(attr).empty();
1006 };
1007 if (llvm::any_of(attrs, nonEmptyAttrsFn))
1008 result.addAttribute(ModuleTy::getPerPortAttrsAttrName(result.name),
1009 builder.getArrayAttr(attrs));
1010
1011 // Add the port locations.
1012 auto unknownLoc = builder.getUnknownLoc();
1013 auto nonEmptyLocsFn = [unknownLoc](Attribute attr) {
1014 return attr && cast<Location>(attr) != unknownLoc;
1015 };
1016 SmallVector<Attribute> locs;
1017 StringAttr portLocsAttrName;
1018 if constexpr (std::is_same_v<ModuleTy, HWModuleOp>) {
1019 // Plain modules only store the output port locations, as the input port
1020 // locations will be stored in the basic block arguments.
1021 portLocsAttrName = ModuleTy::getResultLocsAttrName(result.name);
1022 for (auto &port : ports)
1023 if (port.direction == ModulePort::Direction::Output)
1024 locs.push_back(port.sourceLoc ? Location(*port.sourceLoc) : unknownLoc);
1025 } else {
1026 // All other modules store all port locations in a single array.
1027 portLocsAttrName = ModuleTy::getPortLocsAttrName(result.name);
1028 for (auto &port : ports)
1029 locs.push_back(port.sourceLoc ? Location(*port.sourceLoc) : unknownLoc);
1030 }
1031 if (llvm::any_of(locs, nonEmptyLocsFn))
1032 result.addAttribute(portLocsAttrName, builder.getArrayAttr(locs));
1033
1034 // Add the entry block arguments.
1035 SmallVector<OpAsmParser::Argument, 4> entryArgs;
1036 for (auto &port : ports)
1037 if (port.direction != ModulePort::Direction::Output)
1038 entryArgs.push_back(port);
1039
1040 // Parse the optional function body.
1041 auto *body = result.addRegion();
1042 if (std::is_same_v<ModuleTy, HWModuleOp>) {
1043 if (parser.parseRegion(*body, entryArgs))
1044 return failure();
1045
1046 HWModuleOp::ensureTerminator(*body, parser.getBuilder(), result.location);
1047 }
1048 return success();
1049}
1050
1051ParseResult HWModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1052 return parseHWModuleOp<HWModuleOp>(parser, result);
1053}
1054
1055ParseResult HWModuleExternOp::parse(OpAsmParser &parser,
1056 OperationState &result) {
1057 return parseHWModuleOp<HWModuleExternOp>(parser, result);
1058}
1059
1060ParseResult HWModuleGeneratedOp::parse(OpAsmParser &parser,
1061 OperationState &result) {
1062 return parseHWModuleOp<HWModuleGeneratedOp>(parser, result);
1063}
1064
1065FunctionType getHWModuleOpType(Operation *op) {
1066 if (auto mod = dyn_cast<HWModuleLike>(op))
1067 return mod.getHWModuleType().getFuncType();
1068 return cast<FunctionType>(
1069 cast<mlir::FunctionOpInterface>(op).getFunctionType());
1070}
1071
1072template <typename ModuleTy>
1073static void printModuleOp(OpAsmPrinter &p, ModuleTy mod) {
1074 p << ' ';
1075 // Print the visibility of the module.
1076 StringRef visibilityAttrName =
1077 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
1078 if (auto visibility = mod.getOperation()->template getAttrOfType<StringAttr>(
1079 visibilityAttrName))
1080 p << visibility.getValue() << ' ';
1081
1082 // Print the operation and the function name.
1083 p.printSymbolName(mod.getName());
1084 if (auto gen = dyn_cast<HWModuleGeneratedOp>(mod.getOperation())) {
1085 p << ", ";
1086 p.printSymbolName(gen.getGeneratorKind());
1087 }
1088
1089 // Print the parameter list if present.
1090 printOptionalParameterList(p, mod.getOperation(), mod.getParameters());
1091
1093
1094 SmallVector<StringRef, 3> omittedAttrs;
1095 omittedAttrs.push_back(mod.getSymNameAttrName());
1096 if (isa<HWModuleGeneratedOp>(mod.getOperation()))
1097 omittedAttrs.push_back("generatorKind");
1098 if constexpr (std::is_same_v<ModuleTy, HWModuleOp>)
1099 omittedAttrs.push_back(mod.getResultLocsAttrName());
1100 else
1101 omittedAttrs.push_back(mod.getPortLocsAttrName());
1102 omittedAttrs.push_back(mod.getModuleTypeAttrName());
1103 omittedAttrs.push_back(mod.getPerPortAttrsAttrName());
1104 omittedAttrs.push_back(mod.getParametersAttrName());
1105 omittedAttrs.push_back(visibilityAttrName);
1106 if (auto cmt =
1107 mod.getOperation()->template getAttrOfType<StringAttr>("comment"))
1108 if (cmt.getValue().empty())
1109 omittedAttrs.push_back("comment");
1110
1111 mlir::function_interface_impl::printFunctionAttributes(p, mod.getOperation(),
1112 omittedAttrs);
1113}
1114
1115void HWModuleExternOp::print(OpAsmPrinter &p) { printModuleOp(p, *this); }
1116void HWModuleGeneratedOp::print(OpAsmPrinter &p) { printModuleOp(p, *this); }
1117
1118void HWModuleOp::print(OpAsmPrinter &p) {
1119 printModuleOp(p, *this);
1120
1121 // Print the body if this is not an external function.
1122 Region &body = getBody();
1123 if (!body.empty()) {
1124 p << " ";
1125 p.printRegion(body, /*printEntryBlockArgs=*/false,
1126 /*printBlockTerminators=*/true);
1127 }
1128}
1129
1130static LogicalResult verifyModuleCommon(HWModuleLike module) {
1131 assert(isa<HWModuleLike>(module) &&
1132 "verifier hook should only be called on modules");
1133
1134 if (auto portLocs = module->getAttrOfType<ArrayAttr>("port_locs"))
1135 if (!portLocs.empty() && portLocs.size() != module.getNumPorts())
1136 return module->emitOpError("requires ")
1137 << module.getNumPorts() << " port locations but got "
1138 << portLocs.size();
1139
1140 for (auto port : module.getPortList()) {
1141 auto result = success();
1142 port.type.walk([&](Type type) {
1143 if (failed(result))
1144 return;
1145 auto &dialect = type.getDialect();
1146 auto *interface =
1147 dialect.getRegisteredInterface<hw::HWModulePortTypeInterface>();
1148 if (!interface)
1149 return;
1150 result = interface->verifyHWModulePortType(
1151 [&] { return module->emitOpError(); }, port.dir, type);
1152 });
1153 if (failed(result))
1154 return result;
1155 }
1156
1157 SmallPtrSet<Attribute, 4> paramNames;
1158
1159 // Check parameter default values are sensible.
1160 for (auto param : module->getAttrOfType<ArrayAttr>("parameters")) {
1161 auto paramAttr = cast<ParamDeclAttr>(param);
1162
1163 // Check that we don't have any redundant parameter names. These are
1164 // resolved by string name: reuse of the same name would cause ambiguities.
1165 if (!paramNames.insert(paramAttr.getName()).second)
1166 return module->emitOpError("parameter ")
1167 << paramAttr << " has the same name as a previous parameter";
1168
1169 // Default values are allowed to be missing, check them if present.
1170 auto value = paramAttr.getValue();
1171 if (!value)
1172 continue;
1173
1174 auto typedValue = dyn_cast<TypedAttr>(value);
1175 if (!typedValue)
1176 return module->emitOpError("parameter ")
1177 << paramAttr << " should have a typed value; has value " << value;
1178
1179 if (typedValue.getType() != paramAttr.getType())
1180 return module->emitOpError("parameter ")
1181 << paramAttr << " should have type " << paramAttr.getType()
1182 << "; has type " << typedValue.getType();
1183
1184 // Verify that this is a valid parameter value, disallowing parameter
1185 // references. We could allow parameters to refer to each other in the
1186 // future with lexical ordering if there is a need.
1187 if (failed(checkParameterInContext(value, module, module,
1188 /*disallowParamRefs=*/true)))
1189 return failure();
1190 }
1191 return success();
1192}
1193
1194LogicalResult HWModuleOp::verify() {
1195 if (failed(verifyModuleCommon(*this)))
1196 return failure();
1197
1198 auto type = getModuleType();
1199 auto *body = getBodyBlock();
1200
1201 // Verify the number of block arguments.
1202 auto numInputs = type.getNumInputs();
1203 if (body->getNumArguments() != numInputs)
1204 return emitOpError("entry block must have ")
1205 << numInputs << " arguments to match module signature";
1206
1207 return success();
1208}
1209
1210LogicalResult HWModuleExternOp::verify() { return verifyModuleCommon(*this); }
1211
1212std::pair<StringAttr, BlockArgument>
1213HWModuleOp::insertInput(unsigned index, StringAttr name, Type ty) {
1214 // Find a unique name for the wire.
1215 Namespace ns;
1216 auto ports = getPortList();
1217 for (auto port : ports)
1218 ns.newName(port.name.getValue());
1219 auto nameAttr = StringAttr::get(getContext(), ns.newName(name.getValue()));
1220
1221 Block *body = getBodyBlock();
1222
1223 // Create a new port for the host clock.
1224 PortInfo port;
1225 port.name = nameAttr;
1227 port.type = ty;
1228 modifyModulePorts(getOperation(), {std::make_pair(index, port)}, {}, {}, {},
1229 body);
1230
1231 // Add a new argument.
1232 return {nameAttr, body->getArgument(index)};
1233}
1234
1235void HWModuleOp::insertOutputs(unsigned index,
1236 ArrayRef<std::pair<StringAttr, Value>> outputs) {
1237
1238 auto output = cast<OutputOp>(getBodyBlock()->getTerminator());
1239 assert(index <= output->getNumOperands() && "invalid output index");
1240
1241 // Rewrite the port list of the module.
1242 SmallVector<std::pair<unsigned, PortInfo>> indexedNewPorts;
1243 for (auto &[name, value] : outputs) {
1244 PortInfo port;
1245 port.name = name;
1247 port.type = value.getType();
1248 indexedNewPorts.emplace_back(index, port);
1249 }
1250 modifyModulePorts(getOperation(), {}, indexedNewPorts, {}, {},
1251 getBodyBlock());
1252
1253 // Rewrite the output op.
1254 for (auto &[name, value] : outputs)
1255 output->insertOperands(index++, value);
1256}
1257
1258void HWModuleOp::appendOutputs(ArrayRef<std::pair<StringAttr, Value>> outputs) {
1259 return insertOutputs(getNumOutputPorts(), outputs);
1260}
1261
1262void HWModuleOp::getAsmBlockArgumentNames(mlir::Region &region,
1263 mlir::OpAsmSetValueNameFn setNameFn) {
1264 getAsmBlockArgumentNamesImpl(region, setNameFn);
1265}
1266
1267void HWModuleExternOp::getAsmBlockArgumentNames(
1268 mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
1269 getAsmBlockArgumentNamesImpl(region, setNameFn);
1270}
1271
1272template <typename ModTy>
1273static SmallVector<Location> getAllPortLocs(ModTy module) {
1274 auto locs = module.getPortLocs();
1275 if (locs) {
1276 SmallVector<Location> retval;
1277 retval.reserve(locs->size());
1278 for (auto l : *locs)
1279 retval.push_back(cast<Location>(l));
1280 // Either we have a length of 0 or the correct length
1281 assert(!locs->size() || locs->size() == module.getNumPorts());
1282 return retval;
1283 }
1284 return SmallVector<Location>(module.getNumPorts(),
1285 UnknownLoc::get(module.getContext()));
1286}
1287
1288SmallVector<Location> HWModuleOp::getAllPortLocs() {
1289 SmallVector<Location> portLocs;
1290 portLocs.reserve(getNumPorts());
1291 auto resultLocs = getResultLocsAttr();
1292 unsigned inputCount = 0;
1293 auto modType = getModuleType();
1294 auto unknownLoc = UnknownLoc::get(getContext());
1295 auto *body = getBodyBlock();
1296 for (unsigned i = 0, e = getNumPorts(); i < e; ++i) {
1297 if (modType.isOutput(i)) {
1298 auto loc = resultLocs
1299 ? cast<Location>(
1300 resultLocs.getValue()[portLocs.size() - inputCount])
1301 : unknownLoc;
1302 portLocs.push_back(loc);
1303 } else {
1304 auto loc = body ? body->getArgument(inputCount).getLoc() : unknownLoc;
1305 portLocs.push_back(loc);
1306 ++inputCount;
1307 }
1308 }
1309 return portLocs;
1310}
1311
1312SmallVector<Location> HWModuleExternOp::getAllPortLocs() {
1313 return ::getAllPortLocs(*this);
1314}
1315
1316SmallVector<Location> HWModuleGeneratedOp::getAllPortLocs() {
1317 return ::getAllPortLocs(*this);
1318}
1319
1320void HWModuleOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
1321 SmallVector<Attribute> resultLocs;
1322 unsigned inputCount = 0;
1323 auto modType = getModuleType();
1324 auto *body = getBodyBlock();
1325 for (unsigned i = 0, e = getNumPorts(); i < e; ++i) {
1326 if (modType.isOutput(i))
1327 resultLocs.push_back(locs[i]);
1328 else
1329 body->getArgument(inputCount++).setLoc(cast<Location>(locs[i]));
1330 }
1331 setResultLocsAttr(ArrayAttr::get(getContext(), resultLocs));
1332}
1333
1334void HWModuleExternOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
1335 setPortLocsAttr(ArrayAttr::get(getContext(), locs));
1336}
1337
1338void HWModuleGeneratedOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
1339 setPortLocsAttr(ArrayAttr::get(getContext(), locs));
1340}
1341
1342template <typename ModTy>
1343static void setAllPortNames(ArrayRef<Attribute> names, ModTy module) {
1344 auto numInputs = module.getNumInputPorts();
1345 SmallVector<Attribute> argNames(names.begin(), names.begin() + numInputs);
1346 SmallVector<Attribute> resNames(names.begin() + numInputs, names.end());
1347 auto oldType = module.getModuleType();
1348 SmallVector<ModulePort> newPorts(oldType.getPorts().begin(),
1349 oldType.getPorts().end());
1350 for (size_t i = 0UL, e = newPorts.size(); i != e; ++i)
1351 newPorts[i].name = cast<StringAttr>(names[i]);
1352 auto newType = ModuleType::get(module.getContext(), newPorts);
1353 module.setModuleType(newType);
1354}
1355
1356void HWModuleOp::setAllPortNames(ArrayRef<Attribute> names) {
1357 ::setAllPortNames(names, *this);
1358}
1359
1360void HWModuleExternOp::setAllPortNames(ArrayRef<Attribute> names) {
1361 ::setAllPortNames(names, *this);
1362}
1363
1364void HWModuleGeneratedOp::setAllPortNames(ArrayRef<Attribute> names) {
1365 ::setAllPortNames(names, *this);
1366}
1367
1368ArrayRef<Attribute> HWModuleOp::getAllPortAttrs() {
1369 auto attrs = getPerPortAttrs();
1370 if (attrs && !attrs->empty())
1371 return attrs->getValue();
1372 return {};
1373}
1374
1375ArrayRef<Attribute> HWModuleExternOp::getAllPortAttrs() {
1376 auto attrs = getPerPortAttrs();
1377 if (attrs && !attrs->empty())
1378 return attrs->getValue();
1379 return {};
1380}
1381
1382ArrayRef<Attribute> HWModuleGeneratedOp::getAllPortAttrs() {
1383 auto attrs = getPerPortAttrs();
1384 if (attrs && !attrs->empty())
1385 return attrs->getValue();
1386 return {};
1387}
1388
1389void HWModuleOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
1390 setPerPortAttrsAttr(arrayOrEmpty(getContext(), attrs));
1391}
1392
1393void HWModuleExternOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
1394 setPerPortAttrsAttr(arrayOrEmpty(getContext(), attrs));
1395}
1396
1397void HWModuleGeneratedOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
1398 setPerPortAttrsAttr(arrayOrEmpty(getContext(), attrs));
1399}
1400
1401void HWModuleOp::removeAllPortAttrs() {
1402 setPerPortAttrsAttr(ArrayAttr::get(getContext(), {}));
1403}
1404
1405void HWModuleExternOp::removeAllPortAttrs() {
1406 setPerPortAttrsAttr(ArrayAttr::get(getContext(), {}));
1407}
1408
1409void HWModuleGeneratedOp::removeAllPortAttrs() {
1410 setPerPortAttrsAttr(ArrayAttr::get(getContext(), {}));
1411}
1412
1413// This probably does really unexpected stuff when you change the number of
1414
1415template <typename ModTy>
1416static void setHWModuleType(ModTy &mod, ModuleType type) {
1417 auto argAttrs = mod.getAllInputAttrs();
1418 auto resAttrs = mod.getAllOutputAttrs();
1419 mod.setModuleTypeAttr(TypeAttr::get(type));
1420 unsigned newNumArgs = type.getNumInputs();
1421 unsigned newNumResults = type.getNumOutputs();
1422
1423 auto emptyDict = DictionaryAttr::get(mod.getContext());
1424 argAttrs.resize(newNumArgs, emptyDict);
1425 resAttrs.resize(newNumResults, emptyDict);
1426
1427 SmallVector<Attribute> attrs;
1428 attrs.append(argAttrs.begin(), argAttrs.end());
1429 attrs.append(resAttrs.begin(), resAttrs.end());
1430
1431 if (attrs.empty())
1432 return mod.removeAllPortAttrs();
1433 mod.setAllPortAttrs(attrs);
1434}
1435
1436void HWModuleOp::setHWModuleType(ModuleType type) {
1437 return ::setHWModuleType(*this, type);
1438}
1439
1440void HWModuleExternOp::setHWModuleType(ModuleType type) {
1441 return ::setHWModuleType(*this, type);
1442}
1443
1444void HWModuleGeneratedOp::setHWModuleType(ModuleType type) {
1445 return ::setHWModuleType(*this, type);
1446}
1447
1448/// Lookup the generator for the symbol. This returns null on
1449/// invalid IR.
1450Operation *HWModuleGeneratedOp::getGeneratorKindOp() {
1451 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1452 return topLevelModuleOp.lookupSymbol(getGeneratorKind());
1453}
1454
1455LogicalResult
1456HWModuleGeneratedOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1457 auto *referencedKind =
1458 symbolTable.lookupNearestSymbolFrom(*this, getGeneratorKindAttr());
1459
1460 if (referencedKind == nullptr)
1461 return emitError("Cannot find generator definition '")
1462 << getGeneratorKind() << "'";
1463
1464 if (!isa<HWGeneratorSchemaOp>(referencedKind))
1465 return emitError("Symbol resolved to '")
1466 << referencedKind->getName()
1467 << "' which is not a HWGeneratorSchemaOp";
1468
1469 auto referencedKindOp = dyn_cast<HWGeneratorSchemaOp>(referencedKind);
1470 auto paramRef = referencedKindOp.getRequiredAttrs();
1471 auto dict = (*this)->getAttrDictionary();
1472 for (auto str : paramRef) {
1473 auto strAttr = dyn_cast<StringAttr>(str);
1474 if (!strAttr)
1475 return emitError("Unknown attribute type, expected a string");
1476 if (!dict.get(strAttr.getValue()))
1477 return emitError("Missing attribute '") << strAttr.getValue() << "'";
1478 }
1479
1480 return success();
1481}
1482
1483LogicalResult HWModuleGeneratedOp::verify() {
1484 return verifyModuleCommon(*this);
1485}
1486
1487void HWModuleGeneratedOp::getAsmBlockArgumentNames(
1488 mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
1489 getAsmBlockArgumentNamesImpl(region, setNameFn);
1490}
1491
1492LogicalResult HWModuleOp::verifyBody() { return success(); }
1493
1494template <typename ModuleTy>
1495static SmallVector<PortInfo> getPortList(ModuleTy &mod) {
1496 auto modTy = mod.getHWModuleType();
1497 auto emptyDict = DictionaryAttr::get(mod.getContext());
1498 SmallVector<PortInfo> retval;
1499 auto locs = mod.getAllPortLocs();
1500 for (unsigned i = 0, e = modTy.getNumPorts(); i < e; ++i) {
1501 LocationAttr loc = locs[i];
1502 DictionaryAttr attrs =
1503 dyn_cast_or_null<DictionaryAttr>(mod.getPortAttrs(i));
1504 if (!attrs)
1505 attrs = emptyDict;
1506 retval.push_back({modTy.getPorts()[i],
1507 modTy.isOutput(i) ? modTy.getOutputIdForPortId(i)
1508 : modTy.getInputIdForPortId(i),
1509 attrs, loc});
1510 }
1511 return retval;
1512}
1513
1514template <typename ModuleTy>
1515static PortInfo getPort(ModuleTy &mod, size_t idx) {
1516 auto modTy = mod.getHWModuleType();
1517 auto emptyDict = DictionaryAttr::get(mod.getContext());
1518 LocationAttr loc = mod.getPortLoc(idx);
1519 DictionaryAttr attrs =
1520 dyn_cast_or_null<DictionaryAttr>(mod.getPortAttrs(idx));
1521 if (!attrs)
1522 attrs = emptyDict;
1523 return {modTy.getPorts()[idx],
1524 modTy.isOutput(idx) ? modTy.getOutputIdForPortId(idx)
1525 : modTy.getInputIdForPortId(idx),
1526 attrs, loc};
1527}
1528
1529//===----------------------------------------------------------------------===//
1530// InstanceOp
1531//===----------------------------------------------------------------------===//
1532
1533/// Create a instance that refers to a known module.
1534void InstanceOp::build(OpBuilder &builder, OperationState &result,
1535 Operation *module, StringAttr name,
1536 ArrayRef<Value> inputs, ArrayAttr parameters,
1537 InnerSymAttr innerSym) {
1538 if (!parameters)
1539 parameters = builder.getArrayAttr({});
1540
1541 auto mod = cast<hw::HWModuleLike>(module);
1542 auto argNames = builder.getArrayAttr(mod.getInputNames());
1543 auto resultNames = builder.getArrayAttr(mod.getOutputNames());
1544
1545 // Try to resolve the parameterized module type. If failed, use the module's
1546 // parmeterized type. If the client doesn't fix this error, the verifier will
1547 // fail.
1548 ModuleType modType = mod.getHWModuleType();
1549 FailureOr<ModuleType> resolvedModType = modType.resolveParametricTypes(
1550 parameters, result.location, /*emitErrors=*/false);
1551 if (succeeded(resolvedModType))
1552 modType = *resolvedModType;
1553 FunctionType funcType = resolvedModType->getFuncType();
1554 build(builder, result, funcType.getResults(), name,
1555 FlatSymbolRefAttr::get(SymbolTable::getSymbolName(module)), inputs,
1556 argNames, resultNames, parameters, innerSym, /*doNotPrint=*/{});
1557}
1558
1559std::optional<size_t> InstanceOp::getTargetResultIndex() {
1560 // Inner symbols on instance operations target the op not any result.
1561 return std::nullopt;
1562}
1563
1564LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1566 *this, getModuleNameAttr(), getInputs(), getResultTypes(), getArgNames(),
1567 getResultNames(), getParameters(), symbolTable);
1568}
1569
1570LogicalResult InstanceOp::verify() {
1571 auto module = (*this)->getParentOfType<HWModuleOp>();
1572 if (!module)
1573 return success();
1574
1575 auto moduleParameters = module->getAttrOfType<ArrayAttr>("parameters");
1577 [&](const std::function<bool(InFlightDiagnostic &)> &fn) {
1578 auto diag = emitOpError();
1579 if (fn(diag))
1580 diag.attachNote(module->getLoc()) << "module declared here";
1581 };
1583 getParameters(), moduleParameters, emitError);
1584}
1585
1586ParseResult InstanceOp::parse(OpAsmParser &parser, OperationState &result) {
1587 StringAttr instanceNameAttr;
1588 InnerSymAttr innerSym;
1589 FlatSymbolRefAttr moduleNameAttr;
1590 SmallVector<OpAsmParser::UnresolvedOperand, 4> inputsOperands;
1591 SmallVector<Type, 1> inputsTypes, allResultTypes;
1592 ArrayAttr argNames, resultNames, parameters;
1593 auto noneType = parser.getBuilder().getType<NoneType>();
1594
1595 if (parser.parseAttribute(instanceNameAttr, noneType, "instanceName",
1596 result.attributes))
1597 return failure();
1598
1599 if (succeeded(parser.parseOptionalKeyword("sym"))) {
1600 // Parsing an optional symbol name doesn't fail, so no need to check the
1601 // result.
1602 if (parser.parseCustomAttributeWithFallback(innerSym))
1603 return failure();
1604 result.addAttribute(InnerSymbolTable::getInnerSymbolAttrName(), innerSym);
1605 }
1606
1607 llvm::SMLoc parametersLoc, inputsOperandsLoc;
1608 if (parser.parseAttribute(moduleNameAttr, noneType, "moduleName",
1609 result.attributes) ||
1610 parser.getCurrentLocation(&parametersLoc) ||
1611 parseOptionalParameterList(parser, parameters) ||
1612 parseInputPortList(parser, inputsOperands, inputsTypes, argNames) ||
1613 parser.resolveOperands(inputsOperands, inputsTypes, inputsOperandsLoc,
1614 result.operands) ||
1615 parser.parseArrow() ||
1616 parseOutputPortList(parser, allResultTypes, resultNames) ||
1617 parser.parseOptionalAttrDict(result.attributes)) {
1618 return failure();
1619 }
1620
1621 result.addAttribute("argNames", argNames);
1622 result.addAttribute("resultNames", resultNames);
1623 result.addAttribute("parameters", parameters);
1624 result.addTypes(allResultTypes);
1625 return success();
1626}
1627
1628void InstanceOp::print(OpAsmPrinter &p) {
1629 p << ' ';
1630 p.printAttributeWithoutType(getInstanceNameAttr());
1631 if (auto attr = getInnerSymAttr()) {
1632 p << " sym ";
1633 attr.print(p);
1634 }
1635 p << ' ';
1636 p.printAttributeWithoutType(getModuleNameAttr());
1637 printOptionalParameterList(p, *this, getParameters());
1638 printInputPortList(p, *this, getInputs(), getInputs().getTypes(),
1639 getArgNames());
1640 p << " -> ";
1641 printOutputPortList(p, *this, getResultTypes(), getResultNames());
1642
1643 p.printOptionalAttrDict(
1644 (*this)->getAttrs(),
1645 /*elidedAttrs=*/{"instanceName",
1646 InnerSymbolTable::getInnerSymbolAttrName(), "moduleName",
1647 "argNames", "resultNames", "parameters"});
1648}
1649
1650//===----------------------------------------------------------------------===//
1651// HWOutputOp
1652//===----------------------------------------------------------------------===//
1653
1654/// Verify that the num of operands and types fit the declared results.
1655LogicalResult OutputOp::verify() {
1656 // Check that the we (hw.output) have the same number of operands as our
1657 // region has results.
1658 ModuleType modType;
1659 if (auto mod = dyn_cast<HWModuleOp>((*this)->getParentOp()))
1660 modType = mod.getHWModuleType();
1661 else {
1662 emitOpError("must have a module parent");
1663 return failure();
1664 }
1665 auto modResults = modType.getOutputTypes();
1666 OperandRange outputValues = getOperands();
1667 if (modResults.size() != outputValues.size()) {
1668 emitOpError("must have same number of operands as region results.");
1669 return failure();
1670 }
1671
1672 // Check that the types of our operands and the region's results match.
1673 for (size_t i = 0, e = modResults.size(); i < e; ++i) {
1674 if (modResults[i] != outputValues[i].getType()) {
1675 emitOpError("output types must match module. In "
1676 "operand ")
1677 << i << ", expected " << modResults[i] << ", but got "
1678 << outputValues[i].getType() << ".";
1679 return failure();
1680 }
1681 }
1682
1683 return success();
1684}
1685
1686//===----------------------------------------------------------------------===//
1687// Other Operations
1688//===----------------------------------------------------------------------===//
1689
1690static ParseResult parseSliceTypes(OpAsmParser &p, Type &srcType,
1691 Type &idxType) {
1692 Type type;
1693 if (p.parseType(type))
1694 return p.emitError(p.getCurrentLocation(), "Expected type");
1695 auto arrType = type_dyn_cast<ArrayType>(type);
1696 if (!arrType)
1697 return p.emitError(p.getCurrentLocation(), "Expected !hw.array type");
1698 srcType = type;
1699 unsigned idxWidth = llvm::Log2_64_Ceil(arrType.getNumElements());
1700 idxType = IntegerType::get(p.getBuilder().getContext(), idxWidth);
1701 return success();
1702}
1703
1704static void printSliceTypes(OpAsmPrinter &p, Operation *, Type srcType,
1705 Type idxType) {
1706 p.printType(srcType);
1707}
1708
1709ParseResult ArrayCreateOp::parse(OpAsmParser &parser, OperationState &result) {
1710 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
1711 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> operands;
1712 Type elemType;
1713
1714 if (parser.parseOperandList(operands) ||
1715 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
1716 parser.parseType(elemType))
1717 return failure();
1718
1719 if (operands.size() == 0)
1720 return parser.emitError(inputOperandsLoc,
1721 "Cannot construct an array of length 0");
1722
1723 // Check for optional result type: " -> <type>"
1724 Type resultType;
1725 if (parser.parseOptionalArrow().succeeded()) {
1726 if (parser.parseType(resultType))
1727 return failure();
1728 result.addTypes(resultType);
1729 } else {
1730 result.addTypes({ArrayType::get(elemType, operands.size())});
1731 }
1732
1733 for (auto operand : operands)
1734 if (parser.resolveOperand(operand, elemType, result.operands))
1735 return failure();
1736 return success();
1737}
1738
1739void ArrayCreateOp::print(OpAsmPrinter &p) {
1740 p << " ";
1741 p.printOperands(getInputs());
1742 p.printOptionalAttrDict((*this)->getAttrs());
1743 p << " : " << getInputs()[0].getType();
1744
1745 // Print optional result type if it's not the default constructed type
1746 Type expectedType =
1747 ArrayType::get(getInputs()[0].getType(), getInputs().size());
1748 if (getType() != expectedType)
1749 p << " -> " << getType();
1750}
1751
1752void ArrayCreateOp::build(OpBuilder &b, OperationState &state,
1753 ValueRange values) {
1754 assert(values.size() > 0 && "Cannot build array of zero elements");
1755 Type elemType = values[0].getType();
1756 assert(llvm::all_of(
1757 values,
1758 [elemType](Value v) -> bool { return v.getType() == elemType; }) &&
1759 "All values must have same type.");
1760 build(b, state, ArrayType::get(elemType, values.size()), values);
1761}
1762
1763LogicalResult ArrayCreateOp::verify() {
1764 unsigned returnSize = hw::type_cast<ArrayType>(getType()).getNumElements();
1765 if (getInputs().size() != returnSize)
1766 return failure();
1767 return success();
1768}
1769
1770OpFoldResult ArrayCreateOp::fold(FoldAdaptor adaptor) {
1771 if (llvm::any_of(adaptor.getInputs(), [](Attribute attr) {
1772 return !isa_and_nonnull<IntegerAttr>(attr);
1773 }))
1774 return {};
1775 return ArrayAttr::get(getContext(), adaptor.getInputs());
1776}
1777
1778// Check whether an integer value is an offset from a base.
1779bool hw::isOffset(Value base, Value index, uint64_t offset) {
1780 if (auto constBase = base.getDefiningOp<hw::ConstantOp>()) {
1781 if (auto constIndex = index.getDefiningOp<hw::ConstantOp>()) {
1782 // If both values are a constant, check if index == base + offset.
1783 // To account for overflow, the addition is performed with an extra bit
1784 // and the offset is asserted to fit in the bit width of the base.
1785 auto baseValue = constBase.getValue();
1786 auto indexValue = constIndex.getValue();
1787
1788 unsigned bits = baseValue.getBitWidth();
1789 assert(bits == indexValue.getBitWidth() && "mismatched widths");
1790
1791 if (bits < 64 && offset >= (1ull << bits))
1792 return false;
1793
1794 APInt baseExt = baseValue.zextOrTrunc(bits + 1);
1795 APInt indexExt = indexValue.zextOrTrunc(bits + 1);
1796 return baseExt + offset == indexExt;
1797 }
1798 }
1799 return false;
1800}
1801
1802// Canonicalize a create of consecutive elements to a slice.
1803static LogicalResult foldCreateToSlice(ArrayCreateOp op,
1804 PatternRewriter &rewriter) {
1805 // Do not canonicalize create of get into a slice.
1806 auto arrayTy = hw::type_cast<ArrayType>(op.getType());
1807 if (arrayTy.getNumElements() <= 1)
1808 return failure();
1809 auto elemTy = arrayTy.getElementType();
1810
1811 // Check if create arguments are consecutive elements of the same array.
1812 // Attempt to break a create of gets into a sequence of consecutive intervals.
1813 struct Chunk {
1814 Value input;
1815 Value index;
1816 size_t size;
1817 };
1818 SmallVector<Chunk> chunks;
1819 for (Value value : llvm::reverse(op.getInputs())) {
1820 auto get = value.getDefiningOp<ArrayGetOp>();
1821 if (!get)
1822 return failure();
1823
1824 Value input = get.getInput();
1825 Value index = get.getIndex();
1826 if (!chunks.empty()) {
1827 auto &c = *chunks.rbegin();
1828 if (c.input == get.getInput() && isOffset(c.index, index, c.size)) {
1829 c.size++;
1830 continue;
1831 }
1832 }
1833
1834 chunks.push_back(Chunk{input, index, 1});
1835 }
1836
1837 // If there is a single slice, eliminate the create.
1838 if (chunks.size() == 1) {
1839 auto &chunk = chunks[0];
1840 rewriter.replaceOp(op, rewriter.createOrFold<ArraySliceOp>(
1841 op.getLoc(), arrayTy, chunk.input, chunk.index));
1842 return success();
1843 }
1844
1845 // If the number of chunks is significantly less than the number of
1846 // elements, replace the create with a concat of the identified slices.
1847 if (chunks.size() * 2 < arrayTy.getNumElements()) {
1848 SmallVector<Value> slices;
1849 for (auto &chunk : llvm::reverse(chunks)) {
1850 auto sliceTy = ArrayType::get(elemTy, chunk.size);
1851 slices.push_back(rewriter.createOrFold<ArraySliceOp>(
1852 op.getLoc(), sliceTy, chunk.input, chunk.index));
1853 }
1854 rewriter.replaceOpWithNewOp<ArrayConcatOp>(op, arrayTy, slices);
1855 return success();
1856 }
1857
1858 return failure();
1859}
1860
1861LogicalResult ArrayCreateOp::canonicalize(ArrayCreateOp op,
1862 PatternRewriter &rewriter) {
1863 if (succeeded(foldCreateToSlice(op, rewriter)))
1864 return success();
1865 return failure();
1866}
1867
1868Value ArrayCreateOp::getUniformElement() {
1869 if (!getInputs().empty() && llvm::all_equal(getInputs()))
1870 return getInputs()[0];
1871 return {};
1872}
1873
1874static std::optional<uint64_t> getUIntFromValue(Value value) {
1875 auto idxOp = dyn_cast_or_null<ConstantOp>(value.getDefiningOp());
1876 if (!idxOp)
1877 return std::nullopt;
1878 APInt idxAttr = idxOp.getValue();
1879 if (idxAttr.getBitWidth() > 64)
1880 return std::nullopt;
1881 return idxAttr.getLimitedValue();
1882}
1883
1884LogicalResult ArraySliceOp::verify() {
1885 unsigned inputSize =
1886 type_cast<ArrayType>(getInput().getType()).getNumElements();
1887 if (llvm::Log2_64_Ceil(inputSize) !=
1888 getLowIndex().getType().getIntOrFloatBitWidth())
1889 return emitOpError(
1890 "ArraySlice: index width must match clog2 of array size");
1891 return success();
1892}
1893
1894OpFoldResult ArraySliceOp::fold(FoldAdaptor adaptor) {
1895 // If we are slicing the entire input, then return it.
1896 if (getType() == getInput().getType())
1897 return getInput();
1898 return {};
1899}
1900
1901LogicalResult ArraySliceOp::canonicalize(ArraySliceOp op,
1902 PatternRewriter &rewriter) {
1903 auto sliceTy = hw::type_cast<ArrayType>(op.getType());
1904 auto elemTy = sliceTy.getElementType();
1905 uint64_t sliceSize = sliceTy.getNumElements();
1906 if (sliceSize == 0)
1907 return failure();
1908
1909 if (sliceSize == 1) {
1910 // slice(a, n) -> create(a[n])
1911 auto get = ArrayGetOp::create(rewriter, op.getLoc(), op.getInput(),
1912 op.getLowIndex());
1913 rewriter.replaceOpWithNewOp<ArrayCreateOp>(op, op.getType(),
1914 get.getResult());
1915 return success();
1916 }
1917
1918 auto offsetOpt = getUIntFromValue(op.getLowIndex());
1919 if (!offsetOpt)
1920 return failure();
1921
1922 auto *inputOp = op.getInput().getDefiningOp();
1923 if (auto inputSlice = dyn_cast_or_null<ArraySliceOp>(inputOp)) {
1924 // slice(slice(a, n), m) -> slice(a, n + m)
1925 if (inputSlice == op)
1926 return failure();
1927
1928 auto inputIndex = inputSlice.getLowIndex();
1929 auto inputOffsetOpt = getUIntFromValue(inputIndex);
1930 if (!inputOffsetOpt)
1931 return failure();
1932
1933 uint64_t offset = *offsetOpt + *inputOffsetOpt;
1934 auto lowIndex =
1935 ConstantOp::create(rewriter, op.getLoc(), inputIndex.getType(), offset);
1936 rewriter.replaceOpWithNewOp<ArraySliceOp>(op, op.getType(),
1937 inputSlice.getInput(), lowIndex);
1938 return success();
1939 }
1940
1941 if (auto inputCreate = dyn_cast_or_null<ArrayCreateOp>(inputOp)) {
1942 // slice(create(a0, a1, ..., an), m) -> create(am, ...)
1943 auto inputs = inputCreate.getInputs();
1944
1945 uint64_t begin = inputs.size() - *offsetOpt - sliceSize;
1946 rewriter.replaceOpWithNewOp<ArrayCreateOp>(op, op.getType(),
1947 inputs.slice(begin, sliceSize));
1948 return success();
1949 }
1950
1951 if (auto inputConcat = dyn_cast_or_null<ArrayConcatOp>(inputOp)) {
1952 // slice(concat(a1, a2, ...)) -> concat(a2, slice(a3, ..), ...)
1953 SmallVector<Value> chunks;
1954 uint64_t sliceStart = *offsetOpt;
1955 for (auto input : llvm::reverse(inputConcat.getInputs())) {
1956 // Check whether the input intersects with the slice.
1957 uint64_t inputSize =
1958 hw::type_cast<ArrayType>(input.getType()).getNumElements();
1959 if (inputSize == 0 || inputSize <= sliceStart) {
1960 sliceStart -= inputSize;
1961 continue;
1962 }
1963
1964 // Find the indices to slice from this input by intersection.
1965 uint64_t cutEnd = std::min(inputSize, sliceStart + sliceSize);
1966 uint64_t cutSize = cutEnd - sliceStart;
1967 assert(cutSize != 0 && "slice cannot be empty");
1968
1969 if (cutSize == inputSize) {
1970 // The whole input fits in the slice, add it.
1971 assert(sliceStart == 0 && "invalid cut size");
1972 chunks.push_back(input);
1973 } else {
1974 // Slice the required bits from the input.
1975 unsigned width = inputSize == 1 ? 1 : llvm::Log2_64_Ceil(inputSize);
1976 auto lowIndex = ConstantOp::create(
1977 rewriter, op.getLoc(), rewriter.getIntegerType(width), sliceStart);
1978 chunks.push_back(ArraySliceOp::create(
1979 rewriter, op.getLoc(), hw::ArrayType::get(elemTy, cutSize), input,
1980 lowIndex));
1981 }
1982
1983 sliceStart = 0;
1984 sliceSize -= cutSize;
1985 if (sliceSize == 0)
1986 break;
1987 }
1988
1989 assert(chunks.size() > 0 && "missing sliced items");
1990 if (chunks.size() == 1)
1991 rewriter.replaceOp(op, chunks[0]);
1992 else
1993 rewriter.replaceOpWithNewOp<ArrayConcatOp>(
1994 op, llvm::to_vector(llvm::reverse(chunks)));
1995 return success();
1996 }
1997 return failure();
1998}
1999
2000//===----------------------------------------------------------------------===//
2001// ArrayConcatOp
2002//===----------------------------------------------------------------------===//
2003
2004static ParseResult parseArrayConcatTypes(OpAsmParser &p,
2005 SmallVectorImpl<Type> &inputTypes,
2006 Type &resultType) {
2007 Type elemType;
2008 uint64_t resultSize = 0;
2009
2010 auto parseElement = [&]() -> ParseResult {
2011 Type ty;
2012 if (p.parseType(ty))
2013 return failure();
2014 auto arrTy = type_dyn_cast<ArrayType>(ty);
2015 if (!arrTy)
2016 return p.emitError(p.getCurrentLocation(), "Expected !hw.array type");
2017 if (elemType && elemType != arrTy.getElementType())
2018 return p.emitError(p.getCurrentLocation(), "Expected array element type ")
2019 << elemType;
2020
2021 elemType = arrTy.getElementType();
2022 inputTypes.push_back(ty);
2023 resultSize += arrTy.getNumElements();
2024 return success();
2025 };
2026
2027 if (p.parseCommaSeparatedList(parseElement))
2028 return failure();
2029
2030 resultType = ArrayType::get(elemType, resultSize);
2031 return success();
2032}
2033
2034static void printArrayConcatTypes(OpAsmPrinter &p, Operation *,
2035 TypeRange inputTypes, Type resultType) {
2036 llvm::interleaveComma(inputTypes, p, [&p](Type t) { p << t; });
2037}
2038
2039void ArrayConcatOp::build(OpBuilder &b, OperationState &state,
2040 ValueRange values) {
2041 assert(!values.empty() && "Cannot build array of zero elements");
2042 ArrayType arrayTy = cast<ArrayType>(values[0].getType());
2043 Type elemTy = arrayTy.getElementType();
2044 assert(llvm::all_of(values,
2045 [elemTy](Value v) -> bool {
2046 return isa<ArrayType>(v.getType()) &&
2047 cast<ArrayType>(v.getType()).getElementType() ==
2048 elemTy;
2049 }) &&
2050 "All values must be of ArrayType with the same element type.");
2051
2052 uint64_t resultSize = 0;
2053 for (Value val : values)
2054 resultSize += cast<ArrayType>(val.getType()).getNumElements();
2055 build(b, state, ArrayType::get(elemTy, resultSize), values);
2056}
2057
2058OpFoldResult ArrayConcatOp::fold(FoldAdaptor adaptor) {
2059 if (getInputs().size() == 1)
2060 return getInputs()[0];
2061
2062 auto inputs = adaptor.getInputs();
2063 SmallVector<Attribute> array;
2064 for (size_t i = 0, e = getNumOperands(); i < e; ++i) {
2065 if (!inputs[i])
2066 return {};
2067 llvm::copy(cast<ArrayAttr>(inputs[i]), std::back_inserter(array));
2068 }
2069 return ArrayAttr::get(getContext(), array);
2070}
2071
2072// Flatten a concatenation of array creates into a single create.
2073static bool flattenConcatOp(ArrayConcatOp op, PatternRewriter &rewriter) {
2074 for (auto input : op.getInputs())
2075 if (!input.getDefiningOp<ArrayCreateOp>())
2076 return false;
2077
2078 SmallVector<Value> items;
2079 for (auto input : op.getInputs()) {
2080 auto create = cast<ArrayCreateOp>(input.getDefiningOp());
2081 for (auto item : create.getInputs())
2082 items.push_back(item);
2083 }
2084
2085 rewriter.replaceOpWithNewOp<ArrayCreateOp>(op, items);
2086 return true;
2087}
2088
2089// Merge consecutive slice expressions in a concatenation.
2090static bool mergeConcatSlices(ArrayConcatOp op, PatternRewriter &rewriter) {
2091 struct Slice {
2092 Value input;
2093 Value index;
2094 size_t size;
2095 Value op;
2096 SmallVector<Location> locs;
2097 };
2098
2099 SmallVector<Value> items;
2100 std::optional<Slice> last;
2101 bool changed = false;
2102
2103 auto concatenate = [&] {
2104 // If there is only one op in the slice, place it to the items list.
2105 if (!last)
2106 return;
2107 if (last->op) {
2108 items.push_back(last->op);
2109 last.reset();
2110 return;
2111 }
2112
2113 // Otherwise, create a new slice of with the given size and place it.
2114 // In this case, the concat op is replaced, using the new argument.
2115 changed = true;
2116 auto loc = FusedLoc::get(op.getContext(), last->locs);
2117 auto origTy = hw::type_cast<ArrayType>(last->input.getType());
2118 auto arrayTy = ArrayType::get(origTy.getElementType(), last->size);
2119 items.push_back(rewriter.createOrFold<ArraySliceOp>(
2120 loc, arrayTy, last->input, last->index));
2121
2122 last.reset();
2123 };
2124
2125 auto append = [&](Value op, Value input, Value index, size_t size) {
2126 // If this slice is an extension of the previous one, extend the size
2127 // saved. In this case, a new slice of is created and the concatenation
2128 // operator is rewritten. Otherwise, flush the last slice.
2129 if (last) {
2130 if (last->input == input && isOffset(last->index, index, last->size)) {
2131 last->size += size;
2132 last->op = {};
2133 last->locs.push_back(op.getLoc());
2134 return;
2135 }
2136 concatenate();
2137 }
2138 last.emplace(Slice{input, index, size, op, {op.getLoc()}});
2139 };
2140
2141 for (auto item : llvm::reverse(op.getInputs())) {
2142 if (auto slice = item.getDefiningOp<ArraySliceOp>()) {
2143 auto size = hw::type_cast<ArrayType>(slice.getType()).getNumElements();
2144 append(item, slice.getInput(), slice.getLowIndex(), size);
2145 continue;
2146 }
2147
2148 if (auto create = item.getDefiningOp<ArrayCreateOp>()) {
2149 if (create.getInputs().size() == 1) {
2150 if (auto get = create.getInputs()[0].getDefiningOp<ArrayGetOp>()) {
2151 append(item, get.getInput(), get.getIndex(), 1);
2152 continue;
2153 }
2154 }
2155 }
2156
2157 concatenate();
2158 items.push_back(item);
2159 }
2160 concatenate();
2161
2162 if (!changed)
2163 return false;
2164
2165 if (items.size() == 1) {
2166 rewriter.replaceOp(op, items[0]);
2167 } else {
2168 std::reverse(items.begin(), items.end());
2169 rewriter.replaceOpWithNewOp<ArrayConcatOp>(op, items);
2170 }
2171 return true;
2172}
2173
2174LogicalResult ArrayConcatOp::canonicalize(ArrayConcatOp op,
2175 PatternRewriter &rewriter) {
2176 // concat(create(a1, ...), create(a3, ...), ...) -> create(a1, ..., a3, ...)
2177 if (flattenConcatOp(op, rewriter))
2178 return success();
2179
2180 // concat(slice(a, n, m), slice(a, n + m, p)) -> concat(slice(a, n, m + p))
2181 if (mergeConcatSlices(op, rewriter))
2182 return success();
2183
2184 return failure();
2185}
2186
2187//===----------------------------------------------------------------------===//
2188// EnumConstantOp
2189//===----------------------------------------------------------------------===//
2190
2191ParseResult EnumConstantOp::parse(OpAsmParser &parser, OperationState &result) {
2192 // Parse a Type instead of an EnumType since the type might be a type alias.
2193 // The validity of the canonical type is checked during construction of the
2194 // EnumFieldAttr.
2195 Type type;
2196 StringRef field;
2197
2198 auto loc = parser.getEncodedSourceLoc(parser.getCurrentLocation());
2199 if (parser.parseKeyword(&field) || parser.parseColonType(type))
2200 return failure();
2201
2202 auto fieldAttr = EnumFieldAttr::get(
2203 loc, StringAttr::get(parser.getContext(), field), type);
2204
2205 if (!fieldAttr)
2206 return failure();
2207
2208 result.addAttribute("field", fieldAttr);
2209 result.addTypes(type);
2210
2211 return success();
2212}
2213
2214void EnumConstantOp::print(OpAsmPrinter &p) {
2215 p << " " << getField().getField().getValue() << " : "
2216 << getField().getType().getValue();
2217}
2218
2219void EnumConstantOp::getAsmResultNames(
2220 function_ref<void(Value, StringRef)> setNameFn) {
2221 setNameFn(getResult(), getField().getField().str());
2222}
2223
2224void EnumConstantOp::build(OpBuilder &builder, OperationState &odsState,
2225 EnumFieldAttr field) {
2226 return build(builder, odsState, field.getType().getValue(), field);
2227}
2228
2229OpFoldResult EnumConstantOp::fold(FoldAdaptor adaptor) {
2230 assert(adaptor.getOperands().empty() && "constant has no operands");
2231 return getFieldAttr();
2232}
2233
2234LogicalResult EnumConstantOp::verify() {
2235 auto fieldAttr = getFieldAttr();
2236 auto fieldType = fieldAttr.getType().getValue();
2237 // This check ensures that we are using the exact same type, without looking
2238 // through type aliases.
2239 if (fieldType != getType())
2240 emitOpError("return type ")
2241 << getType() << " does not match attribute type " << fieldAttr;
2242 return success();
2243}
2244
2245//===----------------------------------------------------------------------===//
2246// EnumCmpOp
2247//===----------------------------------------------------------------------===//
2248
2249LogicalResult EnumCmpOp::verify() {
2250 // Compare the canonical types.
2251 auto lhsType = type_cast<EnumType>(getLhs().getType());
2252 auto rhsType = type_cast<EnumType>(getRhs().getType());
2253 if (rhsType != lhsType)
2254 emitOpError("types do not match");
2255 return success();
2256}
2257
2258//===----------------------------------------------------------------------===//
2259// StructCreateOp
2260//===----------------------------------------------------------------------===//
2261
2262ParseResult StructCreateOp::parse(OpAsmParser &parser, OperationState &result) {
2263 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
2264 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
2265 Type declOrAliasType;
2266
2267 if (parser.parseLParen() || parser.parseOperandList(operands) ||
2268 parser.parseRParen() || parser.parseOptionalAttrDict(result.attributes) ||
2269 parser.parseColonType(declOrAliasType))
2270 return failure();
2271
2272 auto declType = type_dyn_cast<StructType>(declOrAliasType);
2273 if (!declType)
2274 return parser.emitError(parser.getNameLoc(),
2275 "expected !hw.struct type or alias");
2276
2277 llvm::SmallVector<Type, 4> structInnerTypes;
2278 declType.getInnerTypes(structInnerTypes);
2279 result.addTypes(declOrAliasType);
2280
2281 if (parser.resolveOperands(operands, structInnerTypes, inputOperandsLoc,
2282 result.operands))
2283 return failure();
2284 return success();
2285}
2286
2287void StructCreateOp::print(OpAsmPrinter &printer) {
2288 printer << " (";
2289 printer.printOperands(getInput());
2290 printer << ")";
2291 printer.printOptionalAttrDict((*this)->getAttrs());
2292 printer << " : " << getType();
2293}
2294
2295LogicalResult StructCreateOp::verify() {
2296 auto elements = hw::type_cast<StructType>(getType()).getElements();
2297
2298 if (elements.size() != getInput().size())
2299 return emitOpError("structure field count mismatch");
2300
2301 for (const auto &[field, value] : llvm::zip(elements, getInput()))
2302 if (field.type != value.getType())
2303 return emitOpError("structure field `")
2304 << field.name << "` type does not match";
2305
2306 return success();
2307}
2308
2309OpFoldResult StructCreateOp::fold(FoldAdaptor adaptor) {
2310 // struct_create(struct_explode(x)) => x
2311 if (!getInput().empty())
2312 if (auto explodeOp = getInput()[0].getDefiningOp<StructExplodeOp>();
2313 explodeOp && getInput() == explodeOp.getResults() &&
2314 getResult().getType() == explodeOp.getInput().getType())
2315 return explodeOp.getInput();
2316
2317 auto inputs = adaptor.getInput();
2318 if (llvm::any_of(inputs, [](Attribute attr) {
2319 return !isa_and_nonnull<IntegerAttr>(attr);
2320 }))
2321 return {};
2322 return ArrayAttr::get(getContext(), inputs);
2323}
2324
2325LogicalResult StructCreateOp::canonicalize(StructCreateOp op,
2326 PatternRewriter &rewriter) {
2327 // Fold away a struct_create whose inputs are struct_extract ops that
2328 // reconstruct the same struct in field order from a single source value.
2329 Value foldVal;
2330 for (auto [i, operand] : llvm::enumerate(op.getInput())) {
2331 auto extractOp = operand.getDefiningOp<StructExtractOp>();
2332 if (!extractOp || extractOp.getFieldIndex() != i ||
2333 extractOp.getInput().getType() != op.getType()) {
2334 foldVal = {};
2335 break;
2336 }
2337 if (i == 0) {
2338 foldVal = extractOp.getInput();
2339 } else if (extractOp.getInput() != foldVal) {
2340 foldVal = {};
2341 break;
2342 }
2343 }
2344 if (foldVal && foldVal != op.getResult()) {
2345 rewriter.replaceOp(op, foldVal);
2346 return success();
2347 }
2348 return failure();
2349}
2350
2351//===----------------------------------------------------------------------===//
2352// StructExplodeOp
2353//===----------------------------------------------------------------------===//
2354
2355ParseResult StructExplodeOp::parse(OpAsmParser &parser,
2356 OperationState &result) {
2357 OpAsmParser::UnresolvedOperand operand;
2358 Type declType;
2359
2360 if (parser.parseOperand(operand) ||
2361 parser.parseOptionalAttrDict(result.attributes) ||
2362 parser.parseColonType(declType))
2363 return failure();
2364 auto structType = type_dyn_cast<StructType>(declType);
2365 if (!structType)
2366 return parser.emitError(parser.getNameLoc(),
2367 "invalid kind of type specified");
2368
2369 llvm::SmallVector<Type, 4> structInnerTypes;
2370 structType.getInnerTypes(structInnerTypes);
2371 result.addTypes(structInnerTypes);
2372
2373 if (parser.resolveOperand(operand, declType, result.operands))
2374 return failure();
2375 return success();
2376}
2377
2378void StructExplodeOp::print(OpAsmPrinter &printer) {
2379 printer << " ";
2380 printer.printOperand(getInput());
2381 printer.printOptionalAttrDict((*this)->getAttrs());
2382 printer << " : " << getInput().getType();
2383}
2384
2385LogicalResult StructExplodeOp::fold(FoldAdaptor adaptor,
2386 SmallVectorImpl<OpFoldResult> &results) {
2387 auto input = adaptor.getInput();
2388 if (!input)
2389 return failure();
2390 llvm::copy(cast<ArrayAttr>(input), std::back_inserter(results));
2391 return success();
2392}
2393
2394LogicalResult StructExplodeOp::canonicalize(StructExplodeOp op,
2395 PatternRewriter &rewriter) {
2396 auto *inputOp = op.getInput().getDefiningOp();
2397 auto elements = type_cast<StructType>(op.getInput().getType()).getElements();
2398 auto result = failure();
2399 auto opResults = op.getResults();
2400 for (uint32_t index = 0; index < elements.size(); index++) {
2401 if (auto foldResult = foldStructExtract(inputOp, index)) {
2402 rewriter.replaceAllUsesWith(opResults[index], foldResult);
2403 result = success();
2404 }
2405 }
2406 return result;
2407}
2408
2409void StructExplodeOp::getAsmResultNames(
2410 function_ref<void(Value, StringRef)> setNameFn) {
2411 auto structType = type_cast<StructType>(getInput().getType());
2412 for (auto [res, field] : llvm::zip(getResults(), structType.getElements()))
2413 setNameFn(res, field.name.str());
2414}
2415
2416void StructExplodeOp::build(OpBuilder &odsBuilder, OperationState &odsState,
2417 Value input) {
2418 StructType inputType = dyn_cast<StructType>(input.getType());
2419 assert(inputType);
2420 SmallVector<Type, 16> fieldTypes;
2421 for (auto field : inputType.getElements())
2422 fieldTypes.push_back(field.type);
2423 build(odsBuilder, odsState, fieldTypes, input);
2424}
2425
2426//===----------------------------------------------------------------------===//
2427// StructExtractOp
2428//===----------------------------------------------------------------------===//
2429
2430/// Ensure an aggregate op's field index is within the bounds of
2431/// the aggregate type and the accessed field is of 'elementType'.
2432template <typename AggregateOp, typename AggregateType>
2433static LogicalResult verifyAggregateFieldIndexAndType(AggregateOp &op,
2434 AggregateType aggType,
2435 Type elementType) {
2436 auto index = op.getFieldIndex();
2437 if (index >= aggType.getElements().size())
2438 return op.emitOpError() << "field index " << index
2439 << " exceeds element count of aggregate type";
2440
2442 getCanonicalType(aggType.getElements()[index].type))
2443 return op.emitOpError()
2444 << "type " << aggType.getElements()[index].type
2445 << " of accessed field in aggregate at index " << index
2446 << " does not match expected type " << elementType;
2447
2448 return success();
2449}
2450
2451LogicalResult StructExtractOp::verify() {
2452 return verifyAggregateFieldIndexAndType<StructExtractOp, StructType>(
2453 *this, getInput().getType(), getType());
2454}
2455
2456/// Use the same parser for both struct_extract and union_extract since the
2457/// syntax is identical.
2458template <typename AggregateType>
2459static ParseResult parseExtractOp(OpAsmParser &parser, OperationState &result) {
2460 OpAsmParser::UnresolvedOperand operand;
2461 StringAttr fieldName;
2462 Type declType;
2463
2464 if (parser.parseOperand(operand) || parser.parseLSquare() ||
2465 parser.parseAttribute(fieldName) || parser.parseRSquare() ||
2466 parser.parseOptionalAttrDict(result.attributes) ||
2467 parser.parseColonType(declType))
2468 return failure();
2469 auto aggType = type_dyn_cast<AggregateType>(declType);
2470 if (!aggType)
2471 return parser.emitError(parser.getNameLoc(),
2472 "invalid kind of type specified");
2473
2474 auto fieldIndex = aggType.getFieldIndex(fieldName);
2475 if (!fieldIndex) {
2476 parser.emitError(parser.getNameLoc(), "field name '" +
2477 fieldName.getValue() +
2478 "' not found in aggregate type");
2479 return failure();
2480 }
2481
2482 auto indexAttr =
2483 IntegerAttr::get(IntegerType::get(parser.getContext(), 32), *fieldIndex);
2484 result.addAttribute("fieldIndex", indexAttr);
2485 Type resultType = aggType.getElements()[*fieldIndex].type;
2486 result.addTypes(resultType);
2487
2488 if (parser.resolveOperand(operand, declType, result.operands))
2489 return failure();
2490 return success();
2491}
2492
2493/// Use the same printer for both struct_extract and union_extract since the
2494/// syntax is identical.
2495template <typename AggType>
2496static void printExtractOp(OpAsmPrinter &printer, AggType op) {
2497 printer << " ";
2498 printer.printOperand(op.getInput());
2499 printer << "[\"" << op.getFieldName() << "\"]";
2500 printer.printOptionalAttrDict(op->getAttrs(), {"fieldIndex"});
2501 printer << " : " << op.getInput().getType();
2502}
2503
2504ParseResult StructExtractOp::parse(OpAsmParser &parser,
2505 OperationState &result) {
2506 return parseExtractOp<StructType>(parser, result);
2507}
2508
2509void StructExtractOp::print(OpAsmPrinter &printer) {
2510 printExtractOp(printer, *this);
2511}
2512
2513void StructExtractOp::build(OpBuilder &builder, OperationState &odsState,
2514 Value input, StructType::FieldInfo field) {
2515 auto fieldIndex =
2516 type_cast<StructType>(input.getType()).getFieldIndex(field.name);
2517 assert(fieldIndex.has_value() && "field name not found in aggregate type");
2518 build(builder, odsState, field.type, input, *fieldIndex);
2519}
2520
2521void StructExtractOp::build(OpBuilder &builder, OperationState &odsState,
2522 Value input, StringAttr fieldName) {
2523 auto structType = type_cast<StructType>(input.getType());
2524 auto fieldIndex = structType.getFieldIndex(fieldName);
2525 assert(fieldIndex.has_value() && "field name not found in aggregate type");
2526 auto resultType = structType.getElements()[*fieldIndex].type;
2527 build(builder, odsState, resultType, input, *fieldIndex);
2528}
2529
2530OpFoldResult StructExtractOp::fold(FoldAdaptor adaptor) {
2531 if (auto constOperand = adaptor.getInput()) {
2532 // Fold extract from aggregate constant
2533 auto operandAttr = llvm::cast<ArrayAttr>(constOperand);
2534 return operandAttr.getValue()[getFieldIndex()];
2535 }
2536
2537 if (auto foldResult =
2538 foldStructExtract(getInput().getDefiningOp(), getFieldIndex()))
2539 return foldResult;
2540 return {};
2541}
2542
2543LogicalResult StructExtractOp::canonicalize(StructExtractOp op,
2544 PatternRewriter &rewriter) {
2545 auto *inputOp = op.getInput().getDefiningOp();
2546
2547 // b = extract(inject(x["a"], v0)["b"]) => extract(x, "b")
2548 if (auto structInject = dyn_cast_or_null<StructInjectOp>(inputOp)) {
2549 if (structInject.getFieldIndex() != op.getFieldIndex()) {
2550 rewriter.replaceOpWithNewOp<StructExtractOp>(
2551 op, op.getType(), structInject.getInput(), op.getFieldIndexAttr());
2552 return success();
2553 }
2554 }
2555
2556 return failure();
2557}
2558
2559void StructExtractOp::getAsmResultNames(
2560 function_ref<void(Value, StringRef)> setNameFn) {
2561 setNameFn(getResult(), getFieldName());
2562}
2563
2564//===----------------------------------------------------------------------===//
2565// StructInjectOp
2566//===----------------------------------------------------------------------===//
2567
2568void StructInjectOp::build(OpBuilder &builder, OperationState &odsState,
2569 Value input, StringAttr fieldName, Value newValue) {
2570 auto structType = type_cast<StructType>(input.getType());
2571 auto fieldIndex = structType.getFieldIndex(fieldName);
2572 assert(fieldIndex.has_value() && "field name not found in aggregate type");
2573 build(builder, odsState, input, *fieldIndex, newValue);
2574}
2575
2576LogicalResult StructInjectOp::verify() {
2577 return verifyAggregateFieldIndexAndType<StructInjectOp, StructType>(
2578 *this, getInput().getType(), getNewValue().getType());
2579}
2580
2581ParseResult StructInjectOp::parse(OpAsmParser &parser, OperationState &result) {
2582 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
2583 OpAsmParser::UnresolvedOperand operand, val;
2584 StringAttr fieldName;
2585 Type declType;
2586
2587 if (parser.parseOperand(operand) || parser.parseLSquare() ||
2588 parser.parseAttribute(fieldName) || parser.parseRSquare() ||
2589 parser.parseComma() || parser.parseOperand(val) ||
2590 parser.parseOptionalAttrDict(result.attributes) ||
2591 parser.parseColonType(declType))
2592 return failure();
2593 auto structType = type_dyn_cast<StructType>(declType);
2594 if (!structType)
2595 return parser.emitError(inputOperandsLoc, "invalid kind of type specified");
2596
2597 auto fieldIndex = structType.getFieldIndex(fieldName);
2598 if (!fieldIndex) {
2599 parser.emitError(parser.getNameLoc(), "field name '" +
2600 fieldName.getValue() +
2601 "' not found in aggregate type");
2602 return failure();
2603 }
2604
2605 auto indexAttr =
2606 IntegerAttr::get(IntegerType::get(parser.getContext(), 32), *fieldIndex);
2607 result.addAttribute("fieldIndex", indexAttr);
2608 result.addTypes(declType);
2609
2610 Type resultType = structType.getElements()[*fieldIndex].type;
2611 if (parser.resolveOperands({operand, val}, {declType, resultType},
2612 inputOperandsLoc, result.operands))
2613 return failure();
2614 return success();
2615}
2616
2617void StructInjectOp::print(OpAsmPrinter &printer) {
2618 printer << " ";
2619 printer.printOperand(getInput());
2620 printer << "[\"" << getFieldName() << "\"], ";
2621 printer.printOperand(getNewValue());
2622 printer.printOptionalAttrDict((*this)->getAttrs(), {"fieldIndex"});
2623 printer << " : " << getInput().getType();
2624}
2625
2626OpFoldResult StructInjectOp::fold(FoldAdaptor adaptor) {
2627 auto input = adaptor.getInput();
2628 auto newValue = adaptor.getNewValue();
2629 if (!input || !newValue)
2630 return {};
2631 SmallVector<Attribute> array;
2632 llvm::copy(cast<ArrayAttr>(input), std::back_inserter(array));
2633 array[getFieldIndex()] = newValue;
2634 return ArrayAttr::get(getContext(), array);
2635}
2636
2637LogicalResult StructInjectOp::canonicalize(StructInjectOp op,
2638 PatternRewriter &rewriter) {
2639 // If this inject is only used as an input to another inject, don't try to
2640 // canonicalize it. It will be included in that other op's canonicalization
2641 // attempt. This avoids doing redundant work.
2642 if (op->hasOneUse()) {
2643 auto &use = *op->use_begin();
2644 if (isa<StructInjectOp>(use.getOwner()) && use.getOperandNumber() == 0)
2645 return failure();
2646 }
2647
2648 // Canonicalize multiple injects into a create op and eliminate overwrites.
2649 SmallPtrSet<Operation *, 4> injects;
2650 DenseMap<StringAttr, Value> fields;
2651
2652 // Chase a chain of injects. Bail out if cycles are present.
2653 StructInjectOp inject = op;
2654 Value input;
2655 do {
2656 if (!injects.insert(inject).second)
2657 break;
2658
2659 fields.try_emplace(inject.getFieldNameAttr(), inject.getNewValue());
2660 input = inject.getInput();
2661 inject = dyn_cast_or_null<StructInjectOp>(input.getDefiningOp());
2662 } while (inject);
2663 assert(input && "missing input to inject chain");
2664
2665 auto ty = hw::type_cast<StructType>(op.getType());
2666 auto elements = ty.getElements();
2667
2668 // If the inject chain sets all fields, canonicalize to create.
2669 if (fields.size() == elements.size()) {
2670 SmallVector<Value> createFields;
2671 for (const auto &field : elements) {
2672 auto it = fields.find(field.name);
2673 assert(it != fields.end() && "missing field");
2674 createFields.push_back(it->second);
2675 }
2676 rewriter.replaceOpWithNewOp<StructCreateOp>(op, ty, createFields);
2677 return success();
2678 }
2679
2680 // Nothing to canonicalize, only the original inject in the chain.
2681 if (injects.size() == fields.size())
2682 return failure();
2683
2684 // Eliminate overwrites. The hash map contains the last write to each field.
2685 for (uint32_t fieldIndex = 0; fieldIndex < elements.size(); fieldIndex++) {
2686 auto it = fields.find(elements[fieldIndex].name);
2687 if (it == fields.end())
2688 continue;
2689 input = StructInjectOp::create(rewriter, op.getLoc(), ty, input, fieldIndex,
2690 it->second);
2691 }
2692
2693 rewriter.replaceOp(op, input);
2694 return success();
2695}
2696
2697//===----------------------------------------------------------------------===//
2698// UnionCreateOp
2699//===----------------------------------------------------------------------===//
2700
2701LogicalResult UnionCreateOp::verify() {
2702 return verifyAggregateFieldIndexAndType<UnionCreateOp, UnionType>(
2703 *this, getType(), getInput().getType());
2704}
2705
2706void UnionCreateOp::build(OpBuilder &builder, OperationState &odsState,
2707 Type unionType, StringAttr fieldName, Value input) {
2708 auto fieldIndex = type_cast<UnionType>(unionType).getFieldIndex(fieldName);
2709 assert(fieldIndex.has_value() && "field name not found in aggregate type");
2710 build(builder, odsState, unionType, *fieldIndex, input);
2711}
2712
2713ParseResult UnionCreateOp::parse(OpAsmParser &parser, OperationState &result) {
2714 Type declOrAliasType;
2715 StringAttr fieldName;
2716 OpAsmParser::UnresolvedOperand input;
2717 llvm::SMLoc fieldLoc = parser.getCurrentLocation();
2718
2719 if (parser.parseAttribute(fieldName) || parser.parseComma() ||
2720 parser.parseOperand(input) ||
2721 parser.parseOptionalAttrDict(result.attributes) ||
2722 parser.parseColonType(declOrAliasType))
2723 return failure();
2724
2725 auto declType = type_dyn_cast<UnionType>(declOrAliasType);
2726 if (!declType)
2727 return parser.emitError(parser.getNameLoc(),
2728 "expected !hw.union type or alias");
2729
2730 auto fieldIndex = declType.getFieldIndex(fieldName);
2731 if (!fieldIndex) {
2732 parser.emitError(fieldLoc, "cannot find union field '")
2733 << fieldName.getValue() << '\'';
2734 return failure();
2735 }
2736
2737 auto indexAttr =
2738 IntegerAttr::get(IntegerType::get(parser.getContext(), 32), *fieldIndex);
2739 result.addAttribute("fieldIndex", indexAttr);
2740 Type inputType = declType.getElements()[*fieldIndex].type;
2741
2742 if (parser.resolveOperand(input, inputType, result.operands))
2743 return failure();
2744 result.addTypes({declOrAliasType});
2745 return success();
2746}
2747
2748void UnionCreateOp::print(OpAsmPrinter &printer) {
2749 printer << " \"" << getFieldName() << "\", ";
2750 printer.printOperand(getInput());
2751 printer.printOptionalAttrDict((*this)->getAttrs(), {"fieldIndex"});
2752 printer << " : " << getType();
2753}
2754
2755//===----------------------------------------------------------------------===//
2756// UnionExtractOp
2757//===----------------------------------------------------------------------===//
2758
2759ParseResult UnionExtractOp::parse(OpAsmParser &parser, OperationState &result) {
2760 return parseExtractOp<UnionType>(parser, result);
2761}
2762
2763void UnionExtractOp::print(OpAsmPrinter &printer) {
2764 printExtractOp(printer, *this);
2765}
2766
2767LogicalResult UnionExtractOp::inferReturnTypes(
2768 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
2769 DictionaryAttr attrs, mlir::PropertyRef properties,
2770 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
2771 Adaptor adaptor(operands, attrs, properties, regions);
2772 auto unionElements =
2773 hw::type_cast<UnionType>((adaptor.getInput().getType())).getElements();
2774 unsigned fieldIndex = adaptor.getFieldIndexAttr().getValue().getZExtValue();
2775 if (fieldIndex >= unionElements.size()) {
2776 if (loc)
2777 mlir::emitError(*loc, "field index " + Twine(fieldIndex) +
2778 " exceeds element count of aggregate type");
2779 return failure();
2780 }
2781 results.push_back(unionElements[fieldIndex].type);
2782 return success();
2783}
2784
2785void UnionExtractOp::build(OpBuilder &odsBuilder, OperationState &odsState,
2786 Value input, StringAttr fieldName) {
2787 auto unionType = type_cast<UnionType>(input.getType());
2788 auto fieldIndex = unionType.getFieldIndex(fieldName);
2789 assert(fieldIndex.has_value() && "field name not found in aggregate type");
2790 auto resultType = unionType.getElements()[*fieldIndex].type;
2791 build(odsBuilder, odsState, resultType, input, *fieldIndex);
2792}
2793
2794LogicalResult UnionExtractOp::canonicalize(UnionExtractOp extractOp,
2795 PatternRewriter &rewriter) {
2796 // hw.union_extract("F1", hw.union_create("F2", a)) -> a, if F1 and F2 map to
2797 // the same bits
2798 if (auto createOp = extractOp.getInput().getDefiningOp<UnionCreateOp>()) {
2799 if (createOp.getInput().getType() == extractOp.getType() &&
2800 createOp.getInput() != extractOp.getResult()) {
2801 auto unionTypeElts =
2802 cast<UnionType>(extractOp.getInput().getType()).getElements();
2803 if (unionTypeElts[createOp.getFieldIndex()].offset ==
2804 unionTypeElts[extractOp.getFieldIndex()].offset) {
2805 rewriter.replaceOp(extractOp, createOp.getInput());
2806 return success();
2807 }
2808 }
2809 }
2810 // Forward bitcasts if the extract covers the entire union
2811 if (auto bitcastOp = extractOp.getInput().getDefiningOp<BitcastOp>()) {
2812 auto inputWidth = getBitWidth(bitcastOp.getInput().getType());
2813 assert(inputWidth >= 0 &&
2814 inputWidth == getBitWidth(extractOp.getInput().getType()) &&
2815 "bitcast does not cover entire union");
2816 if (inputWidth == getBitWidth(extractOp.getType())) {
2817 auto loc = FusedLoc::get(rewriter.getContext(),
2818 {bitcastOp.getLoc(), extractOp.getLoc()});
2819 auto newBitcast = rewriter.createOrFold<BitcastOp>(
2820 loc, extractOp.getType(), bitcastOp.getInput());
2821 rewriter.replaceOp(extractOp, newBitcast);
2822 return success();
2823 }
2824 }
2825
2826 return failure();
2827}
2828
2829//===----------------------------------------------------------------------===//
2830// ArrayGetOp
2831//===----------------------------------------------------------------------===//
2832
2833// An array_get of an array_create with a constant index can just be the
2834// array_create operand at the constant index. If the array_create has a
2835// single uniform value for each element, just return that value regardless of
2836// the index. If the array is constructed from a constant by a bitcast
2837// operation, we can fold into a constant.
2838OpFoldResult ArrayGetOp::fold(FoldAdaptor adaptor) {
2839 auto inputCst = dyn_cast_or_null<ArrayAttr>(adaptor.getInput());
2840 auto indexCst = dyn_cast_or_null<IntegerAttr>(adaptor.getIndex());
2841
2842 if (inputCst) {
2843 // Constant array index.
2844 if (indexCst) {
2845 auto indexVal = indexCst.getValue();
2846 if (indexVal.getBitWidth() < 64) {
2847 auto index = indexVal.getZExtValue();
2848 return inputCst[inputCst.size() - 1 - index];
2849 }
2850 }
2851 // If all elements of the array are the same, we can return any element of
2852 // array.
2853 if (!inputCst.empty() && llvm::all_equal(inputCst))
2854 return inputCst[0];
2855 }
2856
2857 // array_get(bitcast(c), i) -> c[i*w+w-1:i*w]
2858 if (auto bitcast = getInput().getDefiningOp<hw::BitcastOp>()) {
2859 auto intTy = dyn_cast<IntegerType>(getType());
2860 if (!intTy)
2861 return {};
2862 auto bitcastInputOp = bitcast.getInput().getDefiningOp<hw::ConstantOp>();
2863 if (!bitcastInputOp)
2864 return {};
2865 if (!indexCst)
2866 return {};
2867 auto bitcastInputCst = bitcastInputOp.getValue();
2868 // Calculate the index. Make sure to zero-extend the index value before
2869 // multiplying the element width.
2870 auto startIdx = indexCst.getValue().zext(bitcastInputCst.getBitWidth()) *
2871 getType().getIntOrFloatBitWidth();
2872 // Extract [startIdx + width - 1: startIdx].
2873 return IntegerAttr::get(intTy, bitcastInputCst.lshr(startIdx).trunc(
2874 intTy.getIntOrFloatBitWidth()));
2875 }
2876
2877 // array_get(array_inject(_, index, element), index) -> element
2878 if (auto inject = getInput().getDefiningOp<ArrayInjectOp>())
2879 if (getIndex() == inject.getIndex())
2880 return inject.getElement();
2881
2882 auto inputCreate = getInput().getDefiningOp<ArrayCreateOp>();
2883 if (!inputCreate)
2884 return {};
2885
2886 if (auto uniformValue = inputCreate.getUniformElement())
2887 return uniformValue;
2888
2889 if (!indexCst || indexCst.getValue().getBitWidth() > 64)
2890 return {};
2891
2892 uint64_t index = indexCst.getValue().getLimitedValue();
2893 auto createInputs = inputCreate.getInputs();
2894 if (index >= createInputs.size())
2895 return {};
2896 return createInputs[createInputs.size() - index - 1];
2897}
2898
2899LogicalResult ArrayGetOp::canonicalize(ArrayGetOp op,
2900 PatternRewriter &rewriter) {
2901 auto idxOpt = getUIntFromValue(op.getIndex());
2902 if (!idxOpt)
2903 return failure();
2904
2905 auto *inputOp = op.getInput().getDefiningOp();
2906 if (auto inputSlice = dyn_cast_or_null<ArraySliceOp>(inputOp)) {
2907 // get(slice(a, n), m) -> get(a, n + m)
2908 auto offsetOp = inputSlice.getLowIndex();
2909 auto offsetOpt = getUIntFromValue(offsetOp);
2910 if (!offsetOpt)
2911 return failure();
2912
2913 uint64_t offset = *offsetOpt + *idxOpt;
2914 auto newOffset =
2915 ConstantOp::create(rewriter, op.getLoc(), offsetOp.getType(), offset);
2916 rewriter.replaceOpWithNewOp<ArrayGetOp>(op, inputSlice.getInput(),
2917 newOffset);
2918 return success();
2919 }
2920
2921 if (auto inputConcat = dyn_cast_or_null<ArrayConcatOp>(inputOp)) {
2922 // get(concat(a0, a1, ...), m) -> get(an, m - s0 - s1 - ...)
2923 uint64_t elemIndex = *idxOpt;
2924 for (auto input : llvm::reverse(inputConcat.getInputs())) {
2925 size_t size = hw::type_cast<ArrayType>(input.getType()).getNumElements();
2926 if (elemIndex >= size) {
2927 elemIndex -= size;
2928 continue;
2929 }
2930
2931 unsigned indexWidth = size == 1 ? 1 : llvm::Log2_64_Ceil(size);
2932 auto newIdxOp =
2933 ConstantOp::create(rewriter, op.getLoc(),
2934 rewriter.getIntegerType(indexWidth), elemIndex);
2935
2936 rewriter.replaceOpWithNewOp<ArrayGetOp>(op, input, newIdxOp);
2937 return success();
2938 }
2939 return failure();
2940 }
2941
2942 // array_get const, (array_get sel, (array_create a, b, c, d)) -->
2943 // array_get sel, (array_create (array_get const a), (array_get const b),
2944 // (array_get const, c), (array_get const, d))
2945 if (auto innerGet = dyn_cast_or_null<hw::ArrayGetOp>(inputOp)) {
2946 if (!innerGet.getIndex().getDefiningOp<hw::ConstantOp>()) {
2947 if (auto create =
2948 innerGet.getInput().getDefiningOp<hw::ArrayCreateOp>()) {
2949
2950 SmallVector<Value> newValues;
2951 for (auto operand : create.getOperands())
2952 newValues.push_back(rewriter.createOrFold<hw::ArrayGetOp>(
2953 op.getLoc(), operand, op.getIndex()));
2954
2955 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(
2956 op,
2957 rewriter.createOrFold<hw::ArrayCreateOp>(op.getLoc(), newValues),
2958 innerGet.getIndex());
2959 return success();
2960 }
2961 }
2962 }
2963
2964 return failure();
2965}
2966
2967//===----------------------------------------------------------------------===//
2968// ArrayInjectOp
2969//===----------------------------------------------------------------------===//
2970
2971OpFoldResult ArrayInjectOp::fold(FoldAdaptor adaptor) {
2972 auto inputAttr = dyn_cast_or_null<ArrayAttr>(adaptor.getInput());
2973 auto indexAttr = dyn_cast_or_null<IntegerAttr>(adaptor.getIndex());
2974 auto elementAttr = adaptor.getElement();
2975
2976 // inject(constant[xs, y, zs], iy, a) -> constant[x, a, z]
2977 if (inputAttr && indexAttr && elementAttr) {
2978 if (auto index = indexAttr.getValue().tryZExtValue()) {
2979 if (*index < inputAttr.size()) {
2980 SmallVector<Attribute> elements(inputAttr.getValue());
2981 elements[inputAttr.size() - 1 - *index] = elementAttr;
2982 return ArrayAttr::get(getContext(), elements);
2983 }
2984 }
2985 }
2986
2987 return {};
2988}
2989
2990static LogicalResult canonicalizeArrayInjectChain(ArrayInjectOp op,
2991 PatternRewriter &rewriter) {
2992 // If this inject is only used as an input to another inject, don't try to
2993 // canonicalize it. It will be included in that other op's canonicalization
2994 // attempt. This avoids doing redundant work.
2995 if (op->hasOneUse()) {
2996 auto &use = *op->use_begin();
2997 if (isa<ArrayInjectOp>(use.getOwner()) && use.getOperandNumber() == 0)
2998 return failure();
2999 }
3000
3001 // Collect all injects to constant indices.
3002 auto arrayLength = type_cast<ArrayType>(op.getType()).getNumElements();
3003 Value input = op;
3005 while (auto inject = input.getDefiningOp<ArrayInjectOp>()) {
3006 // Determine the constant index.
3007 APInt indexAPInt;
3008 if (!matchPattern(inject.getIndex(), mlir::m_ConstantInt(&indexAPInt)))
3009 break;
3010 if (indexAPInt.getActiveBits() > 32)
3011 break;
3012 uint32_t index = indexAPInt.getZExtValue();
3013
3014 // Track the injected value. Make sure to only track indices that are in
3015 // bounds. This will allow us to later check if `elements.size()` matches
3016 // the array length.
3017 if (index < arrayLength)
3018 elements.insert({index, inject.getElement()});
3019
3020 // Step to the next inject op.
3021 input = inject.getInput();
3022 if (input == op)
3023 break; // break cycles
3024 }
3025
3026 // If we are assigning every single element, replace the op with an
3027 // `hw.array_create`.
3028 if (elements.size() == arrayLength) {
3029 SmallVector<Value, 4> operands;
3030 operands.reserve(arrayLength);
3031 for (uint32_t idx = 0; idx < arrayLength; ++idx)
3032 operands.push_back(elements.at(arrayLength - idx - 1));
3033 rewriter.replaceOpWithNewOp<ArrayCreateOp>(op, op.getType(), operands);
3034 return success();
3035 }
3036
3037 return failure();
3038}
3039
3040static LogicalResult
3041canonicalizeArrayInjectIntoCreate(ArrayInjectOp op, PatternRewriter &rewriter) {
3042 auto createOp = op.getInput().getDefiningOp<ArrayCreateOp>();
3043 if (!createOp)
3044 return failure();
3045
3046 // Make sure the access is in bounds.
3047 APInt indexAPInt;
3048 if (!matchPattern(op.getIndex(), mlir::m_ConstantInt(&indexAPInt)) ||
3049 !indexAPInt.ult(createOp.getInputs().size()))
3050 return failure();
3051
3052 // Substitute the injected value.
3053 SmallVector<Value> elements = createOp.getInputs();
3054 elements[elements.size() - indexAPInt.getZExtValue() - 1] = op.getElement();
3055 rewriter.replaceOpWithNewOp<ArrayCreateOp>(op, elements);
3056 return success();
3057}
3058
3059void ArrayInjectOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
3060 MLIRContext *context) {
3061 patterns.add<ArrayInjectToSameIndex>(context);
3064}
3065
3066//===----------------------------------------------------------------------===//
3067// TypedeclOp
3068//===----------------------------------------------------------------------===//
3069
3070LogicalResult TypedeclOp::verify() {
3071 if (!isa_and_nonnull<hw::TypeScopeLike>(getOperation()->getParentOp()))
3072 return emitOpError("expects parent op to be a type scope, such as "
3073 "'hw.type_scope' or 'sv.package'");
3074 return success();
3075}
3076
3077StringRef TypedeclOp::getPreferredName() {
3078 return getVerilogName().value_or(getName());
3079}
3080
3081Type TypedeclOp::getAliasType() {
3082 auto parentScope = cast<hw::TypeScopeLike>(getOperation()->getParentOp());
3083 return hw::TypeAliasType::get(
3084 SymbolRefAttr::get(parentScope.getNameAttr(),
3085 {FlatSymbolRefAttr::get(*this)}),
3086 getType());
3087}
3088
3089//===----------------------------------------------------------------------===//
3090// BitcastOp
3091//===----------------------------------------------------------------------===//
3092
3093OpFoldResult BitcastOp::fold(FoldAdaptor adaptor) {
3094 // Identity.
3095 // bitcast(%a) : A -> A ==> %a
3096 if (getOperand().getType() == getType())
3097 return getOperand();
3098
3099 // bitcast(int_constant)
3100 if (auto intAttr = dyn_cast_or_null<IntegerAttr>(adaptor.getInput())) {
3101 ArrayAttr arrVal;
3102 if (succeeded(apIntToAggregateAttr(getType(), intAttr.getValue(), arrVal)))
3103 return arrVal;
3104 }
3105
3106 // bitcast(aggregate_constant)
3107 if (auto arrayAttr = dyn_cast_or_null<ArrayAttr>(adaptor.getInput())) {
3108 APInt intVal;
3109 if (succeeded(
3110 aggregateAttrToAPInt(getInput().getType(), arrayAttr, intVal))) {
3111 if (auto intType = dyn_cast<IntegerType>(getType()))
3112 return IntegerAttr::get(intType, intVal);
3113
3114 ArrayAttr arrVal;
3115 if (succeeded(apIntToAggregateAttr(getType(), intVal, arrVal)))
3116 return arrVal;
3117 }
3118 }
3119 return {};
3120}
3121
3122LogicalResult BitcastOp::canonicalize(BitcastOp op, PatternRewriter &rewriter) {
3123 // Composition.
3124 // %b = bitcast(%a) : A -> B
3125 // bitcast(%b) : B -> C
3126 // ===> bitcast(%a) : A -> C
3127 auto inputBitcast =
3128 dyn_cast_or_null<BitcastOp>(op.getInput().getDefiningOp());
3129 if (!inputBitcast)
3130 return failure();
3131 auto bitcast = rewriter.createOrFold<BitcastOp>(op.getLoc(), op.getType(),
3132 inputBitcast.getInput());
3133 rewriter.replaceOp(op, bitcast);
3134 return success();
3135}
3136
3137LogicalResult BitcastOp::verify() {
3138 if (getBitWidth(getInput().getType()) != getBitWidth(getResult().getType()))
3139 return this->emitOpError("Bitwidth of input must match result");
3140 return success();
3141}
3142
3143//===----------------------------------------------------------------------===//
3144// HierPathOp helpers.
3145//===----------------------------------------------------------------------===//
3146
3147bool HierPathOp::dropModule(StringAttr moduleToDrop) {
3148 SmallVector<Attribute, 4> newPath;
3149 bool updateMade = false;
3150 for (auto nameRef : getNamepath()) {
3151 // nameRef is either an InnerRefAttr or a FlatSymbolRefAttr.
3152 if (auto ref = dyn_cast<hw::InnerRefAttr>(nameRef)) {
3153 if (ref.getModule() == moduleToDrop)
3154 updateMade = true;
3155 else
3156 newPath.push_back(ref);
3157 } else {
3158 if (cast<FlatSymbolRefAttr>(nameRef).getAttr() == moduleToDrop)
3159 updateMade = true;
3160 else
3161 newPath.push_back(nameRef);
3162 }
3163 }
3164 if (updateMade)
3165 setNamepathAttr(ArrayAttr::get(getContext(), newPath));
3166 return updateMade;
3167}
3168
3169bool HierPathOp::inlineModule(StringAttr moduleToDrop) {
3170 SmallVector<Attribute, 4> newPath;
3171 bool updateMade = false;
3172 StringRef inlinedInstanceName = "";
3173 for (auto nameRef : getNamepath()) {
3174 // nameRef is either an InnerRefAttr or a FlatSymbolRefAttr.
3175 if (auto ref = dyn_cast<hw::InnerRefAttr>(nameRef)) {
3176 if (ref.getModule() == moduleToDrop) {
3177 inlinedInstanceName = ref.getName().getValue();
3178 updateMade = true;
3179 } else if (!inlinedInstanceName.empty()) {
3180 newPath.push_back(hw::InnerRefAttr::get(
3181 ref.getModule(),
3182 StringAttr::get(getContext(), inlinedInstanceName + "_" +
3183 ref.getName().getValue())));
3184 inlinedInstanceName = "";
3185 } else
3186 newPath.push_back(ref);
3187 } else {
3188 if (cast<FlatSymbolRefAttr>(nameRef).getAttr() == moduleToDrop)
3189 updateMade = true;
3190 else
3191 newPath.push_back(nameRef);
3192 }
3193 }
3194 if (updateMade)
3195 setNamepathAttr(ArrayAttr::get(getContext(), newPath));
3196 return updateMade;
3197}
3198
3199bool HierPathOp::updateModule(StringAttr oldMod, StringAttr newMod) {
3200 SmallVector<Attribute, 4> newPath;
3201 bool updateMade = false;
3202 for (auto nameRef : getNamepath()) {
3203 // nameRef is either an InnerRefAttr or a FlatSymbolRefAttr.
3204 if (auto ref = dyn_cast<hw::InnerRefAttr>(nameRef)) {
3205 if (ref.getModule() == oldMod) {
3206 newPath.push_back(hw::InnerRefAttr::get(newMod, ref.getName()));
3207 updateMade = true;
3208 } else
3209 newPath.push_back(ref);
3210 } else {
3211 if (cast<FlatSymbolRefAttr>(nameRef).getAttr() == oldMod) {
3212 newPath.push_back(FlatSymbolRefAttr::get(newMod));
3213 updateMade = true;
3214 } else
3215 newPath.push_back(nameRef);
3216 }
3217 }
3218 if (updateMade)
3219 setNamepathAttr(ArrayAttr::get(getContext(), newPath));
3220 return updateMade;
3221}
3222
3223bool HierPathOp::updateModuleAndInnerRef(
3224 StringAttr oldMod, StringAttr newMod,
3225 const llvm::DenseMap<StringAttr, StringAttr> &innerSymRenameMap) {
3226 auto fromRef = FlatSymbolRefAttr::get(oldMod);
3227 if (oldMod == newMod)
3228 return false;
3229
3230 auto namepathNew = getNamepath().getValue().vec();
3231 bool updateMade = false;
3232 // Break from the loop if the module is found, since it can occur only once.
3233 for (auto &element : namepathNew) {
3234 if (auto innerRef = dyn_cast<hw::InnerRefAttr>(element)) {
3235 if (innerRef.getModule() != oldMod)
3236 continue;
3237 auto symName = innerRef.getName();
3238 // Since the module got updated, the old innerRef symbol inside oldMod
3239 // should also be updated to the new symbol inside the newMod.
3240 auto to = innerSymRenameMap.find(symName);
3241 if (to != innerSymRenameMap.end())
3242 symName = to->second;
3243 updateMade = true;
3244 element = hw::InnerRefAttr::get(newMod, symName);
3245 break;
3246 }
3247 if (element != fromRef)
3248 continue;
3249
3250 updateMade = true;
3251 element = FlatSymbolRefAttr::get(newMod);
3252 break;
3253 }
3254 if (updateMade)
3255 setNamepathAttr(ArrayAttr::get(getContext(), namepathNew));
3256 return updateMade;
3257}
3258
3259bool HierPathOp::truncateAtModule(StringAttr atMod, bool includeMod) {
3260 SmallVector<Attribute, 4> newPath;
3261 bool updateMade = false;
3262 for (auto nameRef : getNamepath()) {
3263 // nameRef is either an InnerRefAttr or a FlatSymbolRefAttr.
3264 if (auto ref = dyn_cast<hw::InnerRefAttr>(nameRef)) {
3265 if (ref.getModule() == atMod) {
3266 updateMade = true;
3267 if (includeMod)
3268 newPath.push_back(ref);
3269 } else
3270 newPath.push_back(ref);
3271 } else {
3272 if (cast<FlatSymbolRefAttr>(nameRef).getAttr() == atMod && !includeMod)
3273 updateMade = true;
3274 else
3275 newPath.push_back(nameRef);
3276 }
3277 if (updateMade)
3278 break;
3279 }
3280 if (updateMade)
3281 setNamepathAttr(ArrayAttr::get(getContext(), newPath));
3282 return updateMade;
3283}
3284
3285/// Return just the module part of the namepath at a specific index.
3286StringAttr HierPathOp::modPart(unsigned i) {
3287 return TypeSwitch<Attribute, StringAttr>(getNamepath()[i])
3288 .Case<FlatSymbolRefAttr>([](auto a) { return a.getAttr(); })
3289 .Case<hw::InnerRefAttr>([](auto a) { return a.getModule(); });
3290}
3291
3292/// Return the root module.
3293StringAttr HierPathOp::root() {
3294 assert(!getNamepath().empty());
3295 return modPart(0);
3296}
3297
3298/// Return true if the NLA has the module in its path.
3299bool HierPathOp::hasModule(StringAttr modName) {
3300 for (auto nameRef : getNamepath()) {
3301 // nameRef is either an InnerRefAttr or a FlatSymbolRefAttr.
3302 if (auto ref = dyn_cast<hw::InnerRefAttr>(nameRef)) {
3303 if (ref.getModule() == modName)
3304 return true;
3305 } else {
3306 if (cast<FlatSymbolRefAttr>(nameRef).getAttr() == modName)
3307 return true;
3308 }
3309 }
3310 return false;
3311}
3312
3313/// Return true if the NLA has the InnerSym .
3314bool HierPathOp::hasInnerSym(StringAttr modName, StringAttr symName) const {
3315 for (auto nameRef : const_cast<HierPathOp *>(this)->getNamepath())
3316 if (auto ref = dyn_cast<hw::InnerRefAttr>(nameRef))
3317 if (ref.getName() == symName && ref.getModule() == modName)
3318 return true;
3319
3320 return false;
3321}
3322
3323/// Return just the reference part of the namepath at a specific index. This
3324/// will return an empty attribute if this is the leaf and the leaf is a module.
3325StringAttr HierPathOp::refPart(unsigned i) {
3326 return TypeSwitch<Attribute, StringAttr>(getNamepath()[i])
3327 .Case<FlatSymbolRefAttr>([](auto a) { return StringAttr({}); })
3328 .Case<hw::InnerRefAttr>([](auto a) { return a.getName(); });
3329}
3330
3331/// Return the leaf reference. This returns an empty attribute if the leaf
3332/// reference is a module.
3333StringAttr HierPathOp::ref() {
3334 assert(!getNamepath().empty());
3335 return refPart(getNamepath().size() - 1);
3336}
3337
3338/// Return the leaf module.
3339StringAttr HierPathOp::leafMod() {
3340 assert(!getNamepath().empty());
3341 return modPart(getNamepath().size() - 1);
3342}
3343
3344/// Returns true if this NLA targets an instance of a module (as opposed to
3345/// an instance's port or something inside an instance).
3346bool HierPathOp::isModule() { return !ref(); }
3347
3348/// Returns true if this NLA targets something inside a module (as opposed
3349/// to a module or an instance of a module);
3350bool HierPathOp::isComponent() { return (bool)ref(); }
3351
3352// Verify the HierPathOp.
3353// 1. Iterate over the namepath.
3354// 2. The namepath should be a valid instance path, specified either on a
3355// module or a declaration inside a module.
3356// 3. Each element in the namepath is an InnerRefAttr except possibly the
3357// last element.
3358// 4. Make sure that the InnerRefAttr is legal, by verifying the module name
3359// and the corresponding inner_sym on the instance.
3360// 5. Make sure that the instance path is legal, by verifying the sequence of
3361// instance and the expected module occurs as the next element in the path.
3362// 6. The last element of the namepath, can be an InnerRefAttr on either a
3363// module port or a declaration inside the module.
3364// 7. The last element of the namepath can also be a module symbol.
3365LogicalResult HierPathOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
3366 ArrayAttr expectedModuleNames = {};
3367 auto checkExpectedModule = [&](Attribute name) -> LogicalResult {
3368 if (!expectedModuleNames)
3369 return success();
3370 if (llvm::any_of(expectedModuleNames,
3371 [name](Attribute attr) { return attr == name; }))
3372 return success();
3373 auto diag = emitOpError() << "instance path is incorrect. Expected ";
3374 size_t n = expectedModuleNames.size();
3375 if (n != 1) {
3376 diag << "one of ";
3377 }
3378 for (size_t i = 0; i < n; ++i) {
3379 if (i != 0)
3380 diag << ((i + 1 == n) ? " or " : ", ");
3381 diag << cast<StringAttr>(expectedModuleNames[i]);
3382 }
3383 diag << ". Instead found: " << name;
3384 return diag;
3385 };
3386
3387 if (!getNamepath() || getNamepath().empty())
3388 return emitOpError() << "the instance path cannot be empty";
3389 for (unsigned i = 0, s = getNamepath().size() - 1; i < s; ++i) {
3390 hw::InnerRefAttr innerRef = dyn_cast<hw::InnerRefAttr>(getNamepath()[i]);
3391 if (!innerRef)
3392 return emitOpError()
3393 << "the instance path can only contain inner sym reference"
3394 << ", only the leaf can refer to a module symbol";
3395
3396 if (failed(checkExpectedModule(innerRef.getModule())))
3397 return failure();
3398
3399 auto instOp = ns.lookupOp<igraph::InstanceOpInterface>(innerRef);
3400 if (!instOp)
3401 return emitOpError() << " module: " << innerRef.getModule()
3402 << " does not contain any instance with symbol: "
3403 << innerRef.getName();
3404 expectedModuleNames = instOp.getReferencedModuleNamesAttr();
3405 }
3406
3407 // The instance path has been verified. Now verify the last element.
3408 auto leafRef = getNamepath()[getNamepath().size() - 1];
3409 if (auto innerRef = dyn_cast<hw::InnerRefAttr>(leafRef)) {
3410 if (!ns.lookup(innerRef)) {
3411 return emitOpError() << " operation with symbol: " << innerRef
3412 << " was not found ";
3413 }
3414 if (failed(checkExpectedModule(innerRef.getModule())))
3415 return failure();
3416 } else if (failed(checkExpectedModule(
3417 cast<FlatSymbolRefAttr>(leafRef).getAttr()))) {
3418 return failure();
3419 }
3420 return success();
3421}
3422
3423void HierPathOp::print(OpAsmPrinter &p) {
3424 p << " ";
3425
3426 // Print visibility if present.
3427 StringRef visibilityAttrName =
3428 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
3429 if (auto visibility =
3430 getOperation()->getAttrOfType<StringAttr>(visibilityAttrName))
3431 p << visibility.getValue() << ' ';
3432
3433 p.printSymbolName(getSymName());
3434 p << " [";
3435 llvm::interleaveComma(getNamepath().getValue(), p, [&](Attribute attr) {
3436 if (auto ref = dyn_cast<hw::InnerRefAttr>(attr)) {
3437 p.printSymbolName(ref.getModule().getValue());
3438 p << "::";
3439 p.printSymbolName(ref.getName().getValue());
3440 } else {
3441 p.printSymbolName(cast<FlatSymbolRefAttr>(attr).getValue());
3442 }
3443 });
3444 p << "]";
3445 p.printOptionalAttrDict(
3446 (*this)->getAttrs(),
3447 {getSymNameAttrName(), "namepath", visibilityAttrName});
3448}
3449
3450ParseResult HierPathOp::parse(OpAsmParser &parser, OperationState &result) {
3451 // Parse the visibility attribute.
3452 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
3453
3454 // Parse the symbol name.
3455 StringAttr symName;
3456 if (parser.parseSymbolName(symName, getSymNameAttrName(result.name),
3457 result.attributes))
3458 return failure();
3459
3460 // Parse the namepath.
3461 SmallVector<Attribute> namepath;
3462 if (parser.parseCommaSeparatedList(
3463 OpAsmParser::Delimiter::Square, [&]() -> ParseResult {
3464 auto loc = parser.getCurrentLocation();
3465 SymbolRefAttr ref;
3466 if (parser.parseAttribute(ref))
3467 return failure();
3468
3469 // "A" is a Ref, "A::b" is a InnerRef, "A::B::c" is an error.
3470 auto pathLength = ref.getNestedReferences().size();
3471 if (pathLength == 0)
3472 namepath.push_back(
3473 FlatSymbolRefAttr::get(ref.getRootReference()));
3474 else if (pathLength == 1)
3475 namepath.push_back(hw::InnerRefAttr::get(ref.getRootReference(),
3476 ref.getLeafReference()));
3477 else
3478 return parser.emitError(loc,
3479 "only one nested reference is allowed");
3480 return success();
3481 }))
3482 return failure();
3483 result.addAttribute("namepath",
3484 ArrayAttr::get(parser.getContext(), namepath));
3485
3486 if (parser.parseOptionalAttrDict(result.attributes))
3487 return failure();
3488
3489 return success();
3490}
3491
3492//===----------------------------------------------------------------------===//
3493// TriggeredOp
3494//===----------------------------------------------------------------------===//
3495
3496void TriggeredOp::build(OpBuilder &builder, OperationState &odsState,
3497 EventControlAttr event, Value trigger,
3498 ValueRange inputs) {
3499 odsState.addOperands(trigger);
3500 odsState.addOperands(inputs);
3501 odsState.addAttribute(getEventAttrName(odsState.name), event);
3502 auto *r = odsState.addRegion();
3503 Block *b = new Block();
3504 r->push_back(b);
3505
3506 llvm::SmallVector<Location> argLocs;
3507 llvm::transform(inputs, std::back_inserter(argLocs),
3508 [&](Value v) { return v.getLoc(); });
3509 b->addArguments(inputs.getTypes(), argLocs);
3510}
3511
3512//===----------------------------------------------------------------------===//
3513// TableGen generated logic.
3514//===----------------------------------------------------------------------===//
3515
3516// Provide the autogenerated implementation guts for the Op classes.
3517#define GET_OP_CLASSES
3518#include "circt/Dialect/HW/HW.cpp.inc"
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
static std::unique_ptr< Context > context
static void buildModule(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports, ArrayAttr annotations, ArrayAttr layers)
void getAsmBlockArgumentNamesImpl(Operation *op, mlir::Region &region, OpAsmSetValueNameFn setNameFn)
Get a special name to use when printing the entry block arguments of the region contained by an opera...
static LogicalResult verifyModuleCommon(HWModuleLike module)
Definition HWOps.cpp:1130
static void printParamValue(OpAsmPrinter &p, Operation *, Attribute value, Type resultType)
Definition HWOps.cpp:547
static LogicalResult canonicalizeArrayInjectChain(ArrayInjectOp op, PatternRewriter &rewriter)
Definition HWOps.cpp:2990
static void printModuleOp(OpAsmPrinter &p, ModuleTy mod)
Definition HWOps.cpp:1073
static bool flattenConcatOp(ArrayConcatOp op, PatternRewriter &rewriter)
Definition HWOps.cpp:2073
static LogicalResult foldCreateToSlice(ArrayCreateOp op, PatternRewriter &rewriter)
Definition HWOps.cpp:1803
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
Definition HWOps.cpp:1495
static ArrayAttr arrayOrEmpty(mlir::MLIRContext *context, ArrayRef< Attribute > attrs)
Definition HWOps.cpp:84
FunctionType getHWModuleOpType(Operation *op)
Definition HWOps.cpp:1065
static void printExtractOp(OpAsmPrinter &printer, AggType op)
Use the same printer for both struct_extract and union_extract since the syntax is identical.
Definition HWOps.cpp:2496
static void printArrayConcatTypes(OpAsmPrinter &p, Operation *, TypeRange inputTypes, Type resultType)
Definition HWOps.cpp:2034
static ParseResult parseSliceTypes(OpAsmParser &p, Type &srcType, Type &idxType)
Definition HWOps.cpp:1690
static void modifyModulePorts(Operation *op, ArrayRef< std::pair< unsigned, PortInfo > > insertInputs, ArrayRef< std::pair< unsigned, PortInfo > > insertOutputs, ArrayRef< unsigned > removeInputs, ArrayRef< unsigned > removeOutputs, Block *body=nullptr)
Insert and remove ports of a module.
Definition HWOps.cpp:736
static Value foldStructExtract(Operation *inputOp, uint32_t fieldIndex)
Definition HWOps.cpp:69
static bool hasAttribute(StringRef name, ArrayRef< NamedAttribute > attrs)
Definition HWOps.cpp:942
static void modifyModuleArgs(MLIRContext *context, ArrayRef< std::pair< unsigned, PortInfo > > insertArgs, ArrayRef< unsigned > removeArgs, ArrayRef< Attribute > oldArgNames, ArrayRef< Type > oldArgTypes, ArrayRef< Attribute > oldArgAttrs, ArrayRef< Location > oldArgLocs, SmallVector< Attribute > &newArgNames, SmallVector< Type > &newArgTypes, SmallVector< Attribute > &newArgAttrs, SmallVector< Location > &newArgLocs, Block *body=nullptr)
Internal implementation of argument/result insertion and removal on modules.
Definition HWOps.cpp:643
static bool mergeConcatSlices(ArrayConcatOp op, PatternRewriter &rewriter)
Definition HWOps.cpp:2090
static SmallVector< Location > getAllPortLocs(ModTy module)
Definition HWOps.cpp:1273
static ParseResult parseExtractOp(OpAsmParser &parser, OperationState &result)
Use the same parser for both struct_extract and union_extract since the syntax is identical.
Definition HWOps.cpp:2459
static void setAllPortNames(ArrayRef< Attribute > names, ModTy module)
Definition HWOps.cpp:1343
static void getAsmBlockArgumentNamesImpl(mlir::Region &region, OpAsmSetValueNameFn setNameFn)
Get a special name to use when printing the entry block arguments of the region contained by an opera...
Definition HWOps.cpp:101
static void setHWModuleType(ModTy &mod, ModuleType type)
Definition HWOps.cpp:1416
static ParseResult parseParamValue(OpAsmParser &p, Attribute &value, Type &resultType)
Definition HWOps.cpp:539
static LogicalResult checkAttributes(Operation *op, Attribute attr, Type type)
Definition HWOps.cpp:453
static LogicalResult canonicalizeArrayInjectIntoCreate(ArrayInjectOp op, PatternRewriter &rewriter)
Definition HWOps.cpp:3041
static std::optional< uint64_t > getUIntFromValue(Value value)
Definition HWOps.cpp:1874
static ParseResult parseHWModuleOp(OpAsmParser &parser, OperationState &result)
Definition HWOps.cpp:950
static LogicalResult verifyAggregateFieldIndexAndType(AggregateOp &op, AggregateType aggType, Type elementType)
Ensure an aggregate op's field index is within the bounds of the aggregate type and the accessed fiel...
Definition HWOps.cpp:2433
static PortInfo getPort(ModuleTy &mod, size_t idx)
Definition HWOps.cpp:1515
static void printSliceTypes(OpAsmPrinter &p, Operation *, Type srcType, Type idxType)
Definition HWOps.cpp:1704
static bool hasAdditionalAttributes(Op op, ArrayRef< StringRef > ignoredAttrs={})
Check whether an operation has any additional attributes set beyond its standard list of attributes r...
Definition HWOps.cpp:395
Delimiter
Definition HWOps.cpp:116
@ OptionalLessGreater
static ParseResult parseArrayConcatTypes(OpAsmParser &p, SmallVectorImpl< Type > &inputTypes, Type &resultType)
Definition HWOps.cpp:2004
static bool getFieldName(const FieldRef &fieldRef, SmallString< 32 > &string)
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:222
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
static Block * getBodyBlock(FModuleLike mod)
static InstancePath empty
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:30
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Definition Namespace.h:86
void setOutput(unsigned i, Value v)
Definition HWOps.cpp:241
Value getInput(unsigned i)
Definition HWOps.cpp:247
llvm::SmallVector< Value > outputOperands
Definition HWOps.h:120
llvm::SmallVector< Value > inputArgs
Definition HWOps.h:119
llvm::StringMap< unsigned > outputIdx
Definition HWOps.h:118
llvm::StringMap< unsigned > inputIdx
Definition HWOps.h:118
HWModulePortAccessor(Location loc, const ModulePortInfo &info, Region &bodyRegion)
Definition HWOps.cpp:225
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
This helps visit TypeOp nodes.
Definition HWVisitors.h:25
ResultType dispatchTypeOpVisitor(Operation *op, ExtraArgs... args)
Definition HWVisitors.h:27
ResultType visitUnhandledTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any combinational operations that are not handled by the concrete visitor...
Definition HWVisitors.h:57
ResultType visitInvalidTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any non-expression operations.
Definition HWVisitors.h:50
create(array_value, idx)
Definition hw.py:453
create(array_value, low_index, ret_type)
Definition hw.py:469
create(data_type, value)
Definition hw.py:433
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
uint64_t getWidth(Type t)
Definition ESIPasses.cpp:32
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
ModuleType fnToMod(Operation *op, ArrayRef< Attribute > inputNames, ArrayRef< Attribute > outputNames)
Definition HWTypes.cpp:1119
LogicalResult verifyParameterStructure(ArrayAttr parameters, ArrayAttr moduleParameters, const EmitErrorFn &emitError)
Check that all the parameter values specified to the instance are structurally valid.
std::function< void(std::function< bool(InFlightDiagnostic &)>)> EmitErrorFn
Whenever the nested function returns true, a note referring to the referenced module is attached to t...
LogicalResult verifyInstanceOfHWModule(Operation *instance, FlatSymbolRefAttr moduleRef, OperandRange inputs, TypeRange results, ArrayAttr argNames, ArrayAttr resultNames, ArrayAttr parameters, SymbolTableCollection &symbolTable)
Combines verifyReferencedModule, verifyInputs, verifyOutputs, and verifyParameters.
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)
bool isOffset(Value base, Value index, uint64_t offset)
Definition HWOps.cpp:1779
llvm::function_ref< void(OpBuilder &, HWModulePortAccessor &)> HWModuleBuilder
Definition HWOps.h:125
FunctionType getModuleType(Operation *module)
Return the signature for the specified module as a function type.
Definition HWOps.cpp:575
LogicalResult checkParameterInContext(Attribute value, Operation *module, Operation *usingOp, bool disallowParamRefs=false)
Check parameter specified by value to see if it is valid within the scope of the specified module mod...
Definition HWOps.cpp:202
LogicalResult aggregateAttrToAPInt(mlir::Type type, ArrayAttr attr, APInt &result)
Convert an ArrayAttr into an APInt value matching the given type.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition HWTypes.cpp:121
bool isAnyModuleOrInstance(Operation *module)
TODO: Move all these functions to a hw::ModuleLike interface.
Definition HWOps.cpp:569
LogicalResult apIntToAggregateAttr(mlir::Type aggregateType, const APInt &intVal, ArrayAttr &result)
Convert an APInt value into a nested aggregate attribute matching the given HWAggregateType.
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
Definition HWOps.cpp:593
mlir::Type getCanonicalType(mlir::Type type)
Recursively remove HW type aliases from a type and its subelements.
Definition HWTypes.cpp:50
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.
ParseResult parseOptionalParameterList(OpAsmParser &parser, ArrayAttr &parameters)
Parse an parameter list if present.
void printOptionalParameterList(OpAsmPrinter &p, Operation *op, ArrayAttr parameters)
Print a parameter list for a module or instance.
StringRef chooseName(StringRef a, StringRef b)
Choose a good name for an item from two options.
Definition Naming.cpp:47
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:193
This class represents the namespace in which InnerRef's can be resolved.
InnerSymTarget lookup(hw::InnerRefAttr inner) const
Resolve the InnerRef to its target within this namespace, returning empty target if no such name exis...
Operation * lookupOp(hw::InnerRefAttr inner) const
Resolve the InnerRef to its target within this namespace, returning empty target if no such name exis...
This holds a decoded list of input/inout and output ports for a module or instance.
PortInfo & at(size_t idx)
PortDirectionRange getOutputs()
mlir::Type type
Definition HWTypes.h:34
mlir::StringAttr name
Definition HWTypes.h:33
This holds the name, type, direction of a module's ports.