CIRCT  20.0.0git
ESILowerPhysical.cpp
Go to the documentation of this file.
1 //===- ESILowerPhysical.cpp - Lower ESI to physical -------------*- 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 //
9 // Lower ESI to ESI "physical level" ops conversions and pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "../PassDetails.h"
14 
16 #include "circt/Dialect/HW/HWOps.h"
18 #include "circt/Support/LLVM.h"
19 
20 #include "mlir/Transforms/DialectConversion.h"
21 
22 namespace circt {
23 namespace esi {
24 #define GEN_PASS_DEF_LOWERESITOPHYSICAL
25 #include "circt/Dialect/ESI/ESIPasses.h.inc"
26 } // namespace esi
27 } // namespace circt
28 
29 using namespace circt;
30 using namespace circt::esi;
31 using namespace circt::esi::detail;
32 using namespace circt::hw;
33 
34 namespace {
35 /// Lower `ChannelBufferOp`s, breaking out the various options. For now, just
36 /// replace with the specified number of pipeline stages (since that's the only
37 /// option).
38 struct ChannelBufferLowering : public OpConversionPattern<ChannelBufferOp> {
39 public:
40  using OpConversionPattern::OpConversionPattern;
41 
42  LogicalResult
43  matchAndRewrite(ChannelBufferOp buffer, OpAdaptor adaptor,
44  ConversionPatternRewriter &rewriter) const final;
45 };
46 } // anonymous namespace
47 
48 LogicalResult ChannelBufferLowering::matchAndRewrite(
49  ChannelBufferOp buffer, OpAdaptor adaptor,
50  ConversionPatternRewriter &rewriter) const {
51  auto loc = buffer.getLoc();
52 
53  auto type = buffer.getType();
54 
55  // Expand 'abstract' buffer into 'physical' stages.
56  auto stages = buffer.getStagesAttr();
57  uint64_t numStages = 1;
58  if (stages) {
59  // Guaranteed positive by the parser.
60  numStages = stages.getValue().getLimitedValue();
61  }
62  Value input = buffer.getInput();
63  StringAttr bufferName = buffer.getNameAttr();
64  for (uint64_t i = 0; i < numStages; ++i) {
65  // Create the stages, connecting them up as we build.
66  auto stage = rewriter.create<PipelineStageOp>(loc, type, buffer.getClk(),
67  buffer.getRst(), input);
68  if (bufferName) {
69  SmallString<64> stageName(
70  {bufferName.getValue(), "_stage", std::to_string(i)});
71  stage->setAttr("name", StringAttr::get(rewriter.getContext(), stageName));
72  }
73  input = stage;
74  }
75 
76  // Replace the buffer.
77  rewriter.replaceOp(buffer, input);
78  return success();
79 }
80 
81 namespace {
82 /// Lower pure modules into hw.modules.
83 struct PureModuleLowering : public OpConversionPattern<ESIPureModuleOp> {
84 public:
85  using OpConversionPattern::OpConversionPattern;
86 
87  LogicalResult
88  matchAndRewrite(ESIPureModuleOp pureMod, OpAdaptor adaptor,
89  ConversionPatternRewriter &rewriter) const final;
90 };
91 } // anonymous namespace
92 
93 LogicalResult
94 PureModuleLowering::matchAndRewrite(ESIPureModuleOp pureMod, OpAdaptor adaptor,
95  ConversionPatternRewriter &rewriter) const {
96  auto loc = pureMod.getLoc();
97  Block *body = &pureMod.getBody().front();
98 
99  // Track existing names (so we can de-dup) and get op result when we want to
100  // replace it with the block args.
101  DenseMap<StringAttr, ESIPureModuleInputOp> inputPortNames;
102  // Build the port list for `hw.module` construction.
103  SmallVector<hw::PortInfo> ports;
104  // List the input and output ops.
105  SmallVector<ESIPureModuleInputOp> inputs;
106  SmallVector<ESIPureModuleOutputOp> outputs;
107  SmallVector<Attribute> params;
108 
109  for (Operation &op : llvm::make_early_inc_range(body->getOperations())) {
110  if (auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
111  // If we already have an input port of the same name, replace the result
112  // value with the previous one. Checking that the types match is done in
113  // the pure module verifier.
114  auto existingPort = inputPortNames.find(port.getNameAttr());
115  if (existingPort != inputPortNames.end()) {
116  rewriter.replaceAllUsesWith(port.getResult(),
117  existingPort->getSecond().getResult());
118  rewriter.eraseOp(port);
119  continue;
120  }
121  // Normal port construction.
122  ports.push_back(
123  hw::PortInfo{{port.getNameAttr(), port.getResult().getType(),
125  inputs.size(),
126  {},
127  port.getLoc()});
128  inputs.push_back(port);
129  } else if (auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
130  ports.push_back(
131  hw::PortInfo{{port.getNameAttr(), port.getValue().getType(),
133  outputs.size(),
134  {},
135  port.getLoc()});
136  outputs.push_back(port);
137  } else if (auto param = dyn_cast<ESIPureModuleParamOp>(op)) {
138  params.push_back(
139  ParamDeclAttr::get(param.getNameAttr(), param.getType()));
140  rewriter.eraseOp(param);
141  }
142  }
143 
144  // Create the replacement `hw.module`.
145  auto hwMod = rewriter.create<hw::HWModuleOp>(
146  loc, pureMod.getNameAttr(), ports, ArrayAttr::get(getContext(), params));
147  hwMod->setDialectAttrs(pureMod->getDialectAttrs());
148  rewriter.eraseBlock(hwMod.getBodyBlock());
149  rewriter.inlineRegionBefore(*body->getParent(), hwMod.getBodyRegion(),
150  hwMod.getBodyRegion().end());
151  body = hwMod.getBodyBlock();
152 
153  // Re-wire the inputs and erase them.
154  for (auto input : inputs) {
155  BlockArgument newArg;
156  rewriter.modifyOpInPlace(hwMod, [&]() {
157  newArg = body->addArgument(input.getResult().getType(), input.getLoc());
158  });
159  rewriter.replaceAllUsesWith(input.getResult(), newArg);
160  rewriter.eraseOp(input);
161  }
162 
163  // Assemble the output values.
164  SmallVector<Value> hwOutputOperands;
165  for (auto output : outputs) {
166  hwOutputOperands.push_back(output.getValue());
167  rewriter.eraseOp(output);
168  }
169  rewriter.setInsertionPointToEnd(body);
170  rewriter.create<hw::OutputOp>(pureMod.getLoc(), hwOutputOperands);
171 
172  // Erase the original op.
173  rewriter.eraseOp(pureMod);
174  return success();
175 }
176 
177 namespace {
178 /// Run all the physical lowerings.
179 struct ESIToPhysicalPass
180  : public circt::esi::impl::LowerESIToPhysicalBase<ESIToPhysicalPass> {
181  void runOnOperation() override;
182 };
183 } // anonymous namespace
184 
185 void ESIToPhysicalPass::runOnOperation() {
186  // Set up a conversion and give it a set of laws.
187  ConversionTarget target(getContext());
188  target.markUnknownOpDynamicallyLegal([](Operation *) { return true; });
189  target.addIllegalOp<ChannelBufferOp>();
190  target.addIllegalOp<ESIPureModuleOp>();
191 
192  // Add all the conversion patterns.
193  RewritePatternSet patterns(&getContext());
194  patterns.insert<ChannelBufferLowering>(&getContext());
195  patterns.insert<PureModuleLowering>(&getContext());
196 
197  // Run the conversion.
198  if (failed(
199  applyPartialConversion(getOperation(), target, std::move(patterns))))
200  signalPassFailure();
201 }
202 
203 std::unique_ptr<OperationPass<ModuleOp>>
205  return std::make_unique<ESIToPhysicalPass>();
206 }
@ Input
Definition: HW.h:35
@ Output
Definition: HW.h:35
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
std::unique_ptr< OperationPass< ModuleOp > > createESIPhysicalLoweringPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: esi.py:1