CIRCT 23.0.0git
Loading...
Searching...
No Matches
LowerDPIFunc.cpp
Go to the documentation of this file.
1//===- LowerDPIFunc.cpp - Lower sim.dpi.func to func.func ----*- 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 pass lowers Sim DPI func ops to MLIR func and call.
10//
11// sim.dpi.func @foo(input %a: i32, output %b: i64)
12// hw.module @top (..) {
13// %result = sim.dpi.call @foo(%a) clock %clock
14// }
15//
16// ->
17//
18// func.func @foo(%a: i32, %b: !llvm.ptr) // Output is passed by a reference.
19// func.func @foo_wrapper(%a: i32) -> (i64) {
20// %0 = llvm.alloca: !llvm.ptr
21// %v = func.call @foo (%a, %0)
22// func.return %v
23// }
24// hw.module @mod(..) {
25// %result = sim.dpi.call @foo_wrapper(%a) clock %clock
26// }
27//===----------------------------------------------------------------------===//
28
32#include "mlir/Dialect/Func/IR/FuncOps.h"
33#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
34#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
35#include "mlir/Transforms/DialectConversion.h"
36#include "llvm/Support/Debug.h"
37#include "llvm/Support/LogicalResult.h"
38
39#define DEBUG_TYPE "sim-lower-dpi-func"
40
41namespace circt {
42namespace sim {
43#define GEN_PASS_DEF_LOWERDPIFUNC
44#include "circt/Dialect/Sim/SimPasses.h.inc"
45} // namespace sim
46} // namespace circt
47
48using namespace mlir;
49using namespace circt;
50
51//===----------------------------------------------------------------------===//
52// Pass Implementation
53//===----------------------------------------------------------------------===//
54
55namespace {
56
57struct LoweringState {
58 DenseMap<StringAttr, func::FuncOp> dpiFuncDeclMapping;
59 circt::Namespace nameSpace;
60};
61
62struct LowerDPIFuncPass : public sim::impl::LowerDPIFuncBase<LowerDPIFuncPass> {
63
64 LogicalResult lowerDPI();
65 LogicalResult lowerDPIFuncOp(sim::DPIFuncOp simFunc,
66 LoweringState &loweringState,
67 SymbolTable &symbolTable);
68 void runOnOperation() override;
69};
70
71} // namespace
72
73LogicalResult LowerDPIFuncPass::lowerDPIFuncOp(sim::DPIFuncOp simFunc,
74 LoweringState &loweringState,
75 SymbolTable &symbolTable) {
76 ImplicitLocOpBuilder builder(simFunc.getLoc(), simFunc);
77 auto dpiType = simFunc.getDpiFunctionType();
78 auto dpiArgs = dpiType.getArguments();
79 auto *returnArg = dpiType.getReturnArgument();
80 Type explicitReturnType = returnArg ? returnArg->type : Type{};
81
82 llvm::SmallVector<Type> dpiFunctionArgumentTypes;
83 for (auto &arg : dpiArgs) {
84 // TODO: Support additional non-integer types beyond the pointer-shaped
85 // open-array ABI path.
86 if (!arg.type.isInteger() && !(isa<LLVM::LLVMPointerType>(arg.type) &&
87 (arg.dir == sim::DPIDirection::Input ||
88 arg.dir == sim::DPIDirection::Ref)))
89 return simFunc->emitError()
90 << "non-integer type argument is unsupported now";
91
92 switch (arg.dir) {
93 case sim::DPIDirection::Input:
94 dpiFunctionArgumentTypes.push_back(arg.type);
95 break;
96 case sim::DPIDirection::Output:
97 case sim::DPIDirection::InOut:
98 dpiFunctionArgumentTypes.push_back(
99 LLVM::LLVMPointerType::get(arg.type.getContext()));
100 break;
101 case sim::DPIDirection::Return:
102 break;
103 case sim::DPIDirection::Ref:
104 dpiFunctionArgumentTypes.push_back(arg.type);
105 break;
106 }
107 }
108
109 auto funcType = builder.getFunctionType(
110 dpiFunctionArgumentTypes,
111 explicitReturnType ? TypeRange{explicitReturnType} : TypeRange{});
112 func::FuncOp func;
113
114 // Look up func.func by verilog name since the function name is equal to the
115 // symbol name in MLIR
116 if (auto verilogName = simFunc.getVerilogName()) {
117 func = symbolTable.lookup<func::FuncOp>(*verilogName);
118 if (func && func.getFunctionType() != funcType)
119 return simFunc.emitOpError()
120 << "references existing func.func @" << *verilogName
121 << " with incompatible type " << func.getFunctionType()
122 << "; expected " << funcType;
123 }
124
125 // If a referred function is not in the same module, create an external
126 // function declaration.
127 if (!func) {
128 func = func::FuncOp::create(builder,
129 simFunc.getVerilogName()
130 ? *simFunc.getVerilogName()
131 : simFunc.getSymName(),
132 funcType);
133 // External function needs to be private.
134 func.setPrivate();
135 }
136
137 // Create a wrapper module that calls a DPI function.
138 auto wrapperFuncType = cast<FunctionType>(simFunc.getFunctionType());
139 auto funcOp = func::FuncOp::create(
140 builder,
141 loweringState.nameSpace.newName(simFunc.getSymName() + "_wrapper"),
142 wrapperFuncType);
143
144 // Map old symbol to a new func op.
145 loweringState.dpiFuncDeclMapping[simFunc.getSymNameAttr()] = funcOp;
146
147 builder.setInsertionPointToStart(funcOp.addEntryBlock());
148 SmallVector<Value> functionInputs;
149 SmallVector<std::pair<Type, Value>> wrapperOutputAllocas;
150 Value explicitReturnValue;
151
152 size_t inputIndex = 0;
153 for (auto &arg : dpiArgs) {
154 switch (arg.dir) {
155 case sim::DPIDirection::Input:
156 functionInputs.push_back(funcOp.getArgument(inputIndex));
157 ++inputIndex;
158 break;
159 case sim::DPIDirection::Return:
160 break;
161 case sim::DPIDirection::Ref:
162 functionInputs.push_back(funcOp.getArgument(inputIndex));
163 ++inputIndex;
164 break;
165 case sim::DPIDirection::Output: {
166 auto one =
167 LLVM::ConstantOp::create(builder, builder.getI64IntegerAttr(1));
168 auto alloca = LLVM::AllocaOp::create(
169 builder, builder.getType<LLVM::LLVMPointerType>(), arg.type, one);
170 functionInputs.push_back(alloca);
171 wrapperOutputAllocas.push_back({arg.type, alloca});
172 break;
173 }
174 case sim::DPIDirection::InOut: {
175 auto one =
176 LLVM::ConstantOp::create(builder, builder.getI64IntegerAttr(1));
177 auto alloca = LLVM::AllocaOp::create(
178 builder, builder.getType<LLVM::LLVMPointerType>(), arg.type, one);
179 LLVM::StoreOp::create(builder, funcOp.getArgument(inputIndex), alloca);
180 ++inputIndex;
181 functionInputs.push_back(alloca);
182 wrapperOutputAllocas.push_back({arg.type, alloca});
183 break;
184 }
185 }
186 }
187
188 auto call = func::CallOp::create(builder, func, functionInputs);
189 if (explicitReturnType)
190 explicitReturnValue = call.getResult(0);
191
192 SmallVector<Value> results;
193 results.reserve(wrapperOutputAllocas.size() + (explicitReturnValue ? 1 : 0));
194 for (auto [type, alloca] : wrapperOutputAllocas)
195 results.push_back(LLVM::LoadOp::create(builder, type, alloca));
196 if (explicitReturnValue)
197 results.push_back(explicitReturnValue);
198
199 func::ReturnOp::create(builder, results);
200
201 simFunc.erase();
202 return success();
203}
204
205LogicalResult LowerDPIFuncPass::lowerDPI() {
206 LLVM_DEBUG(llvm::dbgs() << "Lowering sim DPI func to func.func\n");
207 auto op = getOperation();
208 LoweringState state;
209 state.nameSpace.add(op);
210 auto &symbolTable = getAnalysis<SymbolTable>();
211 for (auto simFunc : llvm::make_early_inc_range(op.getOps<sim::DPIFuncOp>()))
212 if (failed(lowerDPIFuncOp(simFunc, state, symbolTable)))
213 return failure();
214
215 auto result = op.walk([&](sim::DPICallOp callOp) -> WalkResult {
216 auto it = state.dpiFuncDeclMapping.find(callOp.getCalleeAttr().getAttr());
217 if (it == state.dpiFuncDeclMapping.end()) {
218 callOp.emitOpError() << "references unknown DPI function";
219 return WalkResult::interrupt();
220 }
221 callOp.setCallee(it->second.getSymNameAttr());
222 return WalkResult::advance();
223 });
224 if (result.wasInterrupted())
225 return failure();
226 return success();
227}
228
229void LowerDPIFuncPass::runOnOperation() {
230 if (failed(lowerDPI()))
231 return signalPassFailure();
232}
std::shared_ptr< calyx::CalyxLoweringState > loweringState
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:30
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition sim.py:1