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"
39#define DEBUG_TYPE "sim-lower-dpi-func"
43#define GEN_PASS_DEF_LOWERDPIFUNC
44#include "circt/Dialect/Sim/SimPasses.h.inc"
58 DenseMap<StringAttr, func::FuncOp> dpiFuncDeclMapping;
62struct LowerDPIFuncPass :
public sim::impl::LowerDPIFuncBase<LowerDPIFuncPass> {
64 LogicalResult lowerDPI();
65 LogicalResult lowerDPIFuncOp(sim::DPIFuncOp simFunc,
67 SymbolTable &symbolTable);
68 void runOnOperation()
override;
73LogicalResult LowerDPIFuncPass::lowerDPIFuncOp(sim::DPIFuncOp simFunc,
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{};
82 llvm::SmallVector<Type> dpiFunctionArgumentTypes;
83 for (
auto &arg : dpiArgs) {
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";
93 case sim::DPIDirection::Input:
94 dpiFunctionArgumentTypes.push_back(arg.type);
96 case sim::DPIDirection::Output:
97 case sim::DPIDirection::InOut:
98 dpiFunctionArgumentTypes.push_back(
99 LLVM::LLVMPointerType::get(arg.type.getContext()));
101 case sim::DPIDirection::Return:
103 case sim::DPIDirection::Ref:
104 dpiFunctionArgumentTypes.push_back(arg.type);
109 auto funcType = builder.getFunctionType(
110 dpiFunctionArgumentTypes,
111 explicitReturnType ? TypeRange{explicitReturnType} : TypeRange{});
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;
128 func = func::FuncOp::create(builder,
129 simFunc.getVerilogName()
130 ? *simFunc.getVerilogName()
131 : simFunc.getSymName(),
138 auto wrapperFuncType = cast<FunctionType>(simFunc.getFunctionType());
139 auto funcOp = func::FuncOp::create(
141 loweringState.nameSpace.newName(simFunc.getSymName() +
"_wrapper"),
145 loweringState.dpiFuncDeclMapping[simFunc.getSymNameAttr()] = funcOp;
147 builder.setInsertionPointToStart(funcOp.addEntryBlock());
148 SmallVector<Value> functionInputs;
149 SmallVector<std::pair<Type, Value>> wrapperOutputAllocas;
150 Value explicitReturnValue;
152 size_t inputIndex = 0;
153 for (
auto &arg : dpiArgs) {
155 case sim::DPIDirection::Input:
156 functionInputs.push_back(funcOp.getArgument(inputIndex));
159 case sim::DPIDirection::Return:
161 case sim::DPIDirection::Ref:
162 functionInputs.push_back(funcOp.getArgument(inputIndex));
165 case sim::DPIDirection::Output: {
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});
174 case sim::DPIDirection::InOut: {
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);
181 functionInputs.push_back(alloca);
182 wrapperOutputAllocas.push_back({arg.type, alloca});
188 auto call = func::CallOp::create(builder, func, functionInputs);
189 if (explicitReturnType)
190 explicitReturnValue = call.getResult(0);
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);
199 func::ReturnOp::create(builder, results);
205LogicalResult LowerDPIFuncPass::lowerDPI() {
206 LLVM_DEBUG(llvm::dbgs() <<
"Lowering sim DPI func to func.func\n");
207 auto op = getOperation();
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)))
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();
221 callOp.setCallee(it->second.getSymNameAttr());
222 return WalkResult::advance();
224 if (result.wasInterrupted())
229void LowerDPIFuncPass::runOnOperation() {
230 if (failed(lowerDPI()))
231 return signalPassFailure();
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.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.