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