Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions lib/Conversion/ImportVerilog/Expressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1731,7 +1731,7 @@ struct RvalueExprVisitor : public ExprVisitor {
SmallVector<Type> &resultTypes) {

// Get the expected receiver type from the lowered method
auto funcTy = lowering->op.getFunctionType();
auto funcTy = cast<FunctionType>(lowering->op.getFunctionType());
auto expected0 = funcTy.getInput(0);
auto expectedHdlTy = cast<moore::ClassHandleType>(expected0);

Expand All @@ -1750,8 +1750,10 @@ struct RvalueExprVisitor : public ExprVisitor {
(subroutine->flags & slang::ast::MethodFlags::Virtual) != 0;

if (!isVirtual) {
auto calleeSym = lowering->op.getSymName();
// Direct (non-virtual) call -> moore.class.call
auto calleeSym = lowering->op.getName();
if (lowering->isCoroutine())
return moore::CallCoroutineOp::create(builder, loc, resultTypes,
calleeSym, explicitArguments);
return mlir::func::CallOp::create(builder, loc, resultTypes, calleeSym,
explicitArguments);
}
Expand Down Expand Up @@ -1859,8 +1861,8 @@ struct RvalueExprVisitor : public ExprVisitor {

// Determine result types from the declared/converted func op.
SmallVector<Type> resultTypes(
lowering->op.getFunctionType().getResults().begin(),
lowering->op.getFunctionType().getResults().end());
cast<FunctionType>(lowering->op.getFunctionType()).getResults().begin(),
cast<FunctionType>(lowering->op.getFunctionType()).getResults().end());

mlir::CallOpInterface callOp;
if (isMethod) {
Expand All @@ -1869,10 +1871,15 @@ struct RvalueExprVisitor : public ExprVisitor {
auto [thisRef, tyHandle] = getMethodReceiverTypeHandle(expr);
callOp = buildMethodCall(subroutine, lowering, tyHandle, thisRef,
arguments, resultTypes);
} else if (lowering->isCoroutine()) {
// Free task -> moore.call_coroutine
auto coroutine = cast<moore::CoroutineOp>(lowering->op);
callOp =
moore::CallCoroutineOp::create(builder, loc, coroutine, arguments);
} else {
// Free function -> func.call
callOp =
mlir::func::CallOp::create(builder, loc, lowering->op, arguments);
auto funcOp = cast<mlir::func::FuncOp>(lowering->op);
callOp = mlir::func::CallOp::create(builder, loc, funcOp, arguments);
}

auto result = resultTypes.size() > 0 ? callOp->getOpResult(0) : Value{};
Expand Down
9 changes: 7 additions & 2 deletions lib/Conversion/ImportVerilog/ImportVerilogInternals.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,18 @@ struct ModuleLowering {
portsBySyntaxNode;
};

/// Function lowering information.
/// Function lowering information. The `op` field holds either a `func::FuncOp`
/// (for SystemVerilog functions) or a `moore::CoroutineOp` (for tasks),
/// accessed through the `FunctionOpInterface`.
struct FunctionLowering {
mlir::func::FuncOp op;
mlir::FunctionOpInterface op;
llvm::SmallVector<Value, 4> captures;
llvm::DenseMap<Value, unsigned> captureIndex;
bool capturesFinalized = false;
bool isConverting = false;

/// Whether this is a coroutine (task) or a regular function.
bool isCoroutine() { return isa<moore::CoroutineOp>(op.getOperation()); }
};

// Class lowering information.
Expand Down
105 changes: 64 additions & 41 deletions lib/Conversion/ImportVerilog/Structure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1489,11 +1489,22 @@ Context::declareCallableImpl(const slang::ast::SubroutineSymbol &subroutine,
auto funcTy = getFunctionSignature(*this, subroutine, extraParams);
if (!funcTy)
return nullptr;
auto funcOp = mlir::func::FuncOp::create(builder, loc, qualifiedName, funcTy);

SymbolTable::setSymbolVisibility(funcOp, SymbolTable::Visibility::Private);
// Create a coroutine for tasks (which can suspend) or a function for
// functions (which cannot).
Operation *funcOp;
if (subroutine.subroutineKind == slang::ast::SubroutineKind::Task) {
auto op = moore::CoroutineOp::create(builder, loc, qualifiedName, funcTy);
SymbolTable::setSymbolVisibility(op, SymbolTable::Visibility::Private);
lowering->op = op;
funcOp = op;
} else {
auto op = mlir::func::FuncOp::create(builder, loc, qualifiedName, funcTy);
SymbolTable::setSymbolVisibility(op, SymbolTable::Visibility::Private);
lowering->op = op;
funcOp = op;
}
orderedRootOps.insert(it, {subroutine.location, funcOp});
lowering->op = funcOp;

// Add the function to the symbol table of the MLIR module, which uniquifies
// its name.
Expand All @@ -1506,59 +1517,66 @@ Context::declareCallableImpl(const slang::ast::SubroutineSymbol &subroutine,
/// Special case handling for recursive functions with captures;
/// this function fixes the in-body call of the recursive function with
/// the captured arguments.
static LogicalResult rewriteCallSitesToPassCaptures(mlir::func::FuncOp callee,
ArrayRef<Value> captures) {
static LogicalResult
rewriteCallSitesToPassCaptures(FunctionLowering &lowering) {
auto &captures = lowering.captures;
if (captures.empty())
return success();

auto *callee = lowering.op.getOperation();
mlir::ModuleOp module = callee->getParentOfType<mlir::ModuleOp>();
if (!module)
return callee.emitError("expected callee to be nested under ModuleOp");
return lowering.op.emitError("expected callee to be nested under ModuleOp");

auto usesOpt = mlir::SymbolTable::getSymbolUses(callee, module);
if (!usesOpt)
return callee.emitError("failed to compute symbol uses");
return lowering.op.emitError("failed to compute symbol uses");

// Snapshot the relevant users before we mutate IR.
SmallVector<mlir::func::CallOp, 8> callSites;
callSites.reserve(std::distance(usesOpt->begin(), usesOpt->end()));
// Snapshot the relevant call users before we mutate IR.
SmallVector<Operation *, 8> callSites;
for (const mlir::SymbolTable::SymbolUse &use : *usesOpt) {
if (auto call = llvm::dyn_cast<mlir::func::CallOp>(use.getUser()))
callSites.push_back(call);
auto *user = use.getUser();
if (isa<mlir::func::CallOp>(user) || isa<moore::CallCoroutineOp>(user))
callSites.push_back(user);
}
if (callSites.empty())
return success();

Block &entry = callee.getBody().front();
Block &entry = lowering.op.getFunctionBody().front();
const unsigned numCaps = captures.size();
const unsigned numEntryArgs = entry.getNumArguments();
if (numEntryArgs < numCaps)
return callee.emitError("entry block has fewer args than captures");
return lowering.op.emitError("entry block has fewer args than captures");
const unsigned capArgStart = numEntryArgs - numCaps;

// Current (finalized) function type.
auto fTy = callee.getFunctionType();
auto fTy = cast<FunctionType>(lowering.op.getFunctionType());

for (auto call : callSites) {
SmallVector<Value> newOperands(call.getArgOperands().begin(),
call.getArgOperands().end());
for (auto *callOp : callSites) {
// Get the existing operands from the call.
auto argOperands = callOp->getOperands();
SmallVector<Value> newOperands(argOperands.begin(), argOperands.end());

const bool inSameFunc = callee->isProperAncestor(call);
const bool inSameFunc = callee->isProperAncestor(callOp);
if (inSameFunc) {
// Append the function’s *capture block arguments* in order.
for (unsigned i = 0; i < numCaps; ++i)
newOperands.push_back(entry.getArgument(capArgStart + i));
} else {
// External call site: pass the captured SSA values.
newOperands.append(captures.begin(), captures.end());
}

OpBuilder b(call);
auto flatRef = mlir::FlatSymbolRefAttr::get(callee);
auto newCall = mlir::func::CallOp::create(
b, call.getLoc(), fTy.getResults(), flatRef, newOperands);
call->replaceAllUsesWith(newCall.getOperation());
call->erase();
OpBuilder b(callOp);
auto flatRef = mlir::FlatSymbolRefAttr::get(callee->getContext(),
lowering.op.getName());
Operation *newCall;
if (lowering.isCoroutine()) {
newCall = moore::CallCoroutineOp::create(
b, callOp->getLoc(), fTy.getResults(), flatRef, newOperands);
} else {
newCall = mlir::func::CallOp::create(
b, callOp->getLoc(), fTy.getResults(), flatRef, newOperands);
}
callOp->replaceAllUsesWith(newCall);
callOp->erase();
}

return success();
Expand Down Expand Up @@ -1616,21 +1634,22 @@ Context::convertFunction(const slang::ast::SubroutineSymbol &subroutine) {

// Create a function body block and populate it with block arguments.
SmallVector<moore::VariableOp> argVariables;
auto &block = lowering->op.getBody().emplaceBlock();
auto &block = lowering->op.getFunctionBody().emplaceBlock();

// If this is a class method, the first input is %this :
// !moore.class<@C>
if (isMethod) {
auto thisLoc = convertLocation(subroutine.location);
auto thisType = lowering->op.getFunctionType().getInput(0);
auto thisType =
cast<FunctionType>(lowering->op.getFunctionType()).getInput(0);
auto thisArg = block.addArgument(thisType, thisLoc);

// Bind `this` so NamedValue/MemberAccess can find it.
valueSymbols.insert(subroutine.thisVar, thisArg);
}

// Add user-defined block arguments
auto inputs = lowering->op.getFunctionType().getInputs();
auto inputs = cast<FunctionType>(lowering->op.getFunctionType()).getInputs();
auto astArgs = subroutine.getArguments();
auto valInputs = llvm::ArrayRef<Type>(inputs).drop_front(isMethod ? 1 : 0);

Expand Down Expand Up @@ -1692,7 +1711,7 @@ Context::convertFunction(const slang::ast::SubroutineSymbol &subroutine) {

// Don't capture anything that's a local reference
mlir::Region *defReg = ref.getParentRegion();
if (defReg && lowering->op.getBody().isAncestor(defReg))
if (defReg && lowering->op.getFunctionBody().isAncestor(defReg))
return;

// If we've already recorded this capture, skip.
Expand Down Expand Up @@ -1725,7 +1744,7 @@ Context::convertFunction(const slang::ast::SubroutineSymbol &subroutine) {

// Don't capture anything that's a local reference
mlir::Region *defReg = dstRef.getParentRegion();
if (defReg && lowering->op.getBody().isAncestor(defReg))
if (defReg && lowering->op.getFunctionBody().isAncestor(defReg))
return;

// If we've already recorded this capture, skip.
Expand Down Expand Up @@ -1759,13 +1778,15 @@ Context::convertFunction(const slang::ast::SubroutineSymbol &subroutine) {

// For the special case of recursive functions, fix the call sites within the
// body
if (failed(rewriteCallSitesToPassCaptures(lowering->op, lowering->captures)))
if (failed(rewriteCallSitesToPassCaptures(*lowering)))
return failure();

// If there was no explicit return statement provided by the user, insert a
// default one.
if (builder.getBlock()) {
if (returnVar && !subroutine.getReturnType().isVoid()) {
if (lowering->isCoroutine()) {
moore::ReturnOp::create(builder, lowering->op.getLoc());
} else if (returnVar && !subroutine.getReturnType().isVoid()) {
Value read =
moore::ReadOp::create(builder, returnVar.getLoc(), returnVar);
mlir::func::ReturnOp::create(builder, lowering->op.getLoc(), read);
Expand Down Expand Up @@ -1800,8 +1821,9 @@ Context::finalizeFunctionBodyCaptures(FunctionLowering &lowering) {
MLIRContext *ctx = getContext();

// Build new input type list: existing inputs + capture ref types.
SmallVector<Type> newInputs(lowering.op.getFunctionType().getInputs().begin(),
lowering.op.getFunctionType().getInputs().end());
SmallVector<Type> newInputs(
cast<FunctionType>(lowering.op.getFunctionType()).getInputs().begin(),
cast<FunctionType>(lowering.op.getFunctionType()).getInputs().end());

for (Value cap : lowering.captures) {
// Expect captures to be refs.
Expand All @@ -1815,11 +1837,12 @@ Context::finalizeFunctionBodyCaptures(FunctionLowering &lowering) {

// Results unchanged.
auto newFuncTy = FunctionType::get(
ctx, newInputs, lowering.op.getFunctionType().getResults());
lowering.op.setFunctionType(newFuncTy);
ctx, newInputs,
cast<FunctionType>(lowering.op.getFunctionType()).getResults());
lowering.op.setType(newFuncTy);

// Add the new block arguments to the entry block.
Block &entry = lowering.op.getBody().front();
Block &entry = lowering.op.getFunctionBody().front();
SmallVector<Value> capArgs;
capArgs.reserve(lowering.captures.size());
for (Type t :
Expand Down Expand Up @@ -2096,7 +2119,7 @@ struct ClassMethodVisitor : ClassDeclVisitorBase {
return success();

// Grab the finalized function type from the lowered func.op.
FunctionType fnTy = lowering->op.getFunctionType();
FunctionType fnTy = cast<FunctionType>(lowering->op.getFunctionType());
// Emit the method decl into the class body, preserving source order.
moore::ClassMethodDeclOp::create(builder, loc, fn.name, fnTy,
SymbolRefAttr::get(lowering->op));
Expand Down
24 changes: 12 additions & 12 deletions test/Conversion/ImportVerilog/basic.sv
Original file line number Diff line number Diff line change
Expand Up @@ -2133,7 +2133,7 @@ function void ConvertConditionalExprsToResultType(bit [15:0] x, struct packed {
r = z ? y : x;
endfunction

// CHECK-LABEL: func.func private @ImplicitEventControl(
// CHECK-LABEL: moore.coroutine private @ImplicitEventControl(
// CHECK-SAME: [[X:%[^:]+]]: !moore.ref<i32>
// CHECK-SAME: [[Y:%[^:]+]]: !moore.ref<i32>
task automatic ImplicitEventControl(ref int x, ref int y);
Expand Down Expand Up @@ -2163,7 +2163,7 @@ task automatic ImplicitEventControl(ref int x, ref int y);
@* dummyD(x + y);
endtask

// CHECK-LABEL: func.func private @DelayControl(
// CHECK-LABEL: moore.coroutine private @DelayControl(
// CHECK-SAME: [[X:%[^:]+]]: !moore.time
task automatic DelayControl(time x);
// CHECK: [[TMP:%.+]] = moore.constant_time 1234000 fs
Expand All @@ -2176,7 +2176,7 @@ task automatic DelayControl(time x);
#x dummyA();
endtask

// CHECK-LABEL: func.func private @SignalEventControl(
// CHECK-LABEL: moore.coroutine private @SignalEventControl(
// CHECK-SAME: [[X:%[^:]+]]: !moore.ref<i32>
// CHECK-SAME: [[Y:%[^:]+]]: !moore.ref<i32>
// CHECK-SAME: [[T:%[^:]+]]: !moore.ref<i1>
Expand Down Expand Up @@ -2275,7 +2275,7 @@ task automatic SignalEventControl(ref int x, ref int y, ref bit t, ref bit u, re
@(posedge t iff u, negedge u iff v) dummyA();
endtask

// CHECK-LABEL: func.func private @ImplicitEventControlExamples(
// CHECK-LABEL: moore.coroutine private @ImplicitEventControlExamples(
task automatic ImplicitEventControlExamples();
// Taken from IEEE 1800-2017 section 9.4.2.2 "Implicit event_expression list".
bit a, b, c, d, f, y, tmp1, tmp2;
Expand Down Expand Up @@ -3568,7 +3568,7 @@ module ContinuousAssignment;
assign #1ns c = ~b;
endmodule

// CHECK-LABEL: func.func private @BlockingAssignment(
// CHECK-LABEL: moore.coroutine private @BlockingAssignment(
// CHECK-SAME: [[A:%.+]]: !moore.ref<i42>
// CHECK-SAME: [[B:%.+]]: !moore.i42
// CHECK-SAME: [[C:%.+]]: !moore.i1
Expand All @@ -3595,7 +3595,7 @@ task BlockingAssignment(
a = @(posedge c) ~b;
endtask

// CHECK-LABEL: func.func private @NonBlockingAssignment(
// CHECK-LABEL: moore.coroutine private @NonBlockingAssignment(
// CHECK-SAME: [[A:%.+]]: !moore.ref<i42>
// CHECK-SAME: [[B:%.+]]: !moore.i42
task NonBlockingAssignment(
Expand Down Expand Up @@ -3696,11 +3696,11 @@ module testLHSTaskCapture();
endtask

always @(posedge a) begin
// CHECK: func.call @testTaskCapture([[A]]) : (!moore.ref<l1>) -> ()
// CHECK: moore.call_coroutine @testTaskCapture([[A]]) : (!moore.ref<l1>) -> ()
testTaskCapture;
end

// CHECK: func.func private @testTaskCapture(%arg0: !moore.ref<l1>) {
// CHECK: moore.coroutine private @testTaskCapture(%arg0: !moore.ref<l1>) {
// CHECK: [[CONST:%.+]] = moore.constant 0 : l1
// CHECK: moore.blocking_assign %arg0, [[CONST]] : l1

Expand Down Expand Up @@ -3746,13 +3746,13 @@ module CaptureInEventControl;
int data;

initial begin
// CHECK: call @waitForClk([[CLK]])
// CHECK: moore.call_coroutine @waitForClk([[CLK]])
waitForClk();
// CHECK: call @readOnClk([[CLK]], [[DATA]])
// CHECK: moore.call_coroutine @readOnClk([[CLK]], [[DATA]])
readOnClk();
end

// CHECK: func.func private @waitForClk(%arg0: !moore.ref<l1>)
// CHECK: moore.coroutine private @waitForClk(%arg0: !moore.ref<l1>)
task automatic waitForClk;
// CHECK: moore.wait_event {
// CHECK: [[TMP:%.+]] = moore.read %arg0
Expand All @@ -3761,7 +3761,7 @@ module CaptureInEventControl;
@(posedge clk);
endtask

// CHECK: func.func private @readOnClk(%arg0: !moore.ref<l1>, %arg1: !moore.ref<i32>)
// CHECK: moore.coroutine private @readOnClk(%arg0: !moore.ref<l1>, %arg1: !moore.ref<i32>)
task automatic readOnClk;
int result;
// CHECK: moore.wait_event {
Expand Down
2 changes: 1 addition & 1 deletion test/Conversion/ImportVerilog/virtual-interface.sv
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class consumer;
vif.data = v;
endfunction

// CHECK: func.func private @"consumer::wait_posedge_clk"(
// CHECK: moore.coroutine private @"consumer::wait_posedge_clk"(
// CHECK: moore.wait_event
// CHECK: moore.struct_extract
// CHECK-SAME: "clk"
Expand Down
Loading