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