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/Pass/Pass.h"
20 
21 namespace circt {
22 namespace firrtl {
23 #define GEN_PASS_DEF_LOWERINTMODULES
24 #include "circt/Dialect/FIRRTL/Passes.h.inc"
25 } // namespace firrtl
26 } // namespace circt
27 
28 using namespace circt;
29 using namespace firrtl;
30 
31 //===----------------------------------------------------------------------===//
32 // Pass Infrastructure
33 //===----------------------------------------------------------------------===//
34 
35 namespace {
36 struct LowerIntmodulesPass
37  : public circt::firrtl::impl::LowerIntmodulesBase<LowerIntmodulesPass> {
38  void runOnOperation() override;
39  using LowerIntmodulesBase::fixupEICGWrapper;
40 };
41 } // namespace
42 
43 static 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 
50 static 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.
59 void 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.
223 std::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
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21