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