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