CIRCT  19.0.0git
IbisContainerize.cpp
Go to the documentation of this file.
1 //===- IbisContainerize.cpp - Implementation of containerizing ------------===//
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 
17 
19 #include "circt/Support/SymCache.h"
20 #include "mlir/Transforms/DialectConversion.h"
21 
22 namespace circt {
23 namespace ibis {
24 #define GEN_PASS_DEF_IBISCONTAINERIZE
25 #include "circt/Dialect/Ibis/IbisPasses.h.inc"
26 } // namespace ibis
27 } // namespace circt
28 
29 using namespace circt;
30 using namespace ibis;
31 
32 namespace {
33 
34 struct OutlineContainerPattern : public OpConversionPattern<ContainerOp> {
35  OutlineContainerPattern(MLIRContext *context, Namespace &ns)
36  : OpConversionPattern<ContainerOp>(context), ns(ns) {}
37 
38  using OpAdaptor = typename OpConversionPattern<ContainerOp>::OpAdaptor;
39 
40  LogicalResult
41  matchAndRewrite(ContainerOp op, OpAdaptor adaptor,
42  ConversionPatternRewriter &rewriter) const override {
43  // Outline the container into the module scope, by prefixing it with the
44  // parent class name.
45  auto parentClass =
46  dyn_cast_or_null<ClassOp>(op.getOperation()->getParentOp());
47  assert(parentClass && "This pattern should never be called on a container"
48  "that is not nested within a class.");
49  auto design = parentClass.getParentOp<DesignOp>();
50  assert(design && "Parent class should be nested within a design.");
51 
52  rewriter.setInsertionPoint(parentClass);
53  StringAttr newContainerName = rewriter.getStringAttr(
54  ns.newName(parentClass.getInnerNameAttr().strref() + "_" +
55  op.getInnerNameAttr().strref()));
56  auto newContainer = rewriter.create<ContainerOp>(
57  op.getLoc(), newContainerName, /*isTopLevel=*/false);
58 
59  rewriter.mergeBlocks(op.getBodyBlock(), newContainer.getBodyBlock(), {});
60 
61  // Rename the ibis.this operation to refer to the proper op.
62  auto thisOp =
63  cast<ThisOp>(cast<ScopeOpInterface>(*newContainer.getOperation())
64  .getThis()
65  .getDefiningOp());
66  rewriter.setInsertionPoint(thisOp);
67  rewriter.replaceOpWithNewOp<ThisOp>(thisOp, design.getSymNameAttr(),
68  newContainer.getInnerSymAttr());
69 
70  // Create a container instance op in the parent class.
71  rewriter.setInsertionPoint(op);
72  rewriter.create<ContainerInstanceOp>(
73  parentClass.getLoc(), hw::InnerSymAttr::get(newContainerName),
74  newContainer.getInnerRef());
75  rewriter.eraseOp(op);
76  return success();
77  }
78 
79  Namespace &ns;
80 };
81 
82 struct ClassToContainerPattern : public OpConversionPattern<ClassOp> {
84 
85  LogicalResult
86  matchAndRewrite(ClassOp op, OpAdaptor adaptor,
87  ConversionPatternRewriter &rewriter) const override {
88  // Replace the class by a container of the same name.
89  auto newContainer =
90  rewriter.create<ContainerOp>(op.getLoc(), op.getInnerSymAttr(),
91  /*topLevel*/ false, op.getNameAttr());
92  rewriter.mergeBlocks(op.getBodyBlock(), newContainer.getBodyBlock(), {});
93  rewriter.eraseOp(op);
94  return success();
95  }
96 };
97 
98 struct InstanceToContainerInstancePattern
99  : public OpConversionPattern<InstanceOp> {
101 
102  LogicalResult
103  matchAndRewrite(InstanceOp op, OpAdaptor adaptor,
104  ConversionPatternRewriter &rewriter) const override {
105  // Replace the instance by a container instance of the same name.
106  rewriter.replaceOpWithNewOp<ContainerInstanceOp>(op, op.getInnerSym(),
107  op.getTargetNameAttr());
108  return success();
109  }
110 };
111 
112 /// Run all the physical lowerings.
113 struct ContainerizePass
114  : public circt::ibis::impl::IbisContainerizeBase<ContainerizePass> {
115  void runOnOperation() override;
116 
117 private:
118  // Outlines containers nested within classes into the module scope.
119  LogicalResult outlineContainers();
120 
121  // Converts classes to containers.
122  LogicalResult containerizeClasses();
123 };
124 } // anonymous namespace
125 
126 LogicalResult ContainerizePass::outlineContainers() {
127  auto *context = &getContext();
128  ConversionTarget target(*context);
129  target.addLegalDialect<IbisDialect>();
130  target.addDynamicallyLegalOp<ContainerOp>(
131  [&](auto *op) { return !isa<ibis::ClassOp>(op->getParentOp()); });
132  RewritePatternSet patterns(context);
133 
134  // Setup a namespace to ensure that the new container names are unique.
135  // Grab existing names from the InnerSymbolTable of the top-level design op.
136  SymbolCache symCache;
138  getOperation(), [&](StringAttr name, const hw::InnerSymTarget &target) {
139  symCache.addDefinition(name, target.getOp());
140  return success();
141  });
142 
143  Namespace ns;
144  symCache.addDefinitions(getOperation());
145  ns.add(symCache);
146  patterns.insert<OutlineContainerPattern>(context, ns);
147  return applyPartialConversion(getOperation(), target, std::move(patterns));
148 }
149 
150 LogicalResult ContainerizePass::containerizeClasses() {
151  auto *context = &getContext();
152  ConversionTarget target(*context);
153  target.addLegalDialect<IbisDialect>();
154  target.addIllegalOp<ClassOp, InstanceOp>();
155  RewritePatternSet patterns(context);
156  patterns.insert<ClassToContainerPattern, InstanceToContainerInstancePattern>(
157  context);
158  return applyPartialConversion(getOperation(), target, std::move(patterns));
159 }
160 
161 void ContainerizePass::runOnOperation() {
162  if (failed(outlineContainers()) || failed(containerizeClasses()))
163  signalPassFailure();
164 }
165 
166 std::unique_ptr<Pass> circt::ibis::createContainerizePass() {
167  return std::make_unique<ContainerizePass>();
168 }
assert(baseType &&"element must be base type")
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition: Namespace.h:30
void add(mlir::ModuleOp module)
Definition: Namespace.h:46
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:72
void addDefinitions(mlir::Operation *top)
Populate the symbol cache with all symbol-defining operations within the 'top' operation.
Definition: SymCache.cpp:23
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
Definition: SymCache.h:85
void addDefinition(mlir::Attribute key, mlir::Operation *op) override
In the building phase, add symbols.
Definition: SymCache.h:88
static RetTy walkSymbols(Operation *op, FuncTy &&callback)
Walk the given IST operation and invoke the callback for all encountered inner symbols.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
std::unique_ptr< mlir::Pass > createContainerizePass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21