CIRCT  20.0.0git
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/IR/ImplicitLocOpBuilder.h"
20 #include "mlir/Pass/Pass.h"
21 
22 namespace circt {
23 namespace firrtl {
24 #define GEN_PASS_DEF_LOWERINTMODULES
25 #include "circt/Dialect/FIRRTL/Passes.h.inc"
26 } // namespace firrtl
27 } // namespace circt
28 
29 using namespace circt;
30 using namespace firrtl;
31 
32 //===----------------------------------------------------------------------===//
33 // Pass Infrastructure
34 //===----------------------------------------------------------------------===//
35 
36 namespace {
37 struct LowerIntmodulesPass
38  : public circt::firrtl::impl::LowerIntmodulesBase<LowerIntmodulesPass> {
39  void runOnOperation() override;
40  using LowerIntmodulesBase::fixupEICGWrapper;
41 };
42 } // namespace
43 
44 static 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 
51 static 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.
60 void 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 = builder.create<WireOp>(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  builder.create<GenericIntrinsicOp>(/*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 = builder.create<GenericIntrinsicOp>(
126  resultType, op.getIntrinsicAttr(), inputs, op.getParameters());
127  outputs.front().result.replaceAllUsesWith(intop.getResult());
128  } else {
129  // For multiple outputs, create a bundle with fields for each output
130  // and replace users with subfields.
131  auto resultType = builder.getType<BundleType>(llvm::map_to_vector(
132  outputs, [](const auto &info) { return info.element; }));
133  auto intop = builder.create<GenericIntrinsicOp>(
134  resultType, op.getIntrinsicAttr(), inputs, op.getParameters());
135  for (auto &output : outputs)
136  output.result.replaceAllUsesWith(builder.create<SubfieldOp>(
137  intop.getResult(), output.element.name));
138  }
139  // Remove instance from IR and instance graph.
140  use->erase();
141  inst.erase();
142  ++numInstances;
143  }
144  // Remove intmodule from IR and instance graph.
145  ig.erase(node);
146  op.erase();
147  ++numIntmodules;
148  }
149 
150  // Special handling for magic EICG wrapper extmodule. Deprecate and remove.
151  if (fixupEICGWrapper) {
152  constexpr StringRef eicgName = "EICG_wrapper";
153  for (auto op :
154  llvm::make_early_inc_range(getOperation().getOps<FExtModuleOp>())) {
155  if (op.getDefname() != eicgName)
156  continue;
157 
158  // FIXME: Dedup group annotation could be annotated to EICG_wrapper but
159  // it causes an error with `fixupEICGWrapper`. For now drop the
160  // annotation until we fully migrate into EICG intrinsic.
162  if (!warnEICGwrapperDropsDedupAnno) {
163  op.emitWarning() << "Annotation " << firrtl::dedupGroupAnnoClass
164  << " on EICG_wrapper is dropped";
165  warnEICGwrapperDropsDedupAnno = true;
166  }
167 
168  if (failed(checkModForAnnotations(op, eicgName)))
169  return signalPassFailure();
170 
171  auto *node = ig.lookup(op);
172  changed = true;
173  for (auto *use : llvm::make_early_inc_range(node->uses())) {
174  auto inst = use->getInstance<InstanceOp>();
175  if (failed(checkInstForAnnotations(inst, eicgName)))
176  return signalPassFailure();
177 
178  ImplicitLocOpBuilder builder(op.getLoc(), inst);
179  auto replaceResults = [](OpBuilder &b, auto &&range) {
180  return llvm::map_to_vector(range, [&b](auto v) {
181  auto w = b.create<WireOp>(v.getLoc(), v.getType()).getResult();
182  v.replaceAllUsesWith(w);
183  return w;
184  });
185  };
186 
187  auto inputs = replaceResults(builder, inst.getResults().drop_back());
188  // en and test_en are swapped between extmodule and intrinsic.
189  if (inputs.size() > 2) {
190  auto port1 = inst.getPortName(1);
191  auto port2 = inst.getPortName(2);
192  if (port1 != "test_en") {
193  mlir::emitError(op.getPortLocation(1),
194  "expected port named 'test_en'");
195  return signalPassFailure();
196  } else if (port2 != "en") {
197  mlir::emitError(op.getPortLocation(2), "expected port named 'en'");
198  return signalPassFailure();
199  } else
200  std::swap(inputs[1], inputs[2]);
201  }
202  auto intop = builder.create<GenericIntrinsicOp>(
203  builder.getType<ClockType>(), "circt_clock_gate", inputs,
204  op.getParameters());
205  inst.getResults().back().replaceAllUsesWith(intop.getResult());
206 
207  // Remove instance from IR and instance graph.
208  use->erase();
209  inst.erase();
210  }
211  // Remove extmodule from IR and instance graph.
212  ig.erase(node);
213  op.erase();
214  }
215  }
216 
217  markAnalysesPreserved<InstanceGraph>();
218 
219  if (!changed)
220  markAllAnalysesPreserved();
221 }
222 
223 /// This is the pass constructor.
224 std::unique_ptr<mlir::Pass>
226  auto pass = std::make_unique<LowerIntmodulesPass>();
227  pass->fixupEICGWrapper = fixupEICGWrapper;
228  return pass;
229 }
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
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21