CIRCT  20.0.0git
LowerDPI.cpp
Go to the documentation of this file.
1 //===- LowerDPI.cpp - Lower to DPI to Sim dialects ------------------------===//
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 LowerDPI pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
19 #include "circt/Dialect/SV/SVOps.h"
21 #include "mlir/IR/BuiltinOps.h"
22 #include "mlir/IR/Threading.h"
23 #include "mlir/Pass/Pass.h"
24 #include "mlir/Support/LogicalResult.h"
25 #include "llvm/ADT/MapVector.h"
26 
27 namespace circt {
28 namespace firrtl {
29 #define GEN_PASS_DEF_LOWERDPI
30 #include "circt/Dialect/FIRRTL/Passes.h.inc"
31 } // namespace firrtl
32 } // namespace circt
33 
34 using namespace mlir;
35 using namespace llvm;
36 using namespace circt;
37 using namespace circt::firrtl;
38 
39 namespace {
40 struct LowerDPIPass : public circt::firrtl::impl::LowerDPIBase<LowerDPIPass> {
41  void runOnOperation() override;
42 };
43 
44 // A helper struct to lower DPI intrinsics in the circuit.
45 struct LowerDPI {
46  LowerDPI(CircuitOp circuitOp) : circuitOp(circuitOp), nameSpace(circuitOp) {}
47  // Tte main logic.
48  LogicalResult run();
49  bool changed() const { return !funcNameToCallSites.empty(); }
50 
51 private:
52  // Walk all modules and peel `funcNameToCallSites`.
53  void collectIntrinsics();
54 
55  // Lower intrinsics recorded in `funcNameToCallSites`.
56  LogicalResult lower();
57 
58  sim::DPIFuncOp getOrCreateDPIFuncDecl(DPICallIntrinsicOp op);
59  LogicalResult lowerDPIIntrinsic(DPICallIntrinsicOp op);
60 
61  MapVector<StringAttr, SmallVector<DPICallIntrinsicOp>> funcNameToCallSites;
62 
63  // A map stores DPI func op for its function name and type.
64  llvm::DenseMap<std::pair<StringAttr, Type>, sim::DPIFuncOp>
65  functionSignatureToDPIFuncOp;
66 
67  firrtl::CircuitOp circuitOp;
68  CircuitNamespace nameSpace;
69 };
70 } // namespace
71 
72 void LowerDPI::collectIntrinsics() {
73  // A helper struct to collect DPI calls in the circuit.
74  struct DpiCallCollections {
75  FModuleOp module;
76  SmallVector<DPICallIntrinsicOp> dpiOps;
77  };
78 
79  SmallVector<DpiCallCollections, 0> collections;
80  collections.reserve(64);
81 
82  for (auto module : circuitOp.getOps<FModuleOp>())
83  collections.push_back(DpiCallCollections{module, {}});
84 
85  parallelForEach(circuitOp.getContext(), collections, [](auto &result) {
86  result.module.walk(
87  [&](DPICallIntrinsicOp dpi) { result.dpiOps.push_back(dpi); });
88  });
89 
90  for (auto &collection : collections)
91  for (auto dpi : collection.dpiOps)
92  funcNameToCallSites[dpi.getFunctionNameAttr()].push_back(dpi);
93 }
94 
95 // Lower FIRRTL type to core dialect types in which array type is replaced with
96 // an open array type.
97 static Type lowerDPIArgumentType(Type type) {
98  auto loweredType = lowerType(type);
99  return loweredType.replace([](hw::ArrayType array) {
100  return sv::UnpackedOpenArrayType::get(array.getElementType());
101  });
102 }
103 
104 static Value convertToUnpackedArray(ImplicitLocOpBuilder &builder,
105  Value loweredValue) {
106  if (isa<IntegerType>(loweredValue.getType()))
107  return loweredValue;
108 
109  auto array = dyn_cast<hw::ArrayType>(loweredValue.getType());
110  if (!array) {
111  // TODO: Support bundle or enum types.
112  return {};
113  }
114 
115  SmallVector<Value> values;
116  auto length = array.getNumElements();
117  auto width = llvm::Log2_64_Ceil(length);
118 
119  // Create an unpacked array from a packed array.
120  for (int i = length - 1; i >= 0; --i) {
121  auto index = builder.create<hw::ConstantOp>(APInt(width, i));
122  auto elem = convertToUnpackedArray(
123  builder, builder.create<hw::ArrayGetOp>(loweredValue, index));
124  if (!elem)
125  return {};
126  values.push_back(elem);
127  }
128 
129  return builder.create<sv::UnpackedArrayCreateOp>(
130  hw::UnpackedArrayType::get(lowerType(array.getElementType()), length),
131  values);
132 }
133 
134 static Value getLowered(ImplicitLocOpBuilder &builder, Value value) {
135  // Insert an unrealized conversion to cast FIRRTL type to HW type.
136  if (!value)
137  return value;
138 
139  auto type = lowerType(value.getType());
140  Value result = builder.create<mlir::UnrealizedConversionCastOp>(type, value)
141  ->getResult(0);
142 
143  // We may need to cast the lowered value to a DPI specific type (e.g. open
144  // array). Get a DPI type and check if they are same.
145  auto dpiType = lowerDPIArgumentType(value.getType());
146  if (type == dpiType)
147  return result;
148 
149  // Cast into unpacked open array type.
150  result = convertToUnpackedArray(builder, result);
151  if (!result) {
152  mlir::emitError(value.getLoc())
153  << "contains a type that currently not supported";
154  return {};
155  }
156 
157  return builder.create<sv::UnpackedOpenArrayCastOp>(dpiType, result);
158 }
159 
160 LogicalResult LowerDPI::lower() {
161  for (auto [name, calls] : funcNameToCallSites) {
162  auto firstDPICallop = calls.front();
163  // Construct DPI func op.
164  auto firstDPIDecl = getOrCreateDPIFuncDecl(firstDPICallop);
165 
166  auto inputTypes = firstDPICallop.getInputs().getTypes();
167  auto outputTypes = firstDPICallop.getResultTypes();
168 
169  ImplicitLocOpBuilder builder(firstDPICallop.getLoc(),
170  circuitOp.getOperation());
171  auto lowerCall = [&](DPICallIntrinsicOp dpiOp) {
172  builder.setInsertionPoint(dpiOp);
173  auto clock = getLowered(builder, dpiOp.getClock());
174  auto enable = getLowered(builder, dpiOp.getEnable());
175  SmallVector<Value, 4> inputs;
176  inputs.reserve(dpiOp.getInputs().size());
177  for (auto input : dpiOp.getInputs()) {
178  inputs.push_back(getLowered(builder, input));
179  if (!inputs.back())
180  return failure();
181  }
182 
183  SmallVector<Type> outputTypes;
184  if (dpiOp.getResult())
185  outputTypes.push_back(
186  lowerDPIArgumentType(dpiOp.getResult().getType()));
187 
188  auto call = builder.create<sim::DPICallOp>(
189  outputTypes, firstDPIDecl.getSymNameAttr(), clock, enable, inputs);
190  if (!call.getResults().empty()) {
191  // Insert unrealized conversion cast HW type to FIRRTL type.
192  auto result = builder
193  .create<mlir::UnrealizedConversionCastOp>(
194  dpiOp.getResult().getType(), call.getResult(0))
195  ->getResult(0);
196  dpiOp.getResult().replaceAllUsesWith(result);
197  }
198  return success();
199  };
200 
201  if (failed(lowerCall(firstDPICallop)))
202  return failure();
203 
204  for (auto dpiOp : llvm::ArrayRef(calls).drop_front()) {
205  // Check that all DPI declaration match.
206  // TODO: This should be implemented as a verifier once function is added
207  // to FIRRTL.
208  if (dpiOp.getInputs().getTypes() != inputTypes) {
209  auto diag = firstDPICallop.emitOpError()
210  << "DPI function " << firstDPICallop.getFunctionNameAttr()
211  << " input types don't match ";
212  diag.attachNote(dpiOp.getLoc()) << " mismatched caller is here";
213  return failure();
214  }
215 
216  if (dpiOp.getResultTypes() != outputTypes) {
217  auto diag = firstDPICallop.emitOpError()
218  << "DPI function " << firstDPICallop.getFunctionNameAttr()
219  << " output types don't match";
220  diag.attachNote(dpiOp.getLoc()) << " mismatched caller is here";
221  return failure();
222  }
223 
224  if (failed(lowerCall(dpiOp)))
225  return failure();
226  }
227 
228  for (auto callOp : calls)
229  callOp.erase();
230  }
231 
232  return success();
233 }
234 
235 sim::DPIFuncOp LowerDPI::getOrCreateDPIFuncDecl(DPICallIntrinsicOp op) {
236  ImplicitLocOpBuilder builder(op.getLoc(), circuitOp.getOperation());
237  builder.setInsertionPointToStart(circuitOp.getBodyBlock());
238  auto inputTypes = op.getInputs().getTypes();
239  auto outputTypes = op.getResultTypes();
240  ArrayAttr inputNames = op.getInputNamesAttr();
241  StringAttr outputName = op.getOutputNameAttr();
242  assert(outputTypes.size() <= 1);
243 
244  SmallVector<hw::ModulePort> ports;
245  ports.reserve(inputTypes.size() + outputTypes.size());
246 
247  // Add input arguments.
248  for (auto [idx, inType] : llvm::enumerate(inputTypes)) {
249  hw::ModulePort port;
251  port.name = inputNames ? cast<StringAttr>(inputNames[idx])
252  : builder.getStringAttr(Twine("in_") + Twine(idx));
253  port.type = lowerDPIArgumentType(inType);
254  ports.push_back(port);
255  }
256 
257  // Add output arguments.
258  for (auto [idx, outType] : llvm::enumerate(outputTypes)) {
259  hw::ModulePort port;
261  port.name = outputName ? outputName
262  : builder.getStringAttr(Twine("out_") + Twine(idx));
263  port.type = lowerDPIArgumentType(outType);
264  ports.push_back(port);
265  }
266 
267  auto modType = hw::ModuleType::get(builder.getContext(), ports);
268  auto it =
269  functionSignatureToDPIFuncOp.find({op.getFunctionNameAttr(), modType});
270  if (it != functionSignatureToDPIFuncOp.end())
271  return it->second;
272 
273  auto funcSymbol = nameSpace.newName(op.getFunctionNameAttr().getValue());
274  auto funcOp = builder.create<sim::DPIFuncOp>(
275  funcSymbol, modType, ArrayAttr(), ArrayAttr(), op.getFunctionNameAttr());
276  // External function must have a private linkage.
277  funcOp.setPrivate();
278  functionSignatureToDPIFuncOp[{op.getFunctionNameAttr(), modType}] = funcOp;
279  return funcOp;
280 }
281 
282 LogicalResult LowerDPI::run() {
283  collectIntrinsics();
284  return lower();
285 }
286 
287 void LowerDPIPass::runOnOperation() {
288  auto circuitOp = getOperation();
289  LowerDPI lowerDPI(circuitOp);
290  if (failed(lowerDPI.run()))
291  return signalPassFailure();
292  if (!lowerDPI.changed())
293  return markAllAnalysesPreserved();
294 }
295 
296 std::unique_ptr<mlir::Pass> circt::firrtl::createLowerDPIPass() {
297  return std::make_unique<LowerDPIPass>();
298 }
assert(baseType &&"element must be base type")
int32_t width
Definition: FIRRTL.cpp:36
@ Input
Definition: HW.h:35
@ Output
Definition: HW.h:35
static Value getLowered(ImplicitLocOpBuilder &builder, Value value)
Definition: LowerDPI.cpp:134
static Type lowerDPIArgumentType(Type type)
Definition: LowerDPI.cpp:97
static Value convertToUnpackedArray(ImplicitLocOpBuilder &builder, Value loweredValue)
Definition: LowerDPI.cpp:104
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
std::unique_ptr< mlir::Pass > createLowerDPIPass()
Definition: LowerDPI.cpp:296
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
Definition: codegen.py:121
The namespace of a CircuitOp, generally inhabited by modules.
Definition: Namespace.h:24