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