Skip to content
Merged
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
2 changes: 2 additions & 0 deletions include/circt/Dialect/Moore/MooreOps.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
#include "circt/Dialect/Moore/MooreDialect.h"
#include "circt/Dialect/Moore/MooreTypes.h"
#include "mlir/IR/RegionKindInterface.h"
#include "mlir/Interfaces/CallInterfaces.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "mlir/Interfaces/FunctionInterfaces.h"
#include "mlir/Interfaces/InferTypeOpInterface.h"
#include "mlir/Interfaces/MemorySlotInterfaces.h"

Expand Down
15 changes: 15 additions & 0 deletions include/circt/Dialect/Moore/MooreOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/InferTypeOpInterface.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
include "mlir/Interfaces/MemorySlotInterfaces.td"
include "mlir/Interfaces/CallInterfaces.td"

// Base class for the operations in this dialect.
class MooreOp<string mnemonic, list<Trait> traits = []> :
Expand Down Expand Up @@ -2377,6 +2378,20 @@ def ClassPropertyDeclOp
}];
}


def ClassMethodDeclOp
: MooreOp<"class.methoddecl", [Symbol, HasParent<"ClassDeclOp">]> {
let summary = "Declare a class method";

let arguments = (ins SymbolNameAttr:$sym_name,
TypeAttrOf<FunctionType>:$function_type);

let results = (outs);
let assemblyFormat = [{
$sym_name `:` $function_type attr-dict
}];
}

