CIRCT  19.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 
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  op.emitWarning() << "Annotation " << firrtl::dedupGroupAnnoClass
162  << " on EICG_wrapper is dropped";
163 
164  if (failed(checkModForAnnotations(op, eicgName)))
165  return signalPassFailure();
166 
167  auto *node = ig.lookup(op);
168  changed = true;
169  for (auto *use : llvm::make_early_inc_range(node->uses())) {
170  auto inst = use->getInstance<InstanceOp>();
171  if (failed(checkInstForAnnotations(inst, eicgName)))
172  return signalPassFailure();
173 
174  ImplicitLocOpBuilder builder(op.getLoc(), inst);
175  auto replaceResults = [](OpBuilder &b, auto &&range) {
176  return llvm::map_to_vector(range, [&b](auto v) {
177  auto w = b.create<WireOp>(v.getLoc(), v.getType()).getResult();
178  v.replaceAllUsesWith(w);
179  return w;
180  });
181  };
182 
183  auto inputs = replaceResults(builder, inst.getResults().drop_back());
184  auto intop = builder.create<GenericIntrinsicOp>(
185  builder.getType<ClockType>(), "circt_clock_gate", inputs,
186  op.getParameters());
187  inst.getResults().back().replaceAllUsesWith(intop.getResult());
188 
189  // Remove instance from IR and instance graph.
190  use->erase();
191  inst.erase();
192  }
193  // Remove extmodule from IR and instance graph.
194  ig.erase(node);
195  op.erase();
196  }
197  }
198 
199  markAnalysesPreserved<InstanceGraph>();
200 
201  if (!changed)
202  markAllAnalysesPreserved();
203 }
204 
205 /// This is the pass constructor.
206 std::unique_ptr<mlir::Pass>
208  auto pass = std::make_unique<LowerIntmodulesPass>();
209  pass->fixupEICGWrapper = fixupEICGWrapper;
210  return pass;
211 }
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