CIRCT 21.0.0git
Loading...
Searching...
No Matches
LowerIntmodules.cpp
Go to the documentation of this file.
1//===- LowerIntmodules.cpp - Lower intmodules to ops ------------*- 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// This file defines the LowerIntmodules pass. This pass processes
10// FIRRTL intmodules and replaces all instances with generic intrinsic ops.
11//
12//===----------------------------------------------------------------------===//
13
18#include "mlir/IR/Diagnostics.h"
19#include "mlir/Pass/Pass.h"
20
21namespace circt {
22namespace firrtl {
23#define GEN_PASS_DEF_LOWERINTMODULES
24#include "circt/Dialect/FIRRTL/Passes.h.inc"
25} // namespace firrtl
26} // namespace circt
27
28using namespace circt;
29using namespace firrtl;
30
31//===----------------------------------------------------------------------===//
32// Pass Infrastructure
33//===----------------------------------------------------------------------===//
34
35namespace {
36struct LowerIntmodulesPass
37 : public circt::firrtl::impl::LowerIntmodulesBase<LowerIntmodulesPass> {
38 void runOnOperation() override;
39 using LowerIntmodulesBase::fixupEICGWrapper;
40};
41} // namespace
42
43static LogicalResult checkModForAnnotations(FModuleLike mod, StringRef name) {
44 if (!AnnotationSet(mod).empty())
45 return mod.emitError(name)
46 << " cannot have annotations since it is an intrinsic";
47 return success();
48}
49
50static LogicalResult checkInstForAnnotations(FInstanceLike inst,
51 StringRef name) {
52 if (!AnnotationSet(inst).empty())
53 return inst.emitError(name)
54 << " instance cannot have annotations since it is an intrinsic";
55 return success();
56}
57
58// This is the main entrypoint for the conversion pass.
59void LowerIntmodulesPass::runOnOperation() {
60 auto &ig = getAnalysis<InstanceGraph>();
61
62 bool changed = false;
63 bool warnEICGwrapperDropsDedupAnno = false;
64
65 // Convert to int ops.
66 for (auto op :
67 llvm::make_early_inc_range(getOperation().getOps<FIntModuleOp>())) {
68 auto *node = ig.lookup(op);
69 changed = true;
70
71 if (failed(checkModForAnnotations(op, op.getIntrinsic())))
72 return signalPassFailure();
73
74 for (auto *use : llvm::make_early_inc_range(node->uses())) {
75 auto inst = use->getInstance<InstanceOp>();
76 if (failed(checkInstForAnnotations(inst, op.getIntrinsic())))
77 return signalPassFailure();
78
79 // Replace the instance of this intmodule with firrtl.int.generic.
80 // Inputs become operands, outputs are the result (if any).
81 ImplicitLocOpBuilder builder(op.getLoc(), inst);
82
83 SmallVector<Value> inputs;
84 struct OutputInfo {
85 Value result;
86 BundleType::BundleElement element;
87 };
88 SmallVector<OutputInfo> outputs;
89 for (auto [idx, result] : llvm::enumerate(inst.getResults())) {
90 // Replace inputs with wires that will be used as operands.
91 if (inst.getPortDirection(idx) != Direction::Out) {
92 auto w = builder.create<WireOp>(result.getLoc(), result.getType())
93 .getResult();
94 result.replaceAllUsesWith(w);
95 inputs.push_back(w);
96 continue;
97 }
98
99 // Gather outputs. This will become a bundle if more than one, but
100 // typically there are zero or one.
101 auto ftype = dyn_cast<FIRRTLBaseType>(inst.getType(idx));
102 if (!ftype) {
103 inst.emitError("intrinsic has non-FIRRTL or non-base port type")
104 << inst.getType(idx);
105 signalPassFailure();
106 return;
107 }
108 outputs.push_back(
109 OutputInfo{inst.getResult(idx),
110 BundleType::BundleElement(inst.getPortName(idx),
111 /*isFlip=*/false, ftype)});
112 }
113
114 // Create the replacement operation.
115 if (outputs.empty()) {
116 // If no outputs, just create the operation.
117 builder.create<GenericIntrinsicOp>(/*result=*/Type(),
118 op.getIntrinsicAttr(), inputs,
119 op.getParameters());
120
121 } else if (outputs.size() == 1) {
122 // For single output, the result is the output.
123 auto resultType = outputs.front().element.type;
124 auto intop = builder.create<GenericIntrinsicOp>(
125 resultType, op.getIntrinsicAttr(), inputs, op.getParameters());
126 outputs.front().result.replaceAllUsesWith(intop.getResult());
127 } else {
128 // For multiple outputs, create a bundle with fields for each output
129 // and replace users with subfields.
130 auto resultType = builder.getType<BundleType>(llvm::map_to_vector(
131 outputs, [](const auto &info) { return info.element; }));
132 auto intop = builder.create<GenericIntrinsicOp>(
133 resultType, op.getIntrinsicAttr(), inputs, op.getParameters());
134 for (auto &output : outputs)
135 output.result.replaceAllUsesWith(builder.create<SubfieldOp>(
136 intop.getResult(), output.element.name));
137 }
138 // Remove instance from IR and instance graph.
139 use->erase();
140 inst.erase();
141 ++numInstances;
142 }
143 // Remove intmodule from IR and instance graph.
144 ig.erase(node);
145 op.erase();
146 ++numIntmodules;
147 }
148
149 // Special handling for magic EICG wrapper extmodule. Deprecate and remove.
150 if (fixupEICGWrapper) {
151 constexpr StringRef eicgName = "EICG_wrapper";
152 for (auto op :
153 llvm::make_early_inc_range(getOperation().getOps<FExtModuleOp>())) {
154 if (op.getDefname() != eicgName)
155 continue;
156
157 // FIXME: Dedup group annotation could be annotated to EICG_wrapper but
158 // it causes an error with `fixupEICGWrapper`. For now drop the
159 // annotation until we fully migrate into EICG intrinsic.
161 if (!warnEICGwrapperDropsDedupAnno) {
162 op.emitWarning() << "Annotation " << firrtl::dedupGroupAnnoClass
163 << " on EICG_wrapper is dropped";
164 warnEICGwrapperDropsDedupAnno = true;
165 }
166
167 if (failed(checkModForAnnotations(op, eicgName)))
168 return signalPassFailure();
169
170 auto *node = ig.lookup(op);
171 changed = true;
172 for (auto *use : llvm::make_early_inc_range(node->uses())) {
173 auto inst = use->getInstance<InstanceOp>();
174 if (failed(checkInstForAnnotations(inst, eicgName)))
175 return signalPassFailure();
176
177 ImplicitLocOpBuilder builder(op.getLoc(), inst);
178 auto replaceResults = [](OpBuilder &b, auto &&range) {
179 return llvm::map_to_vector(range, [&b](auto v) {
180 auto w = b.create<WireOp>(v.getLoc(), v.getType()).getResult();
181 v.replaceAllUsesWith(w);
182 return w;
183 });
184 };
185
186 auto inputs = replaceResults(builder, inst.getResults().drop_back());
187 // en and test_en are swapped between extmodule and intrinsic.
188 if (inputs.size() > 2) {
189 auto port1 = inst.getPortName(1);
190 auto port2 = inst.getPortName(2);
191 if (port1 != "test_en") {
192 mlir::emitError(op.getPortLocation(1),
193 "expected port named 'test_en'");
194 return signalPassFailure();
195 } else if (port2 != "en") {
196 mlir::emitError(op.getPortLocation(2), "expected port named 'en'");
197 return signalPassFailure();
198 } else
199 std::swap(inputs[1], inputs[2]);
200 }
201 auto intop = builder.create<GenericIntrinsicOp>(
202 builder.getType<ClockType>(), "circt_clock_gate", inputs,
203 op.getParameters());
204 inst.getResults().back().replaceAllUsesWith(intop.getResult());
205
206 // Remove instance from IR and instance graph.
207 use->erase();
208 inst.erase();
209 }
210 // Remove extmodule from IR and instance graph.
211 ig.erase(node);
212 op.erase();
213 }
214 }
215
216 markAnalysesPreserved<InstanceGraph>();
217
218 if (!changed)
219 markAllAnalysesPreserved();
220}
221
222/// This is the pass constructor.
223std::unique_ptr<mlir::Pass>
225 auto pass = std::make_unique<LowerIntmodulesPass>();
226 pass->fixupEICGWrapper = fixupEICGWrapper;
227 return pass;
228}
static InstancePath empty
static LogicalResult checkModForAnnotations(FModuleLike mod, StringRef name)
static LogicalResult checkInstForAnnotations(FInstanceLike inst, StringRef name)
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
std::unique_ptr< mlir::Pass > createLowerIntmodulesPass(bool fixupEICGWrapper=false)
This is the pass constructor.
constexpr const char * dedupGroupAnnoClass
void info(Twine message)
Definition LSPUtils.cpp:20
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.