def ClassDeclOp
: MooreOp<"class.classdecl", [Symbol, SymbolTable, IsolatedFromAbove,
NoTerminator, SingleBlock]> {
Expand Down
129 changes: 118 additions & 11 deletions lib/Conversion/ImportVerilog/Expressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,37 @@ struct RvalueExprVisitor : public ExprVisitor {
return value;
}

// We're reading a class property.
if (auto *const property =
expr.symbol.as_if<slang::ast::ClassPropertySymbol>()) {
auto type = context.convertType(*expr.type);

// Get the scope's implicit this variable
mlir::Value instRef = context.getImplicitThisRef();
if (!instRef) {
mlir::emitError(loc) << "class property '" << property->name
<< "' referenced without an implicit 'this'";
return {};
}

auto fieldSym =
mlir::FlatSymbolRefAttr::get(builder.getContext(), property->name);
auto fieldTy = cast<moore::UnpackedType>(type);
auto fieldRefTy = moore::RefType::get(fieldTy);

moore::ClassHandleType classTy =
cast<moore::ClassHandleType>(instRef.getType());

auto targetClassHandle =
context.getAncestorClassWithProperty(classTy, property->name);
auto upcastRef = context.materializeConversion(targetClassHandle, instRef,
false, instRef.getLoc());

Value fieldRef = moore::ClassPropertyRefOp::create(
builder, loc, fieldRefTy, upcastRef, fieldSym);
return moore::ReadOp::create(builder, loc, fieldRef).getResult();
}

// Try to materialize constant values directly.
auto constant = context.evaluateConstant(expr);
if (auto value = context.materializeConstant(constant, *expr.type, loc))
Expand Down Expand Up @@ -1155,12 +1186,6 @@ struct RvalueExprVisitor : public ExprVisitor {

/// Handle calls.
Value visit(const slang::ast::CallExpression &expr) {
// Class method calls are currently not supported.
if (expr.thisClass()) {
mlir::emitError(loc, "unsupported class method call");
return {};
}

// Try to materialize constant values directly.
auto constant = context.evaluateConstant(expr);
if (auto value = context.materializeConstant(constant, *expr.type, loc))
Expand All @@ -1171,9 +1196,77 @@ struct RvalueExprVisitor : public ExprVisitor {
expr.subroutine);
}

/// Get both the actual `this` argument of a method call and the required
/// class type.
std::pair<Value, moore::ClassHandleType>
getMethodReceiverTypeHandle(const slang::ast::CallExpression &expr) {

moore::ClassHandleType handleTy;
Value thisRef;

// Qualified call: t.m(...), extract from thisClass.
if (const slang::ast::Expression *recvExpr = expr.thisClass()) {
thisRef = context.convertRvalueExpression(*recvExpr);
if (!thisRef)
return {};
} else {
// Unqualified call inside a method body: try using implicit %this.
thisRef = context.getImplicitThisRef();
if (!thisRef) {
mlir::emitError(loc) << "method '" << expr.getSubroutineName()
<< "' called without an object";
return {};
}
}
handleTy = cast<moore::ClassHandleType>(thisRef.getType());
return {thisRef, handleTy};
}

/// Build a method call including implicit this argument.
mlir::func::CallOp
buildMethodCall(const slang::ast::SubroutineSymbol *subroutine,
FunctionLowering *lowering,
moore::ClassHandleType actualHandleTy, Value actualThisRef,
SmallVector<Value> &arguments,
SmallVector<Type> &resultTypes) {

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

// Upcast the handle as necessary.
auto implicitThisRef = context.materializeConversion(
expectedHdlTy, actualThisRef, false, actualThisRef.getLoc());

// Build an argument list where the this reference is the first argument.
SmallVector<Value> explicitArguments;
explicitArguments.reserve(arguments.size() + 1);
explicitArguments.push_back(implicitThisRef);
explicitArguments.append(arguments.begin(), arguments.end());

// Method call: choose direct vs virtual.
const bool isVirtual =
(subroutine->flags & slang::ast::MethodFlags::Virtual) != 0;

if (!isVirtual) {
// Direct (non-virtual) call -> moore.class.call
auto calleeSym =
SymbolRefAttr::get(context.getContext(), lowering->op.getSymName());
return mlir::func::CallOp::create(builder, loc, resultTypes, calleeSym,
explicitArguments);
}

mlir::emitError(loc) << "virtual method calls not supported";
return {};
}

/// Handle subroutine calls.
Value visitCall(const slang::ast::CallExpression &expr,
const slang::ast::SubroutineSymbol *subroutine) {

const bool isMethod = (subroutine->thisVar != nullptr);

auto *lowering = context.declareFunction(*subroutine);
if (!lowering)
return {};
Expand Down Expand Up @@ -1254,20 +1347,34 @@ struct RvalueExprVisitor : public ExprVisitor {
}
}

// Create the call.
auto callOp =
mlir::func::CallOp::create(builder, loc, lowering->op, arguments);
// Determine result types from the declared/converted func op.
SmallVector<Type> resultTypes(
lowering->op.getFunctionType().getResults().begin(),
lowering->op.getFunctionType().getResults().end());

mlir::func::CallOp callOp;
if (isMethod) {
// Class functions -> build func.call with implicit this argument
auto [thisRef, tyHandle] = getMethodReceiverTypeHandle(expr);
callOp = buildMethodCall(subroutine, lowering, tyHandle, thisRef,
arguments, resultTypes);
} else {
// Free function -> func.call
callOp =
mlir::func::CallOp::create(builder, loc, lowering->op, arguments);
}

auto result = resultTypes.size() > 0 ? callOp.getResult(0) : Value{};
// For calls to void functions we need to have a value to return from this
// function. Create a dummy `unrealized_conversion_cast`, which will get
// deleted again later on.
if (callOp.getNumResults() == 0)
if (resultTypes.size() == 0)
return mlir::UnrealizedConversionCastOp::create(
builder, loc, moore::VoidType::get(context.getContext()),
ValueRange{})
.getResult(0);

return callOp.getResult(0);
return result;
}

/// Handle system calls.
Expand Down
14 changes: 14 additions & 0 deletions lib/Conversion/ImportVerilog/ImportVerilogInternals.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ struct Context {
getAncestorClassWithProperty(const moore::ClassHandleType &actualTy,
StringRef fieldName);

Value getImplicitThisRef() const {
return currentThisRef; // block arg added in declareFunction
}
// Convert a statement AST node to MLIR ops.
LogicalResult convertStatement(const slang::ast::Statement &stmt);

Expand Down Expand Up @@ -316,6 +319,17 @@ struct Context {

/// The time scale currently in effect.
slang::TimeScale timeScale;

private:
/// Helper function to extract the commonalities in lowering of functions and
/// methods
FunctionLowering *
declareCallableImpl(const slang::ast::SubroutineSymbol &subroutine,
mlir::StringRef qualifiedName,
llvm::SmallVectorImpl<Type> &extraParams);
/// Variable to track the value of the current function's implicit `this`
/// reference
Value currentThisRef = {};
};

} // namespace ImportVerilog
Expand Down
Loading