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 
14 #include "PassDetails.h"
18 #include "mlir/IR/Diagnostics.h"
19 #include "mlir/IR/ImplicitLocOpBuilder.h"
20 
21 using namespace circt;
22 using namespace firrtl;
23 
24 //===----------------------------------------------------------------------===//
25 // Pass Infrastructure
26 //===----------------------------------------------------------------------===//
27 
28 namespace {
29 struct LowerIntmodulesPass : public LowerIntmodulesBase<LowerIntmodulesPass> {
30  void runOnOperation() override;
31  using LowerIntmodulesBase::fixupEICGWrapper;
32 };
33 } // namespace
34 
35 static LogicalResult checkModForAnnotations(FModuleLike mod, StringRef name) {
36  if (!AnnotationSet(mod).empty())
37  return mod.emitError(name)
38  << " cannot have annotations since it is an intrinsic";
39  return success();
40 }
41 
42 static LogicalResult checkInstForAnnotations(FInstanceLike inst,
43  StringRef name) {
44  if (!AnnotationSet(inst).empty())
45  return inst.emitError(name)
46  << " instance cannot have annotations since it is an intrinsic";
47  return success();
48 }
49 
50 // This is the main entrypoint for the conversion pass.
51 void LowerIntmodulesPass::runOnOperation() {
52  auto &ig = getAnalysis<InstanceGraph>();
53 
54  bool changed = false;
55 
56  // Convert to int ops.
57  for (auto op :
58  llvm::make_early_inc_range(getOperation().getOps<FIntModuleOp>())) {
59  auto *node = ig.lookup(op);
60  changed = true;
61 
62  if (failed(checkModForAnnotations(op, op.getIntrinsic())))
63  return signalPassFailure();
64 
65  for (auto *use : llvm::make_early_inc_range(node->uses())) {
66  auto inst = use->getInstance<InstanceOp>();
67  if (failed(checkInstForAnnotations(inst, op.getIntrinsic())))
68  return signalPassFailure();
69 
70  // Replace the instance of this intmodule with firrtl.int.generic.
71  // Inputs become operands, outputs are the result (if any).
72  ImplicitLocOpBuilder builder(op.getLoc(), inst);
73 
74  SmallVector<Value> inputs;
75  struct OutputInfo {
76  Value result;
77  BundleType::BundleElement element;
78  };
79  SmallVector<OutputInfo> outputs;
80  for (auto [idx, result] : llvm::enumerate(inst.getResults())) {
81  // Replace inputs with wires that will be used as operands.
82  if (inst.getPortDirection(idx) != Direction::Out) {
83  auto w = builder.create<WireOp>(result.getLoc(), result.getType())
84  .getResult();
85  result.replaceAllUsesWith(w);
86  inputs.push_back(w);
87  continue;
88  }
89 
90  // Gather outputs. This will become a bundle if more than one, but
91  // typically there are zero or one.
92  auto ftype = dyn_cast<FIRRTLBaseType>(inst.getType(idx));
93  if (!ftype) {
94  inst.emitError("intrinsic has non-FIRRTL or non-base port type")
95  << inst.getType(idx);
96  signalPassFailure();
97  return;
98  }
99  outputs.push_back(
100  OutputInfo{inst.getResult(idx),
101  BundleType::BundleElement(inst.getPortName(idx),
102  /*isFlip=*/false, ftype)});
103  }
104 
105  // Create the replacement operation.
106  if (outputs.empty()) {
107  // If no outputs, just create the operation.
108  builder.create<GenericIntrinsicOp>(/*result=*/Type(),
109  op.getIntrinsicAttr(), inputs,
110  op.getParameters());
111 
112  } else if (outputs.size() == 1) {
113  // For single output, the result is the output.
114  auto resultType = outputs.front().element.type;
115  auto intop = builder.create<GenericIntrinsicOp>(
116  resultType, op.getIntrinsicAttr(), inputs, op.getParameters());
117  outputs.front().result.replaceAllUsesWith(intop.getResult());
118  } else {
119  // For multiple outputs, create a bundle with fields for each output
120  // and replace users with subfields.
121  auto resultType = builder.getType<BundleType>(llvm::map_to_vector(
122  outputs, [](const auto &info) { return info.element; }));
123  auto intop = builder.create<GenericIntrinsicOp>(
124  resultType, op.getIntrinsicAttr(), inputs, op.getParameters());
125  for (auto &output : outputs)
126  output.result.replaceAllUsesWith(builder.create<SubfieldOp>(
127  intop.getResult(), output.element.name));
128  }
129  // Remove instance from IR and instance graph.
130  use->erase();
131  inst.erase();
132  ++numInstances;
133  }
134  // Remove intmodule from IR and instance graph.
135  ig.erase(node);
136  op.erase();
137  ++numIntmodules;
138  }
139 
140  // Special handling for magic EICG wrapper extmodule. Deprecate and remove.
141  if (fixupEICGWrapper) {
142  constexpr StringRef eicgName = "EICG_wrapper";
143  for (auto op :
144  llvm::make_early_inc_range(getOperation().getOps<FExtModuleOp>())) {
145  if (op.getDefname() != eicgName)
146  continue;
147 
148  // FIXME: Dedup group annotation could be annotated to EICG_wrapper but
149  // it causes an error with `fixupEICGWrapper`. For now drop the
150  // annotation until we fully migrate into EICG intrinsic.
152  op.emitWarning() << "Annotation " << firrtl::dedupGroupAnnoClass
153  << " on EICG_wrapper is dropped";
154 
155  if (failed(checkModForAnnotations(op, eicgName)))
156  return signalPassFailure();
157 
158  auto *node = ig.lookup(op);
159  changed = true;
160  for (auto *use : llvm::make_early_inc_range(node->uses())) {
161  auto inst = use->getInstance<InstanceOp>();
162  if (failed(checkInstForAnnotations(inst, eicgName)))
163  return signalPassFailure();
164 
165  ImplicitLocOpBuilder builder(op.getLoc(), inst);
166  auto replaceResults = [](OpBuilder &b, auto &&range) {
167  return llvm::map_to_vector(range, [&b](auto v) {
168  auto w = b.create<WireOp>(v.getLoc(), v.getType()).getResult();
169  v.replaceAllUsesWith(w);
170  return w;
171  });
172  };
173 
174  auto inputs = replaceResults(builder, inst.getResults().drop_back());
175  auto intop = builder.create<GenericIntrinsicOp>(
176  builder.getType<ClockType>(), "circt_clock_gate", inputs,
177  op.getParameters());
178  inst.getResults().back().replaceAllUsesWith(intop.getResult());
179 
180  // Remove instance from IR and instance graph.
181  use->erase();
182  inst.erase();
183  }
184  // Remove extmodule from IR and instance graph.
185  ig.erase(node);
186  op.erase();
187  }
188  }
189 
190  markAnalysesPreserved<InstanceGraph>();
191 
192  if (!changed)
193  markAllAnalysesPreserved();
194 }
195 
196 /// This is the pass constructor.
197 std::unique_ptr<mlir::Pass>
199  auto pass = std::make_unique<LowerIntmodulesPass>();
200  pass->fixupEICGWrapper = fixupEICGWrapper;
201  return pass;
202 }
static InstancePath empty
static LogicalResult checkModForAnnotations(FModuleLike mod, StringRef name)
static LogicalResult checkInstForAnnotations(FInstanceLike inst, StringRef name)
llvm::SmallVector< StringAttr > inputs
llvm::SmallVector< StringAttr > outputs
Builder builder
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