CIRCT 20.0.0git
Loading...
Searching...
No Matches
FlattenIO.cpp
Go to the documentation of this file.
1//===- FlattenIO.cpp - HW I/O flattening pass -------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
11#include "mlir/Pass/Pass.h"
12#include "mlir/Transforms/DialectConversion.h"
13#include "llvm/ADT/TypeSwitch.h"
14
15namespace circt {
16namespace hw {
17#define GEN_PASS_DEF_FLATTENIO
18#include "circt/Dialect/HW/Passes.h.inc"
19} // namespace hw
20} // namespace circt
21
22using namespace mlir;
23using namespace circt;
24
25static bool isStructType(Type type) {
26 return isa<hw::StructType>(hw::getCanonicalType(type));
27}
28
29static hw::StructType getStructType(Type type) {
30 return dyn_cast<hw::StructType>(hw::getCanonicalType(type));
31}
32
33// Legal if no in- or output type is a struct.
34static bool isLegalModLikeOp(hw::HWModuleLike moduleLikeOp) {
35 return llvm::none_of(moduleLikeOp.getHWModuleType().getPortTypes(),
37}
38
39static llvm::SmallVector<Type> getInnerTypes(hw::StructType t) {
40 llvm::SmallVector<Type> inner;
41 t.getInnerTypes(inner);
42 for (auto [index, innerType] : llvm::enumerate(inner))
43 inner[index] = hw::getCanonicalType(innerType);
44 return inner;
45}
46
47namespace {
48
49/// Flatten the given value ranges into a single vector of values.
50static SmallVector<Value> flattenValues(ArrayRef<ValueRange> values) {
51 SmallVector<Value> result;
52 for (const auto &vals : values)
53 llvm::append_range(result, vals);
54 return result;
55}
56
57// Replaces an output op with a new output with flattened (exploded) structs.
58struct OutputOpConversion : public OpConversionPattern<hw::OutputOp> {
59 OutputOpConversion(TypeConverter &typeConverter, MLIRContext *context,
60 DenseSet<Operation *> *opVisited)
61 : OpConversionPattern(typeConverter, context), opVisited(opVisited) {}
62
63 LogicalResult
64 matchAndRewrite(hw::OutputOp op, OpAdaptor adaptor,
65 ConversionPatternRewriter &rewriter) const override {
66 llvm::SmallVector<Value> convOperands;
67
68 // Flatten the operands.
69 for (auto operand : adaptor.getOperands()) {
70 if (auto structType = getStructType(operand.getType())) {
71 auto explodedStruct = rewriter.create<hw::StructExplodeOp>(
72 op.getLoc(), getInnerTypes(structType), operand);
73 llvm::copy(explodedStruct.getResults(),
74 std::back_inserter(convOperands));
75 } else {
76 convOperands.push_back(operand);
77 }
78 }
79
80 // And replace.
81 opVisited->insert(op->getParentOp());
82 rewriter.replaceOpWithNewOp<hw::OutputOp>(op, convOperands);
83 return success();
84 }
85
86 LogicalResult
87 matchAndRewrite(hw::OutputOp op, OneToNOpAdaptor adaptor,
88 ConversionPatternRewriter &rewriter) const override {
89 llvm::SmallVector<Value> convOperands;
90
91 // Flatten the operands.
92 for (auto operand : flattenValues(adaptor.getOperands())) {
93 if (auto structType = getStructType(operand.getType())) {
94 auto explodedStruct = rewriter.create<hw::StructExplodeOp>(
95 op.getLoc(), getInnerTypes(structType), operand);
96 llvm::copy(explodedStruct.getResults(),
97 std::back_inserter(convOperands));
98 } else {
99 convOperands.push_back(operand);
100 }
101 }
102
103 // And replace.
104 opVisited->insert(op->getParentOp());
105 rewriter.replaceOpWithNewOp<hw::OutputOp>(op, convOperands);
106 return success();
107 }
108 DenseSet<Operation *> *opVisited;
109};
110
111struct InstanceOpConversion : public OpConversionPattern<hw::InstanceOp> {
112 InstanceOpConversion(TypeConverter &typeConverter, MLIRContext *context,
113 DenseSet<hw::InstanceOp> *convertedOps,
114 const StringSet<> *externModules)
115 : OpConversionPattern(typeConverter, context), convertedOps(convertedOps),
116 externModules(externModules) {}
117
118 LogicalResult
119 matchAndRewrite(hw::InstanceOp op, OneToNOpAdaptor adaptor,
120 ConversionPatternRewriter &rewriter) const override {
121 auto referencedMod = op.getReferencedModuleNameAttr();
122 // If externModules is populated and this is an extern module instance,
123 // donot flatten it.
124 if (externModules->contains(referencedMod.getValue()))
125 return success();
126
127 auto loc = op.getLoc();
128 // Flatten the operands.
129 llvm::SmallVector<Value> convOperands;
130 for (auto operand : flattenValues(adaptor.getOperands())) {
131 if (auto structType = getStructType(operand.getType())) {
132 auto explodedStruct = rewriter.create<hw::StructExplodeOp>(
133 loc, getInnerTypes(structType), operand);
134 llvm::copy(explodedStruct.getResults(),
135 std::back_inserter(convOperands));
136 } else {
137 convOperands.push_back(operand);
138 }
139 }
140
141 // Get the new module return type.
142 llvm::SmallVector<Type> newResultTypes;
143 for (auto oldResultType : op.getResultTypes()) {
144 if (auto structType = getStructType(oldResultType))
145 for (auto t : structType.getElements())
146 newResultTypes.push_back(t.type);
147 else
148 newResultTypes.push_back(oldResultType);
149 }
150
151 // Create the new instance with the flattened module, attributes will be
152 // adjusted later.
153 auto newInstance = rewriter.create<hw::InstanceOp>(
154 loc, newResultTypes, op.getInstanceNameAttr(),
155 FlatSymbolRefAttr::get(referencedMod), convOperands,
156 op.getArgNamesAttr(), op.getResultNamesAttr(), op.getParametersAttr(),
157 op.getInnerSymAttr(), op.getDoNotPrintAttr());
158
159 // re-create any structs in the result.
160 llvm::SmallVector<Value> convResults;
161 size_t oldResultCntr = 0;
162 for (size_t resIndex = 0; resIndex < newInstance.getNumResults();
163 ++resIndex) {
164 Type oldResultType = op.getResultTypes()[oldResultCntr];
165 if (auto structType = getStructType(oldResultType)) {
166 size_t nElements = structType.getElements().size();
167 auto implodedStruct = rewriter.create<hw::StructCreateOp>(
168 loc, structType,
169 newInstance.getResults().slice(resIndex, nElements));
170 convResults.push_back(implodedStruct.getResult());
171 resIndex += nElements - 1;
172 } else
173 convResults.push_back(newInstance.getResult(resIndex));
174
175 ++oldResultCntr;
176 }
177 rewriter.replaceOp(op, convResults);
178 convertedOps->insert(newInstance);
179 return success();
180 }
181
182 DenseSet<hw::InstanceOp> *convertedOps;
183 const StringSet<> *externModules;
184};
185
186using IOTypes = std::pair<TypeRange, TypeRange>;
187
188struct IOInfo {
189 // A mapping between an arg/res index and the struct type of the given field.
190 DenseMap<unsigned, hw::StructType> argStructs, resStructs;
191
192 // Records of the original arg/res types.
193 SmallVector<Type> argTypes, resTypes;
194};
195
196class FlattenIOTypeConverter : public TypeConverter {
197public:
198 FlattenIOTypeConverter() {
199 addConversion([](Type type, SmallVectorImpl<Type> &results) {
200 auto structType = getStructType(type);
201 if (!structType)
202 results.push_back(type);
203 else {
204 for (auto field : structType.getElements())
205 results.push_back(field.type);
206 }
207 return success();
208 });
209
210 // Materialize !hw.struct<a,b,...> to a, b, ... via. hw.explode. This
211 // situation may occur in case of hw.extern_module's with struct outputs.
212 addTargetMaterialization([](OpBuilder &builder, TypeRange resultTypes,
213 ValueRange inputs, Location loc) {
214 if (inputs.size() != 1 && !isStructType(inputs[0].getType()))
215 return ValueRange();
216
217 auto explodeOp = builder.create<hw::StructExplodeOp>(loc, inputs[0]);
218 return ValueRange(explodeOp.getResults());
219 });
220 addTargetMaterialization([](OpBuilder &builder, hw::StructType type,
221 ValueRange inputs, Location loc) {
222 auto result = builder.create<hw::StructCreateOp>(loc, type, inputs);
223 return result.getResult();
224 });
225
226 addTargetMaterialization([](OpBuilder &builder, hw::TypeAliasType type,
227 ValueRange inputs, Location loc) {
228 auto result = builder.create<hw::StructCreateOp>(loc, type, inputs);
229 return result.getResult();
230 });
231
232 // In the presence of hw.extern_module which takes struct arguments, we may
233 // have materialized struct explodes for said arguments (say, e.g., if the
234 // parent module of the hw.instance had structs in its input, and feeds
235 // these structs to the hw.instance).
236 // These struct explodes needs to be converted back to the original struct,
237 // which persist beyond the conversion.
238 addSourceMaterialization([](OpBuilder &builder, hw::StructType type,
239 ValueRange inputs, Location loc) {
240 auto result = builder.create<hw::StructCreateOp>(loc, type, inputs);
241 return result.getResult();
242 });
243 }
244};
245
246} // namespace
247
248template <typename... TOp>
249static void addSignatureConversion(DenseMap<Operation *, IOInfo> &ioMap,
250 ConversionTarget &target,
251 RewritePatternSet &patterns,
252 FlattenIOTypeConverter &typeConverter) {
253 (hw::populateHWModuleLikeTypeConversionPattern(TOp::getOperationName(),
254 patterns, typeConverter),
255 ...);
256
257 // Legality is defined by a module having been processed once. This is due to
258 // that a pattern cannot be applied multiple times (a 'pattern was already
259 // applied' error - a case that would occur for nested structs). Additionally,
260 // if a pattern could be applied multiple times, this would complicate
261 // updating arg/res names.
262
263 // Instead, we define legality as when a module has had a modification to its
264 // top-level i/o. This ensures that only a single level of structs are
265 // processed during signature conversion, which then allows us to use the
266 // signature conversion in a recursive manner.
267 target.addDynamicallyLegalOp<TOp...>([&](hw::HWModuleLike moduleLikeOp) {
268 if (isLegalModLikeOp(moduleLikeOp))
269 return true;
270
271 // This op is involved in conversion. Check if the signature has changed.
272 auto ioInfoIt = ioMap.find(moduleLikeOp);
273 if (ioInfoIt == ioMap.end()) {
274 // Op wasn't primed in the map. Do the safe thing, assume
275 // that it's not considered in this pass, and mark it as legal
276 return true;
277 }
278 auto ioInfo = ioInfoIt->second;
279
280 auto compareTypes = [&](TypeRange oldTypes, TypeRange newTypes) {
281 return llvm::any_of(llvm::zip(oldTypes, newTypes), [&](auto typePair) {
282 auto oldType = std::get<0>(typePair);
283 auto newType = std::get<1>(typePair);
284 return oldType != newType;
285 });
286 };
287 auto mtype = moduleLikeOp.getHWModuleType();
288 if (compareTypes(mtype.getOutputTypes(), ioInfo.resTypes) ||
289 compareTypes(mtype.getInputTypes(), ioInfo.argTypes))
290 return true;
291
292 // We're pre-conversion for an op that was primed in the map - it will
293 // always be illegal since it has to-be-converted struct types at its I/O.
294 return false;
295 });
296}
297
298template <typename T>
299static bool hasUnconvertedOps(mlir::ModuleOp module) {
300 return llvm::any_of(module.getBody()->getOps<T>(),
301 [](T op) { return !isLegalModLikeOp(op); });
302}
303
304template <typename T>
305static DenseMap<Operation *, IOTypes> populateIOMap(mlir::ModuleOp module) {
306 DenseMap<Operation *, IOTypes> ioMap;
307 for (auto op : module.getOps<T>())
308 ioMap[op] = {op.getArgumentTypes(), op.getResultTypes()};
309 return ioMap;
310}
311
312template <typename ModTy, typename T>
313static llvm::SmallVector<Attribute>
314updateNameAttribute(ModTy op, StringRef attrName,
315 DenseMap<unsigned, hw::StructType> &structMap, T oldNames,
316 char joinChar) {
317 llvm::SmallVector<Attribute> newNames;
318 for (auto [i, oldName] : llvm::enumerate(oldNames)) {
319 // Was this arg/res index a struct?
320 auto it = structMap.find(i);
321 if (it == structMap.end()) {
322 // No, keep old name.
323 newNames.push_back(StringAttr::get(op->getContext(), oldName));
324 continue;
325 }
326
327 // Yes - create new names from the struct fields and the old name at the
328 // index.
329 auto structType = it->second;
330 for (auto field : structType.getElements())
331 newNames.push_back(StringAttr::get(
332 op->getContext(), oldName + Twine(joinChar) + field.name.str()));
333 }
334 return newNames;
335}
336
337template <typename ModTy>
338static void updateModulePortNames(ModTy op, hw::ModuleType oldModType,
339 char joinChar) {
340 // Module arg and result port names may not be ordered. So we cannot reuse
341 // updateNameAttribute. The arg and result order must be preserved.
342 SmallVector<Attribute> newNames;
343 SmallVector<hw::ModulePort> oldPorts(oldModType.getPorts().begin(),
344 oldModType.getPorts().end());
345 for (auto oldPort : oldPorts) {
346 auto oldName = oldPort.name;
347 if (auto structType = getStructType(oldPort.type)) {
348 for (auto field : structType.getElements()) {
349 newNames.push_back(StringAttr::get(
350 op->getContext(),
351 oldName.getValue() + Twine(joinChar) + field.name.str()));
352 }
353 } else
354 newNames.push_back(oldName);
355 }
356 op.setAllPortNames(newNames);
357}
358
359static llvm::SmallVector<Location>
360updateLocAttribute(DenseMap<unsigned, hw::StructType> &structMap,
361 SmallVectorImpl<Location> &oldLocs) {
362 llvm::SmallVector<Location> newLocs;
363 for (auto [i, oldLoc] : llvm::enumerate(oldLocs)) {
364 // Was this arg/res index a struct?
365 auto it = structMap.find(i);
366 if (it == structMap.end()) {
367 // No, keep old name.
368 newLocs.push_back(oldLoc);
369 continue;
370 }
371
372 auto structType = it->second;
373 for (size_t i = 0, e = structType.getElements().size(); i < e; ++i)
374 newLocs.push_back(oldLoc);
375 }
376 return newLocs;
377}
378
379/// The conversion framework seems to throw away block argument locations. We
380/// use this function to copy the location from the original argument to the
381/// set of flattened arguments.
382static void
383updateBlockLocations(hw::HWModuleLike op,
384 DenseMap<unsigned, hw::StructType> &structMap) {
385 auto locs = op.getInputLocs();
386 if (locs.empty() || op.getModuleBody().empty())
387 return;
388 for (auto [arg, loc] : llvm::zip(op.getBodyBlock()->getArguments(), locs))
389 arg.setLoc(loc);
390}
391
392static void setIOInfo(hw::HWModuleLike op, IOInfo &ioInfo) {
393 ioInfo.argTypes = op.getInputTypes();
394 ioInfo.resTypes = op.getOutputTypes();
395 for (auto [i, arg] : llvm::enumerate(ioInfo.argTypes)) {
396 if (auto structType = getStructType(arg))
397 ioInfo.argStructs[i] = structType;
398 }
399 for (auto [i, res] : llvm::enumerate(ioInfo.resTypes)) {
400 if (auto structType = getStructType(res))
401 ioInfo.resStructs[i] = structType;
402 }
403}
404
405template <typename T>
406static DenseMap<Operation *, IOInfo> populateIOInfoMap(mlir::ModuleOp module) {
407 DenseMap<Operation *, IOInfo> ioInfoMap;
408 for (auto op : module.getOps<T>()) {
409 IOInfo ioInfo;
410 setIOInfo(op, ioInfo);
411 ioInfoMap[op] = ioInfo;
412 }
413 return ioInfoMap;
414}
415
416template <typename T>
417static LogicalResult flattenOpsOfType(ModuleOp module, bool recursive,
418 StringSet<> &externModules,
419 char joinChar) {
420 auto *ctx = module.getContext();
421 FlattenIOTypeConverter typeConverter;
422
423 // Recursively (in case of nested structs) lower the module. We do this one
424 // conversion at a time to allow for updating the arg/res names of the
425 // module in between flattening each level of structs.
426 while (hasUnconvertedOps<T>(module)) {
427 ConversionTarget target(*ctx);
428 RewritePatternSet patterns(ctx);
429 target.addLegalDialect<hw::HWDialect>();
430
431 // Record any struct types at the module signature. This will be used
432 // post-conversion to update the argument and result names.
433 auto ioInfoMap = populateIOInfoMap<T>(module);
434
435 // Record the instances that were converted. We keep these around since we
436 // need to update their arg/res attribute names after the modules themselves
437 // have been updated.
438 llvm::DenseSet<hw::InstanceOp> convertedInstances;
439
440 // Argument conversion for output ops. Similarly to the signature
441 // conversion, legality is based on the op having been visited once, due to
442 // the possibility of nested structs.
443 DenseSet<Operation *> opVisited;
444 patterns.add<OutputOpConversion>(typeConverter, ctx, &opVisited);
445
446 patterns.add<InstanceOpConversion>(typeConverter, ctx, &convertedInstances,
447 &externModules);
448 target.addDynamicallyLegalOp<hw::OutputOp>(
449 [&](auto op) { return opVisited.contains(op->getParentOp()); });
450 target.addDynamicallyLegalOp<hw::InstanceOp>([&](hw::InstanceOp op) {
451 auto refName = op.getReferencedModuleName();
452 return externModules.contains(refName) ||
453 (llvm::none_of(op->getOperands(),
454 [](auto operand) {
455 return isStructType(operand.getType());
456 }) &&
457 llvm::none_of(op->getResultTypes(),
458 [](auto result) { return isStructType(result); }));
459 });
460
461 DenseMap<Operation *, ArrayAttr> oldArgNames, oldResNames;
462 DenseMap<Operation *, SmallVector<Location>> oldArgLocs, oldResLocs;
463 DenseMap<Operation *, hw::ModuleType> oldModTypes;
464
465 for (auto op : module.getOps<T>()) {
466 oldModTypes[op] = op.getHWModuleType();
467 oldArgNames[op] = ArrayAttr::get(module.getContext(), op.getInputNames());
468 oldResNames[op] =
469 ArrayAttr::get(module.getContext(), op.getOutputNames());
470 oldArgLocs[op] = op.getInputLocs();
471 oldResLocs[op] = op.getOutputLocs();
472 }
473
474 // Signature conversion and legalization patterns.
475 addSignatureConversion<T>(ioInfoMap, target, patterns, typeConverter);
476
477 if (failed(applyPartialConversion(module, target, std::move(patterns))))
478 return failure();
479
480 // Update the arg/res names of the module.
481 for (auto op : module.getOps<T>()) {
482 auto ioInfo = ioInfoMap[op];
483 updateModulePortNames(op, oldModTypes[op], joinChar);
484 auto newArgLocs = updateLocAttribute(ioInfo.argStructs, oldArgLocs[op]);
485 auto newResLocs = updateLocAttribute(ioInfo.resStructs, oldResLocs[op]);
486 newArgLocs.append(newResLocs.begin(), newResLocs.end());
487 op.setAllPortLocs(newArgLocs);
488 updateBlockLocations(op, ioInfo.argStructs);
489 }
490
491 // And likewise with the converted instance ops.
492 for (auto instanceOp : convertedInstances) {
493 auto targetModule =
494 cast<hw::HWModuleLike>(SymbolTable::lookupNearestSymbolFrom(
495 instanceOp, instanceOp.getReferencedModuleNameAttr()));
496
497 IOInfo ioInfo;
498 if (!ioInfoMap.contains(targetModule)) {
499 // If an extern module, then not yet processed, populate the maps.
500 setIOInfo(targetModule, ioInfo);
501 ioInfoMap[targetModule] = ioInfo;
502 oldArgNames[targetModule] =
503 ArrayAttr::get(module.getContext(), targetModule.getInputNames());
504 oldResNames[targetModule] =
505 ArrayAttr::get(module.getContext(), targetModule.getOutputNames());
506 oldArgLocs[targetModule] = targetModule.getInputLocs();
507 oldResLocs[targetModule] = targetModule.getOutputLocs();
508 } else
509 ioInfo = ioInfoMap[targetModule];
510
511 instanceOp.setInputNames(ArrayAttr::get(
512 instanceOp.getContext(),
514 instanceOp, "argNames", ioInfo.argStructs,
515 oldArgNames[targetModule].template getAsValueRange<StringAttr>(),
516 joinChar)));
517 instanceOp.setOutputNames(ArrayAttr::get(
518 instanceOp.getContext(),
520 instanceOp, "resultNames", ioInfo.resStructs,
521 oldResNames[targetModule].template getAsValueRange<StringAttr>(),
522 joinChar)));
523 }
524
525 // Break if we've only lowering a single level of structs.
526 if (!recursive)
527 break;
528 }
529 return success();
530}
531
532//===----------------------------------------------------------------------===//
533// Pass driver
534//===----------------------------------------------------------------------===//
535
536template <typename... TOps>
537static bool flattenIO(ModuleOp module, bool recursive,
538 StringSet<> &externModules, char joinChar) {
539 return (failed(flattenOpsOfType<TOps>(module, recursive, externModules,
540 joinChar)) ||
541 ...);
542}
543
544namespace {
545
546class FlattenIOPass : public circt::hw::impl::FlattenIOBase<FlattenIOPass> {
547public:
548 FlattenIOPass(bool recursiveFlag, bool flattenExternFlag, char join) {
549 recursive = recursiveFlag;
550 flattenExtern = flattenExternFlag;
551 joinChar = join;
552 }
553
554 void runOnOperation() override {
555 ModuleOp module = getOperation();
556 if (!flattenExtern) {
557 // Record the extern modules, do not flatten them.
558 for (auto m : module.getOps<hw::HWModuleExternOp>())
559 externModules.insert(m.getModuleName());
560 if (flattenIO<hw::HWModuleOp, hw::HWModuleGeneratedOp>(
561 module, recursive, externModules, joinChar))
562 signalPassFailure();
563 return;
564 }
565
567 hw::HWModuleGeneratedOp>(module, recursive, externModules,
568 joinChar))
569 signalPassFailure();
570 };
571
572private:
573 StringSet<> externModules;
574};
575} // namespace
576
577//===----------------------------------------------------------------------===//
578// Pass initialization
579//===----------------------------------------------------------------------===//
580
581std::unique_ptr<Pass> circt::hw::createFlattenIOPass(bool recursiveFlag,
582 bool flattenExternFlag,
583 char joinChar) {
584 return std::make_unique<FlattenIOPass>(recursiveFlag, flattenExternFlag,
585 joinChar);
586}
static LogicalResult compareTypes(Location loc, TypeRange rangeA, TypeRange rangeB)
Definition FSMOps.cpp:118
static DenseMap< Operation *, IOInfo > populateIOInfoMap(mlir::ModuleOp module)
static DenseMap< Operation *, IOTypes > populateIOMap(mlir::ModuleOp module)
static llvm::SmallVector< Location > updateLocAttribute(DenseMap< unsigned, hw::StructType > &structMap, SmallVectorImpl< Location > &oldLocs)
static bool isLegalModLikeOp(hw::HWModuleLike moduleLikeOp)
Definition FlattenIO.cpp:34
static llvm::SmallVector< Attribute > updateNameAttribute(ModTy op, StringRef attrName, DenseMap< unsigned, hw::StructType > &structMap, T oldNames, char joinChar)
static void updateBlockLocations(hw::HWModuleLike op, DenseMap< unsigned, hw::StructType > &structMap)
The conversion framework seems to throw away block argument locations.
static bool hasUnconvertedOps(mlir::ModuleOp module)
static llvm::SmallVector< Type > getInnerTypes(hw::StructType t)
Definition FlattenIO.cpp:39
static void setIOInfo(hw::HWModuleLike op, IOInfo &ioInfo)
static LogicalResult flattenOpsOfType(ModuleOp module, bool recursive, StringSet<> &externModules, char joinChar)
static void addSignatureConversion(DenseMap< Operation *, IOInfo > &ioMap, ConversionTarget &target, RewritePatternSet &patterns, FlattenIOTypeConverter &typeConverter)
static bool isStructType(Type type)
Definition FlattenIO.cpp:25
static void updateModulePortNames(ModTy op, hw::ModuleType oldModType, char joinChar)
static hw::StructType getStructType(Type type)
Definition FlattenIO.cpp:29
static bool flattenIO(ModuleOp module, bool recursive, StringSet<> &externModules, char joinChar)
static SmallVector< Value > flattenValues(ArrayRef< ValueRange > values)
Flatten the given value ranges into a single vector of values.
std::unique_ptr< mlir::Pass > createFlattenIOPass(bool recursiveFlag=true, bool flattenExternFlag=false, char joinChar='.')
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1