10 #include "slang/ast/Compilation.h"
12 using namespace circt;
13 using namespace ImportVerilog;
20 SmallString<64> &prefix) {
21 if (symbol.kind != slang::ast::SymbolKind::Package)
24 if (!symbol.name.empty()) {
25 prefix += symbol.name;
42 BaseVisitor(
Context &context, Location loc)
43 : context(context), loc(loc), builder(context.builder) {}
46 LogicalResult visit(
const slang::ast::EmptyMemberSymbol &) {
52 LogicalResult visit(
const slang::ast::TransparentMemberSymbol &) {
57 LogicalResult visit(
const slang::ast::TypeAliasType &) {
return success(); }
60 LogicalResult visit(
const slang::ast::ExplicitImportSymbol &) {
63 LogicalResult visit(
const slang::ast::WildcardImportSymbol &) {
68 LogicalResult visit(
const slang::ast::TypeParameterSymbol &) {
73 LogicalResult visit(
const slang::ast::ElabSystemTaskSymbol &) {
78 LogicalResult visit(
const slang::ast::ParameterSymbol ¶m) {
79 visitParameter(param);
83 LogicalResult visit(
const slang::ast::SpecparamSymbol ¶m) {
84 visitParameter(param);
89 void visitParameter(
const Node ¶m) {
99 if (builder.getInsertionBlock()->getParentOp() == context.
intoModuleOp)
100 context.
orderedRootOps.insert({param.location, value.getDefiningOp()});
104 SmallString<64> paramName;
106 paramName += param.name;
108 builder.create<debug::VariableOp>(loc, builder.getStringAttr(paramName),
119 struct RootVisitor :
public BaseVisitor {
120 using BaseVisitor::BaseVisitor;
121 using BaseVisitor::visit;
124 LogicalResult visit(
const slang::ast::PackageSymbol &package) {
129 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
134 template <
typename T>
135 LogicalResult visit(T &&node) {
136 mlir::emitError(loc,
"unsupported construct: ")
148 struct PackageVisitor :
public BaseVisitor {
149 using BaseVisitor::BaseVisitor;
150 using BaseVisitor::visit;
153 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
158 template <
typename T>
159 LogicalResult visit(T &&node) {
160 mlir::emitError(loc,
"unsupported package member: ")
171 static moore::ProcedureKind
174 case slang::ast::ProceduralBlockKind::Always:
175 return moore::ProcedureKind::Always;
176 case slang::ast::ProceduralBlockKind::AlwaysComb:
177 return moore::ProcedureKind::AlwaysComb;
178 case slang::ast::ProceduralBlockKind::AlwaysLatch:
179 return moore::ProcedureKind::AlwaysLatch;
180 case slang::ast::ProceduralBlockKind::AlwaysFF:
181 return moore::ProcedureKind::AlwaysFF;
182 case slang::ast::ProceduralBlockKind::Initial:
183 return moore::ProcedureKind::Initial;
184 case slang::ast::ProceduralBlockKind::Final:
185 return moore::ProcedureKind::Final;
187 llvm_unreachable(
"all procedure kinds handled");
192 case slang::ast::NetType::Supply0:
193 return moore::NetKind::Supply0;
194 case slang::ast::NetType::Supply1:
195 return moore::NetKind::Supply1;
196 case slang::ast::NetType::Tri:
197 return moore::NetKind::Tri;
198 case slang::ast::NetType::TriAnd:
199 return moore::NetKind::TriAnd;
200 case slang::ast::NetType::TriOr:
201 return moore::NetKind::TriOr;
202 case slang::ast::NetType::TriReg:
203 return moore::NetKind::TriReg;
204 case slang::ast::NetType::Tri0:
205 return moore::NetKind::Tri0;
206 case slang::ast::NetType::Tri1:
207 return moore::NetKind::Tri1;
208 case slang::ast::NetType::UWire:
209 return moore::NetKind::UWire;
210 case slang::ast::NetType::Wire:
211 return moore::NetKind::Wire;
212 case slang::ast::NetType::WAnd:
213 return moore::NetKind::WAnd;
214 case slang::ast::NetType::WOr:
215 return moore::NetKind::WOr;
216 case slang::ast::NetType::Interconnect:
217 return moore::NetKind::Interconnect;
218 case slang::ast::NetType::UserDefined:
219 return moore::NetKind::UserDefined;
220 case slang::ast::NetType::Unknown:
221 return moore::NetKind::Unknown;
223 llvm_unreachable(
"all net kinds handled");
227 struct ModuleVisitor :
public BaseVisitor {
228 using BaseVisitor::BaseVisitor;
229 using BaseVisitor::visit;
232 LogicalResult visit(
const slang::ast::PortSymbol &) {
return success(); }
233 LogicalResult visit(
const slang::ast::MultiPortSymbol &) {
return success(); }
236 LogicalResult visit(
const slang::ast::GenvarSymbol &genvarNode) {
241 LogicalResult visit(
const slang::ast::DefParamSymbol &) {
return success(); }
245 LogicalResult visit(
const slang::ast::TypeParameterSymbol &) {
250 LogicalResult visit(
const slang::ast::InstanceSymbol &instNode) {
251 using slang::ast::ArgumentDirection;
252 using slang::ast::AssignmentExpression;
253 using slang::ast::MultiPortSymbol;
254 using slang::ast::PortSymbol;
259 auto module = moduleLowering->op;
260 auto moduleType = module.getModuleType();
263 SymbolTable::setSymbolVisibility(module, SymbolTable::Visibility::Private);
270 portValues.reserve(moduleType.getNumPorts());
272 for (
const auto *con : instNode.getPortConnections()) {
273 const auto *expr = con->getExpression();
278 auto *port = con->port.as_if<PortSymbol>();
279 if (
auto *existingPort =
280 moduleLowering->portsBySyntaxNode.lookup(port->getSyntax()))
283 switch (port->direction) {
284 case slang::ast::ArgumentDirection::In: {
286 cast<moore::UnpackedType>(context.
convertType(port->getType())));
288 if (
const auto *net =
289 port->internalSymbol->as_if<slang::ast::NetSymbol>()) {
290 auto netOp = builder.create<moore::NetOp>(
293 auto readOp = builder.create<moore::ReadOp>(
294 loc, refType.getNestedType(), netOp);
295 portValues.insert({port, readOp});
296 }
else if (
const auto *var =
298 ->as_if<slang::ast::VariableSymbol>()) {
299 auto varOp = builder.create<moore::VariableOp>(
302 auto readOp = builder.create<moore::ReadOp>(
303 loc, refType.getNestedType(), varOp);
304 portValues.insert({port, readOp});
306 return mlir::emitError(loc)
307 <<
"unsupported internal symbol for unconnected port `"
308 << port->name <<
"`";
315 case slang::ast::ArgumentDirection::Out:
320 return mlir::emitError(loc)
321 <<
"unsupported port `" << port->name <<
"` ("
328 if (
const auto *assign = expr->as_if<AssignmentExpression>())
329 expr = &assign->left();
334 if (
auto *port = con->port.as_if<PortSymbol>()) {
336 auto value = (port->direction == slang::ast::ArgumentDirection::In)
341 if (
auto *existingPort =
342 moduleLowering->portsBySyntaxNode.lookup(con->port.getSyntax()))
344 portValues.insert({port, value});
351 if (
const auto *multiPort = con->port.as_if<MultiPortSymbol>()) {
357 for (
const auto *port : llvm::reverse(multiPort->ports)) {
358 if (
auto *existingPort = moduleLowering->portsBySyntaxNode.lookup(
359 con->port.getSyntax()))
361 unsigned width = port->getType().getBitWidth();
362 auto sliceType = context.
convertType(port->getType());
365 Value slice = builder.create<moore::ExtractRefOp>(
369 if (port->direction == slang::ast::ArgumentDirection::In)
370 slice = builder.create<moore::ReadOp>(loc, sliceType, slice);
371 portValues.insert({port, slice});
377 mlir::emitError(loc) <<
"unsupported instance port `" << con->port.name
384 SmallVector<Value> inputValues;
385 SmallVector<Value> outputValues;
386 inputValues.reserve(moduleType.getNumInputs());
387 outputValues.reserve(moduleType.getNumOutputs());
389 for (
auto &port : moduleLowering->ports) {
390 auto value = portValues.lookup(&port.ast);
391 if (port.ast.direction == ArgumentDirection::Out)
392 outputValues.push_back(value);
394 inputValues.push_back(value);
398 for (
auto [value, type] :
399 llvm::zip(inputValues, moduleType.getInputTypes()))
400 if (value.getType() != type)
402 builder.create<moore::ConversionOp>(value.getLoc(), type, value);
405 auto inputNames = builder.getArrayAttr(moduleType.getInputNames());
406 auto outputNames = builder.getArrayAttr(moduleType.getOutputNames());
407 auto inst = builder.create<moore::InstanceOp>(
408 loc, moduleType.getOutputTypes(), builder.getStringAttr(instNode.name),
410 inputNames, outputNames);
413 for (
auto [lvalue, output] : llvm::zip(outputValues, inst.getOutputs())) {
416 Value rvalue = output;
417 auto dstType = cast<moore::RefType>(lvalue.getType()).getNestedType();
418 if (dstType != rvalue.getType())
419 rvalue = builder.create<moore::ConversionOp>(loc, dstType, rvalue);
420 builder.create<moore::ContinuousAssignOp>(loc, lvalue, rvalue);
427 LogicalResult visit(
const slang::ast::VariableSymbol &varNode) {
428 auto loweredType = context.
convertType(*varNode.getDeclaredType());
433 if (
const auto *init = varNode.getInitializer()) {
439 auto varOp = builder.create<moore::VariableOp>(
441 builder.getStringAttr(varNode.name), initial);
447 LogicalResult visit(
const slang::ast::NetSymbol &netNode) {
448 auto loweredType = context.
convertType(*netNode.getDeclaredType());
453 if (netNode.getInitializer()) {
461 if (netkind == moore::NetKind::Interconnect ||
462 netkind == moore::NetKind::UserDefined ||
463 netkind == moore::NetKind::Unknown)
464 return mlir::emitError(loc,
"unsupported net kind `")
465 << netNode.netType.name <<
"`";
467 auto netOp = builder.create<moore::NetOp>(
469 builder.getStringAttr(netNode.name), netkind, assignment);
475 LogicalResult visit(
const slang::ast::ContinuousAssignSymbol &assignNode) {
476 if (
const auto *delay = assignNode.getDelay()) {
478 return mlir::emitError(loc,
479 "delayed continuous assignments not supported");
483 assignNode.getAssignment().as<slang::ast::AssignmentExpression>();
489 expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
493 builder.create<moore::ContinuousAssignOp>(loc, lhs, rhs);
498 LogicalResult visit(
const slang::ast::ProceduralBlockSymbol &procNode) {
499 auto procOp = builder.create<moore::ProcedureOp>(
501 OpBuilder::InsertionGuard guard(builder);
502 builder.setInsertionPointToEnd(&procOp.getBody().emplaceBlock());
506 if (builder.getBlock())
507 builder.create<moore::ReturnOp>(loc);
512 LogicalResult visit(
const slang::ast::GenerateBlockSymbol &genNode) {
513 if (!genNode.isUninstantiated) {
514 for (
auto &member : genNode.members()) {
515 if (failed(member.visit(ModuleVisitor(context, loc))))
523 LogicalResult visit(
const slang::ast::GenerateBlockArraySymbol &genArrNode) {
524 for (
const auto *member : genArrNode.entries) {
525 if (failed(member->asSymbol().visit(ModuleVisitor(context, loc))))
537 LogicalResult visit(
const slang::ast::StatementBlockSymbol &) {
542 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
547 template <
typename T>
548 LogicalResult visit(T &&node) {
549 mlir::emitError(loc,
"unsupported module member: ")
568 for (
auto *unit : root.compilationUnits) {
569 for (
const auto &member : unit->members()) {
571 if (failed(member.visit(RootVisitor(*
this, loc))))
577 SmallVector<const slang::ast::InstanceSymbol *> topInstances;
578 for (
auto *inst : root.topInstances)
599 using slang::ast::ArgumentDirection;
600 using slang::ast::MultiPortSymbol;
601 using slang::ast::ParameterSymbol;
602 using slang::ast::PortSymbol;
603 using slang::ast::TypeParameterSymbol;
605 auto parameters = module->parameters;
606 bool hasModuleSame =
false;
610 for (
auto const &existingModule :
modules) {
611 if (module->getDeclaringDefinition() ==
612 existingModule.getFirst()->getDeclaringDefinition()) {
613 auto moduleParameters = existingModule.getFirst()->parameters;
614 hasModuleSame =
true;
615 for (
auto it1 = parameters.begin(), it2 = moduleParameters.begin();
616 it1 != parameters.end() && it2 != moduleParameters.end();
619 if (it1 == parameters.end() || it2 == moduleParameters.end()) {
620 hasModuleSame =
false;
623 const auto *para1 = (*it1)->symbol.as_if<ParameterSymbol>();
624 const auto *para2 = (*it2)->symbol.as_if<ParameterSymbol>();
626 if ((para1 ==
nullptr) ^ (para2 ==
nullptr)) {
627 hasModuleSame =
false;
631 if (para1 !=
nullptr) {
632 hasModuleSame = para1->getValue() == para2->getValue();
635 if (para1 ==
nullptr) {
637 (*it1)->symbol.as<TypeParameterSymbol>().getTypeAlias());
639 (*it2)->symbol.as<TypeParameterSymbol>().getTypeAlias());
640 hasModuleSame = para1Type == para2Type;
646 module = existingModule.first;
655 slot = std::make_unique<ModuleLowering>();
656 auto &lowering = *slot;
659 OpBuilder::InsertionGuard g(
builder);
664 if (module->getDefinition().definitionKind !=
665 slang::ast::DefinitionKind::Module) {
666 mlir::emitError(loc) <<
"unsupported definition: "
667 << module->getDefinition().getKindString();
672 auto block = std::make_unique<Block>();
673 SmallVector<hw::ModulePort> modulePorts;
674 for (
auto *symbol : module->getPortList()) {
675 auto handlePort = [&](
const PortSymbol &port) {
680 auto portName =
builder.getStringAttr(port.name);
682 if (port.direction == ArgumentDirection::Out) {
687 if (port.direction != slang::ast::ArgumentDirection::In)
690 arg = block->addArgument(type, portLoc);
692 lowering.ports.push_back({port, portLoc, arg});
696 if (
const auto *port = symbol->as_if<PortSymbol>()) {
697 if (failed(handlePort(*port)))
699 }
else if (
const auto *multiPort = symbol->as_if<MultiPortSymbol>()) {
700 for (
auto *port : multiPort->ports)
701 if (failed(handlePort(*port)))
705 <<
"unsupported module port `" << symbol->name <<
"` ("
718 builder.setInsertionPoint(it->second);
722 builder.create<moore::SVModuleOp>(loc, module->name, moduleType);
724 moduleOp.getBodyRegion().push_back(block.release());
725 lowering.op = moduleOp;
735 for (
const auto &port : lowering.ports)
736 lowering.portsBySyntaxNode.insert({port.ast.getSyntax(), &port.ast});
745 auto &lowering = *
modules[module];
746 OpBuilder::InsertionGuard g(
builder);
747 builder.setInsertionPointToEnd(lowering.op.getBody());
751 for (
auto &member : module->members()) {
753 if (failed(member.visit(ModuleVisitor(*
this, loc))))
760 SmallVector<Value> outputs;
761 for (
auto &port : lowering.ports) {
763 if (
auto *expr = port.ast.getInternalExpr()) {
765 }
else if (port.ast.internalSymbol) {
766 if (
const auto *sym =
767 port.ast.internalSymbol->as_if<slang::ast::ValueSymbol>())
771 return mlir::emitError(port.loc,
"unsupported port: `")
773 <<
"` does not map to an internal symbol or expression";
776 if (port.ast.direction == slang::ast::ArgumentDirection::Out) {
777 if (isa<moore::RefType>(value.getType()))
778 value =
builder.create<moore::ReadOp>(
780 cast<moore::RefType>(value.getType()).getNestedType(), value);
781 outputs.push_back(value);
787 Value portArg = port.arg;
788 if (port.ast.direction != slang::ast::ArgumentDirection::In)
789 portArg =
builder.create<moore::ReadOp>(
790 port.loc, cast<moore::RefType>(value.getType()).getNestedType(),
792 builder.create<moore::ContinuousAssignOp>(port.loc, value, portArg);
794 builder.create<moore::OutputOp>(lowering.op.getLoc(), outputs);
802 OpBuilder::InsertionGuard g(
builder);
805 for (
auto &member : package.members()) {
807 if (failed(member.visit(PackageVisitor(*
this, loc))))
817 using slang::ast::ArgumentDirection;
824 return lowering.get();
826 lowering = std::make_unique<FunctionLowering>();
831 OpBuilder::InsertionGuard g(
builder);
836 builder.setInsertionPoint(it->second);
839 if (subroutine.thisVar) {
840 mlir::emitError(loc) <<
"unsupported class method";
845 SmallVector<Type> inputTypes;
846 SmallVector<Type, 1> outputTypes;
848 for (
const auto *arg : subroutine.getArguments()) {
849 auto type = cast<moore::UnpackedType>(
convertType(arg->getType()));
852 if (arg->direction == ArgumentDirection::In) {
853 inputTypes.push_back(type);
859 if (!subroutine.getReturnType().isVoid()) {
860 auto type =
convertType(subroutine.getReturnType());
863 outputTypes.push_back(type);
870 SmallString<64> funcName;
872 funcName += subroutine.name;
875 auto funcOp =
builder.create<mlir::func::FuncOp>(loc, funcName, funcType);
876 SymbolTable::setSymbolVisibility(funcOp, SymbolTable::Visibility::Private);
878 lowering->op = funcOp;
884 return lowering.get();
897 auto &block = lowering->op.getBody().emplaceBlock();
898 for (
auto [astArg, type] :
899 llvm::zip(subroutine.getArguments(),
900 lowering->op.getFunctionType().getInputs())) {
902 auto blockArg = block.addArgument(type, loc);
907 OpBuilder::InsertionGuard g(
builder);
908 builder.setInsertionPointToEnd(&block);
911 if (subroutine.returnValVar) {
912 auto type =
convertType(*subroutine.returnValVar->getDeclaredType());
915 returnVar =
builder.create<moore::VariableOp>(
916 lowering->op.getLoc(),
919 valueSymbols.insert(subroutine.returnValVar, returnVar);
928 if (returnVar && !subroutine.getReturnType().isVoid()) {
929 Value read =
builder.create<moore::ReadOp>(returnVar.getLoc(), returnVar);
930 builder.create<mlir::func::ReturnOp>(lowering->op.getLoc(), read);
932 builder.create<mlir::func::ReturnOp>(lowering->op.getLoc(), ValueRange{});
935 if (returnVar && returnVar.use_empty())
936 returnVar.getDefiningOp()->erase();
const char * toString(Flow flow)
static moore::ProcedureKind convertProcedureKind(slang::ast::ProceduralBlockKind kind)
static void guessNamespacePrefix(const slang::ast::Symbol &symbol, SmallString< 64 > &prefix)
static moore::NetKind convertNetKind(slang::ast::NetType::NetKind kind)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
bool debugInfo
Generate debug information in the form of debug dialect ops in the IR.
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
LogicalResult convertFunction(const slang::ast::SubroutineSymbol &subroutine)
Convert a function.
ModuleLowering * convertModuleHeader(const slang::ast::InstanceBodySymbol *module)
Convert a module and its ports to an empty module op in the IR.
Value convertLvalueExpression(const slang::ast::Expression &expr)
Value materializeConstant(const slang::ConstantValue &constant, const slang::ast::Type &type, Location loc)
Helper function to materialize a ConstantValue as an SSA value.
LogicalResult convertModuleBody(const slang::ast::InstanceBodySymbol *module)
Convert a module's body to the corresponding IR ops.
slang::ast::Compilation & compilation
OpBuilder builder
The builder used to create IR operations.
std::queue< const slang::ast::InstanceBodySymbol * > moduleWorklist
A list of modules for which the header has been created, but the body has not been converted yet.
std::map< slang::SourceLocation, Operation * > orderedRootOps
The top-level operations ordered by their Slang source location.
Type convertType(const slang::ast::Type &type, LocationAttr loc={})
Convert a slang type into an MLIR type.
DenseMap< const slang::ast::SubroutineSymbol *, std::unique_ptr< FunctionLowering > > functions
Functions that have already been converted.
ValueSymbols valueSymbols
ValueSymbols::ScopeTy ValueSymbolScope
const ImportVerilogOptions & options
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
mlir::ModuleOp intoModuleOp
SymbolTable symbolTable
A symbol table of the MLIR module we are emitting into.
FunctionLowering * declareFunction(const slang::ast::SubroutineSymbol &subroutine)
Convert a function and its arguments to a function declaration in the IR.
LogicalResult convertPackage(const slang::ast::PackageSymbol &package)
Convert a package and its contents.
LogicalResult convertCompilation()
Convert hierarchy and structure AST nodes to MLIR ops.
MLIRContext * getContext()
Return the MLIR context.
LogicalResult convertStatement(const slang::ast::Statement &stmt)
DenseMap< const slang::ast::InstanceBodySymbol *, std::unique_ptr< ModuleLowering > > modules
How we have lowered modules to MLIR.
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.
Function lowering information.
Module lowering information.