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