CIRCT  19.0.0git
ESILowerBundles.cpp
Go to the documentation of this file.
1 //===- ESILowerBundles.cpp - Lower ESI bundles 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 
9 #include "../PassDetails.h"
10 
12 #include "circt/Dialect/HW/HWOps.h"
15 #include "circt/Support/LLVM.h"
16 #include "circt/Support/SymCache.h"
17 
18 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
19 
20 using namespace circt;
21 using namespace circt::esi;
22 using namespace circt::esi::detail;
23 using namespace circt::hw;
24 
25 namespace {
26 
27 /// Lower channel bundles into the constituent channels. The workhorse of this
28 /// pass. Works by adding channel ports, using [un]pack operations to recreate
29 /// the original value. (Pretty standard in MLIR for type conversions.) The new
30 /// [un]pack operations get lowered away later on.
31 class BundlePort : public PortConversion {
32 public:
33  BundlePort(PortConverterImpl &converter, hw::PortInfo origPort)
34  : PortConversion(converter, origPort) {}
35 
36 protected:
37  // Modifies the instance signals.
38  void mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
39  SmallVectorImpl<Value> &newOperands,
40  ArrayRef<Backedge> newResults) override;
41  void mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
42  SmallVectorImpl<Value> &newOperands,
43  ArrayRef<Backedge> newResults) override;
44 
45  // Modifies the module ports.
46  void buildInputSignals() override;
47  void buildOutputSignals() override;
48 
49 private:
50  SmallVector<hw::PortInfo, 4> newInputChannels;
51  SmallVector<hw::PortInfo, 4> newOutputChannels;
52 };
53 
54 class ESIBundleConversionBuilder : public PortConversionBuilder {
55 public:
57  FailureOr<std::unique_ptr<PortConversion>> build(hw::PortInfo port) override {
58  return llvm::TypeSwitch<Type, FailureOr<std::unique_ptr<PortConversion>>>(
59  port.type)
60  .Case([&](esi::ChannelBundleType)
62  return {std::make_unique<BundlePort>(converter, port)};
63  })
64  .Default([&](auto) { return PortConversionBuilder::build(port); });
65  }
66 };
67 } // namespace
68 
69 /// When replacing an instance with an input bundle, we must unpack the
70 /// individual channels and feed/consume them into/from the new instance.
71 void BundlePort::mapInputSignals(OpBuilder &b, Operation *inst, Value,
72  SmallVectorImpl<Value> &newOperands,
73  ArrayRef<Backedge> newResults) {
74  // Assemble the operands/result types and build the op.
75  SmallVector<Value, 4> fromChannels(
76  llvm::map_range(newOutputChannels, [&](hw::PortInfo port) {
77  return newResults[port.argNum];
78  }));
79  SmallVector<Type, 5> toChannelTypes(llvm::map_range(
80  newInputChannels, [](hw::PortInfo port) { return port.type; }));
81  auto unpack = b.create<UnpackBundleOp>(
82  origPort.loc,
83  /*bundle=*/inst->getOperand(origPort.argNum), fromChannels);
84 
85  // Connect the new instance inputs to the results of the unpack.
86  for (auto [idx, inPort] : llvm::enumerate(newInputChannels))
87  newOperands[inPort.argNum] = unpack.getResult(idx);
88 }
89 
90 /// When replacing an instance with an output bundle, we must pack the
91 /// individual channels in a bundle to recreate the original Value.
92 void BundlePort::mapOutputSignals(OpBuilder &b, Operation *inst, Value,
93  SmallVectorImpl<Value> &newOperands,
94  ArrayRef<Backedge> newResults) {
95  // Assemble the operands/result types and build the op.
96  SmallVector<Value, 4> toChannels(
97  llvm::map_range(newOutputChannels, [&](hw::PortInfo port) {
98  return newResults[port.argNum];
99  }));
100  SmallVector<Type, 5> fromChannelTypes(llvm::map_range(
101  newInputChannels, [](hw::PortInfo port) { return port.type; }));
102  auto pack = b.create<PackBundleOp>(
103  origPort.loc, cast<ChannelBundleType>(origPort.type), toChannels);
104 
105  // Feed the fromChannels into the new instance.
106  for (auto [idx, inPort] : llvm::enumerate(newInputChannels))
107  newOperands[inPort.argNum] = pack.getFromChannels()[idx];
108  // Replace the users of the old bundle Value with the new one.
109  inst->getResult(origPort.argNum).replaceAllUsesWith(pack.getBundle());
110 }
111 
112 /// When replacing an instance with an input bundle, we must unpack the
113 /// bundle into its individual channels.
114 void BundlePort::buildInputSignals() {
115  auto bundleType = cast<ChannelBundleType>(origPort.type);
116  SmallVector<Value, 4> newInputValues;
117  SmallVector<BundledChannel, 4> outputChannels;
118 
119  for (BundledChannel ch : bundleType.getChannels()) {
120  // 'to' on an input bundle becomes an input channel.
121  if (ch.direction == ChannelDirection::to) {
122  hw::PortInfo newPort;
123  newInputValues.push_back(converter.createNewInput(
124  origPort, "_" + ch.name.getValue(), ch.type, newPort));
125  newInputChannels.push_back(newPort);
126  } else {
127  // 'from' on an input bundle becomes an output channel.
128  outputChannels.push_back(ch);
129  }
130  }
131 
132  // On an input port, new channels must be packed to recreate the original
133  // Value.
134  PackBundleOp pack;
135  if (body) {
136  ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
137  pack = b.create<PackBundleOp>(bundleType, newInputValues);
138  body->getArgument(origPort.argNum).replaceAllUsesWith(pack.getBundle());
139  }
140 
141  // Build new ports and put the new port info directly into the member
142  // variable.
143  newOutputChannels.resize(outputChannels.size());
144  for (auto [idx, ch] : llvm::enumerate(outputChannels))
145  converter.createNewOutput(origPort, "_" + ch.name.getValue(), ch.type,
146  pack ? pack.getFromChannels()[idx] : nullptr,
147  newOutputChannels[idx]);
148 }
149 
150 /// For an output port, we need to unpack the results from the original value
151 /// into the new channel ports.
152 void BundlePort::buildOutputSignals() {
153  auto bundleType = cast<ChannelBundleType>(origPort.type);
154  SmallVector<Value, 4> unpackChannels;
155  SmallVector<BundledChannel, 4> outputChannels;
156 
157  SmallVector<Type, 4> unpackOpResultTypes;
158  for (BundledChannel ch : bundleType.getChannels()) {
159  // 'from' on an input bundle becomes an input channel.
160  if (ch.direction == ChannelDirection::from) {
161  hw::PortInfo newPort;
162  unpackChannels.push_back(converter.createNewInput(
163  origPort, "_" + ch.name.getValue(), ch.type, newPort));
164  newInputChannels.push_back(newPort);
165  } else {
166  // 'to' on an input bundle becomes an output channel.
167  unpackOpResultTypes.push_back(ch.type);
168  outputChannels.push_back(ch);
169  }
170  }
171 
172  // For an output port, the original bundle must be unpacked into the
173  // individual channel ports.
174  UnpackBundleOp unpack;
175  if (body)
176  unpack = OpBuilder::atBlockTerminator(body).create<UnpackBundleOp>(
177  origPort.loc, body->getTerminator()->getOperand(origPort.argNum),
178  unpackChannels);
179 
180  // Build new ports and put the new port info directly into the member
181  // variable.
182  newOutputChannels.resize(outputChannels.size());
183  for (auto [idx, ch] : llvm::enumerate(outputChannels))
184  converter.createNewOutput(origPort, "_" + ch.name.getValue(), ch.type,
185  unpack ? unpack.getToChannels()[idx] : nullptr,
186  newOutputChannels[idx]);
187 }
188 
189 namespace {
190 /// Convert all the ESI bundle ports on modules to channel ports.
191 struct ESIBundlesPass : public LowerESIBundlesBase<ESIBundlesPass> {
192  void runOnOperation() override;
193 };
194 } // anonymous namespace
195 
196 /// Iterate through the `hw.module[.extern]`s and lower their ports.
197 void ESIBundlesPass::runOnOperation() {
198  MLIRContext &ctxt = getContext();
199  ModuleOp top = getOperation();
200 
201  // Find all modules and run port conversion on them.
202  circt::hw::InstanceGraph &instanceGraph =
203  getAnalysis<circt::hw::InstanceGraph>();
204  for (auto mod : top.getOps<HWMutableModuleLike>()) {
205  if (failed(PortConverter<ESIBundleConversionBuilder>(instanceGraph, mod)
206  .run()))
207  return signalPassFailure();
208  }
209 
210  // Canonicalize away bundle packs and unpacks. Any non-back-to-back [un]packs
211  // need to be gone by now.
212  RewritePatternSet patterns(&ctxt);
213  PackBundleOp::getCanonicalizationPatterns(patterns, &ctxt);
214  UnpackBundleOp::getCanonicalizationPatterns(patterns, &ctxt);
215  if (failed(mlir::applyPatternsAndFoldGreedily(getOperation(),
216  std::move(patterns))))
217  signalPassFailure();
218 
219  top.walk([&](PackBundleOp pack) {
220  pack.emitError("PackBundleOp should have been canonicalized away by now");
221  signalPassFailure();
222  });
223 }
224 
225 std::unique_ptr<OperationPass<ModuleOp>>
227  return std::make_unique<ESIBundlesPass>();
228 }
HW-specific instance graph with a virtual entry node linking to all publicly visible modules.
PortConversionBuilder(PortConverterImpl &converter)
virtual FailureOr< std::unique_ptr< PortConversion > > build(hw::PortInfo port)
Base class for the port conversion of a particular port.
Definition: PortConverter.h:97
std::unique_ptr< OperationPass< ModuleOp > > createESIBundleLoweringPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21