CIRCT  20.0.0git
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 
15 namespace circt {
16 namespace hw {
17 #define GEN_PASS_DEF_FLATTENIO
18 #include "circt/Dialect/HW/Passes.h.inc"
19 } // namespace hw
20 } // namespace circt
21 
22 using namespace mlir;
23 using namespace circt;
24 
25 static bool isStructType(Type type) {
26  return isa<hw::StructType>(hw::getCanonicalType(type));
27 }
28 
29 static 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.
34 static bool isLegalModLikeOp(hw::HWModuleLike moduleLikeOp) {
35  return llvm::none_of(moduleLikeOp.getHWModuleType().getPortTypes(),
36  isStructType);
37 }
38 
39 static 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 
47 namespace {
48 
49 /// Flatten the given value ranges into a single vector of values.
50 static 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.
58 struct 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 
111 struct 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 
186 using IOTypes = std::pair<TypeRange, TypeRange>;
187 
188 struct 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 
196 class FlattenIOTypeConverter : public TypeConverter {
197 public:
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 };
233 
234 } // namespace
235 
236 template <typename... TOp>
237 static void addSignatureConversion(DenseMap<Operation *, IOInfo> &ioMap,
238  ConversionTarget &target,
239  RewritePatternSet &patterns,
240  FlattenIOTypeConverter &typeConverter) {
241  (hw::populateHWModuleLikeTypeConversionPattern(TOp::getOperationName(),
242  patterns, typeConverter),
243  ...);
244 
245  // Legality is defined by a module having been processed once. This is due to
246  // that a pattern cannot be applied multiple times (a 'pattern was already
247  // applied' error - a case that would occur for nested structs). Additionally,
248  // if a pattern could be applied multiple times, this would complicate
249  // updating arg/res names.
250 
251  // Instead, we define legality as when a module has had a modification to its
252  // top-level i/o. This ensures that only a single level of structs are
253  // processed during signature conversion, which then allows us to use the
254  // signature conversion in a recursive manner.
255  target.addDynamicallyLegalOp<TOp...>([&](hw::HWModuleLike moduleLikeOp) {
256  if (isLegalModLikeOp(moduleLikeOp))
257  return true;
258 
259  // This op is involved in conversion. Check if the signature has changed.
260  auto ioInfoIt = ioMap.find(moduleLikeOp);
261  if (ioInfoIt == ioMap.end()) {
262  // Op wasn't primed in the map. Do the safe thing, assume
263  // that it's not considered in this pass, and mark it as legal
264  return true;
265  }
266  auto ioInfo = ioInfoIt->second;
267 
268  auto compareTypes = [&](TypeRange oldTypes, TypeRange newTypes) {
269  return llvm::any_of(llvm::zip(oldTypes, newTypes), [&](auto typePair) {
270  auto oldType = std::get<0>(typePair);
271  auto newType = std::get<1>(typePair);
272  return oldType != newType;
273  });
274  };
275  auto mtype = moduleLikeOp.getHWModuleType();
276  if (compareTypes(mtype.getOutputTypes(), ioInfo.resTypes) ||
277  compareTypes(mtype.getInputTypes(), ioInfo.argTypes))
278  return true;
279 
280  // We're pre-conversion for an op that was primed in the map - it will
281  // always be illegal since it has to-be-converted struct types at its I/O.
282  return false;
283  });
284 }
285 
286 template <typename T>
287 static bool hasUnconvertedOps(mlir::ModuleOp module) {
288  return llvm::any_of(module.getBody()->getOps<T>(),
289  [](T op) { return !isLegalModLikeOp(op); });
290 }
291 
292 template <typename T>
293 static DenseMap<Operation *, IOTypes> populateIOMap(mlir::ModuleOp module) {
294  DenseMap<Operation *, IOTypes> ioMap;
295  for (auto op : module.getOps<T>())
296  ioMap[op] = {op.getArgumentTypes(), op.getResultTypes()};
297  return ioMap;
298 }
299 
300 template <typename ModTy, typename T>
301 static llvm::SmallVector<Attribute>
302 updateNameAttribute(ModTy op, StringRef attrName,
303  DenseMap<unsigned, hw::StructType> &structMap, T oldNames,
304  char joinChar) {
305  llvm::SmallVector<Attribute> newNames;
306  for (auto [i, oldName] : llvm::enumerate(oldNames)) {
307  // Was this arg/res index a struct?
308  auto it = structMap.find(i);
309  if (it == structMap.end()) {
310  // No, keep old name.
311  newNames.push_back(StringAttr::get(op->getContext(), oldName));
312  continue;
313  }
314 
315  // Yes - create new names from the struct fields and the old name at the
316  // index.
317  auto structType = it->second;
318  for (auto field : structType.getElements())
319  newNames.push_back(StringAttr::get(
320  op->getContext(), oldName + Twine(joinChar) + field.name.str()));
321  }
322  return newNames;
323 }
324 
325 template <typename ModTy>
326 static void updateModulePortNames(ModTy op, hw::ModuleType oldModType,
327  char joinChar) {
328  // Module arg and result port names may not be ordered. So we cannot reuse
329  // updateNameAttribute. The arg and result order must be preserved.
330  SmallVector<Attribute> newNames;
331  SmallVector<hw::ModulePort> oldPorts(oldModType.getPorts().begin(),
332  oldModType.getPorts().end());
333  for (auto oldPort : oldPorts) {
334  auto oldName = oldPort.name;
335  if (auto structType = getStructType(oldPort.type)) {
336  for (auto field : structType.getElements()) {
337  newNames.push_back(StringAttr::get(
338  op->getContext(),
339  oldName.getValue() + Twine(joinChar) + field.name.str()));
340  }
341  } else
342  newNames.push_back(oldName);
343  }
344  op.setAllPortNames(newNames);
345 }
346 
347 static llvm::SmallVector<Location>
348 updateLocAttribute(DenseMap<unsigned, hw::StructType> &structMap,
349  SmallVectorImpl<Location> &oldLocs) {
350  llvm::SmallVector<Location> newLocs;
351  for (auto [i, oldLoc] : llvm::enumerate(oldLocs)) {
352  // Was this arg/res index a struct?
353  auto it = structMap.find(i);
354  if (it == structMap.end()) {
355  // No, keep old name.
356  newLocs.push_back(oldLoc);
357  continue;
358  }
359 
360  auto structType = it->second;
361  for (size_t i = 0, e = structType.getElements().size(); i < e; ++i)
362  newLocs.push_back(oldLoc);
363  }
364  return newLocs;
365 }
366 
367 /// The conversion framework seems to throw away block argument locations. We
368 /// use this function to copy the location from the original argument to the
369 /// set of flattened arguments.
370 static void
371 updateBlockLocations(hw::HWModuleLike op,
372  DenseMap<unsigned, hw::StructType> &structMap) {
373  auto locs = op.getInputLocs();
374  if (locs.empty() || op.getModuleBody().empty())
375  return;
376  for (auto [arg, loc] : llvm::zip(op.getBodyBlock()->getArguments(), locs))
377  arg.setLoc(loc);
378 }
379 
380 static void setIOInfo(hw::HWModuleLike op, IOInfo &ioInfo) {
381  ioInfo.argTypes = op.getInputTypes();
382  ioInfo.resTypes = op.getOutputTypes();
383  for (auto [i, arg] : llvm::enumerate(ioInfo.argTypes)) {
384  if (auto structType = getStructType(arg))
385  ioInfo.argStructs[i] = structType;
386  }
387  for (auto [i, res] : llvm::enumerate(ioInfo.resTypes)) {
388  if (auto structType = getStructType(res))
389  ioInfo.resStructs[i] = structType;
390  }
391 }
392 
393 template <typename T>
394 static DenseMap<Operation *, IOInfo> populateIOInfoMap(mlir::ModuleOp module) {
395  DenseMap<Operation *, IOInfo> ioInfoMap;
396  for (auto op : module.getOps<T>()) {
397  IOInfo ioInfo;
398  setIOInfo(op, ioInfo);
399  ioInfoMap[op] = ioInfo;
400  }
401  return ioInfoMap;
402 }
403 
404 template <typename T>
405 static LogicalResult flattenOpsOfType(ModuleOp module, bool recursive,
406  StringSet<> &externModules,
407  char joinChar) {
408  auto *ctx = module.getContext();
409  FlattenIOTypeConverter typeConverter;
410 
411  // Recursively (in case of nested structs) lower the module. We do this one
412  // conversion at a time to allow for updating the arg/res names of the
413  // module in between flattening each level of structs.
414  while (hasUnconvertedOps<T>(module)) {
415  ConversionTarget target(*ctx);
416  RewritePatternSet patterns(ctx);
417  target.addLegalDialect<hw::HWDialect>();
418 
419  // Record any struct types at the module signature. This will be used
420  // post-conversion to update the argument and result names.
421  auto ioInfoMap = populateIOInfoMap<T>(module);
422 
423  // Record the instances that were converted. We keep these around since we
424  // need to update their arg/res attribute names after the modules themselves
425  // have been updated.
426  llvm::DenseSet<hw::InstanceOp> convertedInstances;
427 
428  // Argument conversion for output ops. Similarly to the signature
429  // conversion, legality is based on the op having been visited once, due to
430  // the possibility of nested structs.
431  DenseSet<Operation *> opVisited;
432  patterns.add<OutputOpConversion>(typeConverter, ctx, &opVisited);
433 
434  patterns.add<InstanceOpConversion>(typeConverter, ctx, &convertedInstances,
435  &externModules);
436  target.addDynamicallyLegalOp<hw::OutputOp>(
437  [&](auto op) { return opVisited.contains(op->getParentOp()); });
438  target.addDynamicallyLegalOp<hw::InstanceOp>([&](hw::InstanceOp op) {
439  auto refName = op.getReferencedModuleName();
440  return externModules.contains(refName) ||
441  (llvm::none_of(op->getOperands(),
442  [](auto operand) {
443  return isStructType(operand.getType());
444  }) &&
445  llvm::none_of(op->getResultTypes(),
446  [](auto result) { return isStructType(result); }));
447  });
448 
449  DenseMap<Operation *, ArrayAttr> oldArgNames, oldResNames;
450  DenseMap<Operation *, SmallVector<Location>> oldArgLocs, oldResLocs;
451  DenseMap<Operation *, hw::ModuleType> oldModTypes;
452 
453  for (auto op : module.getOps<T>()) {
454  oldModTypes[op] = op.getHWModuleType();
455  oldArgNames[op] = ArrayAttr::get(module.getContext(), op.getInputNames());
456  oldResNames[op] =
457  ArrayAttr::get(module.getContext(), op.getOutputNames());
458  oldArgLocs[op] = op.getInputLocs();
459  oldResLocs[op] = op.getOutputLocs();
460  }
461 
462  // Signature conversion and legalization patterns.
463  addSignatureConversion<T>(ioInfoMap, target, patterns, typeConverter);
464 
465  if (failed(applyPartialConversion(module, target, std::move(patterns))))
466  return failure();
467 
468  // Update the arg/res names of the module.
469  for (auto op : module.getOps<T>()) {
470  auto ioInfo = ioInfoMap[op];
471  updateModulePortNames(op, oldModTypes[op], joinChar);
472  auto newArgLocs = updateLocAttribute(ioInfo.argStructs, oldArgLocs[op]);
473  auto newResLocs = updateLocAttribute(ioInfo.resStructs, oldResLocs[op]);
474  newArgLocs.append(newResLocs.begin(), newResLocs.end());
475  op.setAllPortLocs(newArgLocs);
476  updateBlockLocations(op, ioInfo.argStructs);
477  }
478 
479  // And likewise with the converted instance ops.
480  for (auto instanceOp : convertedInstances) {
481  auto targetModule =
482  cast<hw::HWModuleLike>(SymbolTable::lookupNearestSymbolFrom(
483  instanceOp, instanceOp.getReferencedModuleNameAttr()));
484 
485  IOInfo ioInfo;
486  if (!ioInfoMap.contains(targetModule)) {
487  // If an extern module, then not yet processed, populate the maps.
488  setIOInfo(targetModule, ioInfo);
489  ioInfoMap[targetModule] = ioInfo;
490  oldArgNames[targetModule] =
491  ArrayAttr::get(module.getContext(), targetModule.getInputNames());
492  oldResNames[targetModule] =
493  ArrayAttr::get(module.getContext(), targetModule.getOutputNames());
494  oldArgLocs[targetModule] = targetModule.getInputLocs();
495  oldResLocs[targetModule] = targetModule.getOutputLocs();
496  } else
497  ioInfo = ioInfoMap[targetModule];
498 
499  instanceOp.setInputNames(ArrayAttr::get(
500  instanceOp.getContext(),
502  instanceOp, "argNames", ioInfo.argStructs,
503  oldArgNames[targetModule].template getAsValueRange<StringAttr>(),
504  joinChar)));
505  instanceOp.setOutputNames(ArrayAttr::get(
506  instanceOp.getContext(),
508  instanceOp, "resultNames", ioInfo.resStructs,
509  oldResNames[targetModule].template getAsValueRange<StringAttr>(),
510  joinChar)));
511  }
512 
513  // Break if we've only lowering a single level of structs.
514  if (!recursive)
515  break;
516  }
517  return success();
518 }
519 
520 //===----------------------------------------------------------------------===//
521 // Pass driver
522 //===----------------------------------------------------------------------===//
523 
524 template <typename... TOps>
525 static bool flattenIO(ModuleOp module, bool recursive,
526  StringSet<> &externModules, char joinChar) {
527  return (failed(flattenOpsOfType<TOps>(module, recursive, externModules,
528  joinChar)) ||
529  ...);
530 }
531 
532 namespace {
533 
534 class FlattenIOPass : public circt::hw::impl::FlattenIOBase<FlattenIOPass> {
535 public:
536  FlattenIOPass(bool recursiveFlag, bool flattenExternFlag, char join) {
537  recursive = recursiveFlag;
538  flattenExtern = flattenExternFlag;
539  joinChar = join;
540  }
541 
542  void runOnOperation() override {
543  ModuleOp module = getOperation();
544  if (!flattenExtern) {
545  // Record the extern modules, do not flatten them.
546  for (auto m : module.getOps<hw::HWModuleExternOp>())
547  externModules.insert(m.getModuleName());
548  if (flattenIO<hw::HWModuleOp, hw::HWModuleGeneratedOp>(
549  module, recursive, externModules, joinChar))
550  signalPassFailure();
551  return;
552  }
553 
555  hw::HWModuleGeneratedOp>(module, recursive, externModules,
556  joinChar))
557  signalPassFailure();
558  };
559 
560 private:
561  StringSet<> externModules;
562 };
563 } // namespace
564 
565 //===----------------------------------------------------------------------===//
566 // Pass initialization
567 //===----------------------------------------------------------------------===//
568 
569 std::unique_ptr<Pass> circt::hw::createFlattenIOPass(bool recursiveFlag,
570  bool flattenExternFlag,
571  char joinChar) {
572  return std::make_unique<FlattenIOPass>(recursiveFlag, flattenExternFlag,
573  joinChar);
574 }
static LogicalResult compareTypes(Location loc, TypeRange rangeA, TypeRange rangeB)
Definition: FSMOps.cpp:118
static llvm::SmallVector< Type > getInnerTypes(hw::StructType t)
Definition: FlattenIO.cpp:39
static llvm::SmallVector< Location > updateLocAttribute(DenseMap< unsigned, hw::StructType > &structMap, SmallVectorImpl< Location > &oldLocs)
Definition: FlattenIO.cpp:348
static bool isLegalModLikeOp(hw::HWModuleLike moduleLikeOp)
Definition: FlattenIO.cpp:34
static void updateBlockLocations(hw::HWModuleLike op, DenseMap< unsigned, hw::StructType > &structMap)
The conversion framework seems to throw away block argument locations.
Definition: FlattenIO.cpp:371
static bool hasUnconvertedOps(mlir::ModuleOp module)
Definition: FlattenIO.cpp:287
static llvm::SmallVector< Attribute > updateNameAttribute(ModTy op, StringRef attrName, DenseMap< unsigned, hw::StructType > &structMap, T oldNames, char joinChar)
Definition: FlattenIO.cpp:302
static void setIOInfo(hw::HWModuleLike op, IOInfo &ioInfo)
Definition: FlattenIO.cpp:380
static LogicalResult flattenOpsOfType(ModuleOp module, bool recursive, StringSet<> &externModules, char joinChar)
Definition: FlattenIO.cpp:405
static DenseMap< Operation *, IOTypes > populateIOMap(mlir::ModuleOp module)
Definition: FlattenIO.cpp:293
static void addSignatureConversion(DenseMap< Operation *, IOInfo > &ioMap, ConversionTarget &target, RewritePatternSet &patterns, FlattenIOTypeConverter &typeConverter)
Definition: FlattenIO.cpp:237
static bool isStructType(Type type)
Definition: FlattenIO.cpp:25
static void updateModulePortNames(ModTy op, hw::ModuleType oldModType, char joinChar)
Definition: FlattenIO.cpp:326
static hw::StructType getStructType(Type type)
Definition: FlattenIO.cpp:29
static bool flattenIO(ModuleOp module, bool recursive, StringSet<> &externModules, char joinChar)
Definition: FlattenIO.cpp:525
static DenseMap< Operation *, IOInfo > populateIOInfoMap(mlir::ModuleOp module)
Definition: FlattenIO.cpp:394
static SmallVector< Value > flattenValues(ArrayRef< ValueRange > values)
Flatten the given value ranges into a single vector of values.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
mlir::Type innerType(mlir::Type type)
Definition: ESITypes.cpp:184
void populateHWModuleLikeTypeConversionPattern(StringRef moduleLikeOpName, RewritePatternSet &patterns, TypeConverter &converter)
mlir::Type getCanonicalType(mlir::Type type)
Definition: HWTypes.cpp:49
std::unique_ptr< mlir::Pass > createFlattenIOPass(bool recursiveFlag=true, bool flattenExternFlag=false, char joinChar='.')
Definition: FlattenIO.cpp:569
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: hw.py:1