10 #include "slang/ast/Compilation.h"
12 using namespace circt;
13 using namespace ImportVerilog;
24 LogicalResult visit(
const slang::ast::EmptyMemberSymbol &) {
30 LogicalResult visit(
const slang::ast::TransparentMemberSymbol &) {
35 LogicalResult visit(
const slang::ast::TypeAliasType &) {
return success(); }
38 LogicalResult visit(
const slang::ast::ExplicitImportSymbol &) {
41 LogicalResult visit(
const slang::ast::WildcardImportSymbol &) {
52 struct RootVisitor :
public BaseVisitor {
53 using BaseVisitor::visit;
59 RootVisitor(
Context &context, Location loc)
60 : context(context), loc(loc), builder(context.builder) {}
63 LogicalResult visit(
const slang::ast::PackageSymbol &package) {
68 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
74 LogicalResult visit(T &&node) {
75 mlir::emitError(loc,
"unsupported construct: ")
87 struct PackageVisitor :
public BaseVisitor {
88 using BaseVisitor::visit;
94 PackageVisitor(
Context &context, Location loc)
95 : context(context), loc(loc), builder(context.builder) {}
98 LogicalResult visit(
const slang::ast::ParameterSymbol &) {
return success(); }
99 LogicalResult visit(
const slang::ast::SpecparamSymbol &) {
return success(); }
102 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
107 template <
typename T>
108 LogicalResult visit(T &&node) {
109 mlir::emitError(loc,
"unsupported construct: ")
120 static moore::ProcedureKind
123 case slang::ast::ProceduralBlockKind::Always:
124 return moore::ProcedureKind::Always;
125 case slang::ast::ProceduralBlockKind::AlwaysComb:
126 return moore::ProcedureKind::AlwaysComb;
127 case slang::ast::ProceduralBlockKind::AlwaysLatch:
128 return moore::ProcedureKind::AlwaysLatch;
129 case slang::ast::ProceduralBlockKind::AlwaysFF:
130 return moore::ProcedureKind::AlwaysFF;
131 case slang::ast::ProceduralBlockKind::Initial:
132 return moore::ProcedureKind::Initial;
133 case slang::ast::ProceduralBlockKind::Final:
134 return moore::ProcedureKind::Final;
136 llvm_unreachable(
"all procedure kinds handled");
141 case slang::ast::NetType::Supply0:
142 return moore::NetKind::Supply0;
143 case slang::ast::NetType::Supply1:
144 return moore::NetKind::Supply1;
145 case slang::ast::NetType::Tri:
146 return moore::NetKind::Tri;
147 case slang::ast::NetType::TriAnd:
148 return moore::NetKind::TriAnd;
149 case slang::ast::NetType::TriOr:
150 return moore::NetKind::TriOr;
151 case slang::ast::NetType::TriReg:
152 return moore::NetKind::TriReg;
153 case slang::ast::NetType::Tri0:
154 return moore::NetKind::Tri0;
155 case slang::ast::NetType::Tri1:
156 return moore::NetKind::Tri1;
157 case slang::ast::NetType::UWire:
158 return moore::NetKind::UWire;
159 case slang::ast::NetType::Wire:
160 return moore::NetKind::Wire;
161 case slang::ast::NetType::WAnd:
162 return moore::NetKind::WAnd;
163 case slang::ast::NetType::WOr:
164 return moore::NetKind::WOr;
165 case slang::ast::NetType::Interconnect:
166 return moore::NetKind::Interconnect;
167 case slang::ast::NetType::UserDefined:
168 return moore::NetKind::UserDefined;
169 case slang::ast::NetType::Unknown:
170 return moore::NetKind::Unknown;
172 llvm_unreachable(
"all net kinds handled");
176 struct ModuleVisitor :
public BaseVisitor {
177 using BaseVisitor::visit;
183 ModuleVisitor(
Context &context, Location loc)
184 : context(context), loc(loc), builder(context.builder) {}
187 LogicalResult visit(
const slang::ast::PortSymbol &) {
return success(); }
188 LogicalResult visit(
const slang::ast::MultiPortSymbol &) {
return success(); }
191 LogicalResult visit(
const slang::ast::GenvarSymbol &genvarNode) {
196 LogicalResult visit(
const slang::ast::InstanceSymbol &instNode) {
197 using slang::ast::ArgumentDirection;
198 using slang::ast::AssignmentExpression;
199 using slang::ast::MultiPortSymbol;
200 using slang::ast::PortSymbol;
205 auto module = moduleLowering->op;
206 auto moduleType = module.getModuleType();
209 SymbolTable::setSymbolVisibility(module, SymbolTable::Visibility::Private);
216 portValues.reserve(moduleType.getNumPorts());
218 for (
const auto *con : instNode.getPortConnections()) {
219 const auto *expr = con->getExpression();
224 auto *port = con->port.as_if<PortSymbol>();
225 if (
auto *existingPort =
226 moduleLowering->portsBySyntaxNode.lookup(port->getSyntax()))
229 switch (port->direction) {
230 case slang::ast::ArgumentDirection::In: {
232 cast<moore::UnpackedType>(context.
convertType(port->getType())));
234 if (
const auto *net =
235 port->internalSymbol->as_if<slang::ast::NetSymbol>()) {
236 auto netOp = builder.create<moore::NetOp>(
239 auto readOp = builder.create<moore::ReadOp>(
240 loc, refType.getNestedType(), netOp);
241 portValues.insert({port, readOp});
242 }
else if (
const auto *var =
244 ->as_if<slang::ast::VariableSymbol>()) {
245 auto varOp = builder.create<moore::VariableOp>(
248 auto readOp = builder.create<moore::ReadOp>(
249 loc, refType.getNestedType(), varOp);
250 portValues.insert({port, readOp});
252 return mlir::emitError(loc)
253 <<
"unsupported internal symbol for unconnected port `"
254 << port->name <<
"`";
261 case slang::ast::ArgumentDirection::Out:
266 return mlir::emitError(loc)
267 <<
"unsupported port `" << port->name <<
"` ("
274 if (
const auto *assign = expr->as_if<AssignmentExpression>())
275 expr = &assign->left();
280 if (
auto *port = con->port.as_if<PortSymbol>()) {
282 auto value = (port->direction == slang::ast::ArgumentDirection::In)
287 if (
auto *existingPort =
288 moduleLowering->portsBySyntaxNode.lookup(con->port.getSyntax()))
290 portValues.insert({port, value});
297 if (
const auto *multiPort = con->port.as_if<MultiPortSymbol>()) {
303 for (
const auto *port : llvm::reverse(multiPort->ports)) {
304 if (
auto *existingPort = moduleLowering->portsBySyntaxNode.lookup(
305 con->port.getSyntax()))
307 unsigned width = port->getType().getBitWidth();
308 auto sliceType = context.
convertType(port->getType());
311 Value slice = builder.create<moore::ExtractRefOp>(
315 if (port->direction == slang::ast::ArgumentDirection::In)
316 slice = builder.create<moore::ReadOp>(loc, sliceType, slice);
317 portValues.insert({port, slice});
323 mlir::emitError(loc) <<
"unsupported instance port `" << con->port.name
330 SmallVector<Value> inputValues;
331 SmallVector<Value> outputValues;
332 inputValues.reserve(moduleType.getNumInputs());
333 outputValues.reserve(moduleType.getNumOutputs());
335 for (
auto &port : moduleLowering->ports) {
336 auto value = portValues.lookup(&port.ast);
337 if (port.ast.direction == ArgumentDirection::Out)
338 outputValues.push_back(value);
340 inputValues.push_back(value);
344 auto inputNames = builder.getArrayAttr(moduleType.getInputNames());
345 auto outputNames = builder.getArrayAttr(moduleType.getOutputNames());
346 auto inst = builder.create<moore::InstanceOp>(
347 loc, moduleType.getOutputTypes(), builder.getStringAttr(instNode.name),
349 inputNames, outputNames);
352 for (
auto [lvalue, output] : llvm::zip(outputValues, inst.getOutputs()))
354 builder.create<moore::ContinuousAssignOp>(loc, lvalue, output);
360 LogicalResult visit(
const slang::ast::VariableSymbol &varNode) {
361 auto loweredType = context.
convertType(*varNode.getDeclaredType());
366 if (
const auto *init = varNode.getInitializer()) {
372 auto varOp = builder.create<moore::VariableOp>(
374 builder.getStringAttr(varNode.name), initial);
380 LogicalResult visit(
const slang::ast::NetSymbol &netNode) {
381 auto loweredType = context.
convertType(*netNode.getDeclaredType());
386 if (netNode.getInitializer()) {
393 if (netkind == moore::NetKind::Interconnect ||
394 netkind == moore::NetKind::UserDefined ||
395 netkind == moore::NetKind::Unknown)
396 return mlir::emitError(loc,
"unsupported net kind `")
397 << netNode.netType.name <<
"`";
399 auto netOp = builder.create<moore::NetOp>(
401 builder.getStringAttr(netNode.name), netkind, assignment);
407 LogicalResult visit(
const slang::ast::ContinuousAssignSymbol &assignNode) {
408 if (
const auto *delay = assignNode.getDelay()) {
410 return mlir::emitError(loc,
411 "delayed continuous assignments not supported");
415 assignNode.getAssignment().as<slang::ast::AssignmentExpression>();
422 if (
auto refOp = lhs.getDefiningOp<moore::StructExtractRefOp>()) {
423 auto input = refOp.getInput();
424 if (isa<moore::SVModuleOp>(input.getDefiningOp()->getParentOp())) {
425 builder.create<moore::StructInjectOp>(loc, input.getType(), input,
426 refOp.getFieldNameAttr(), rhs);
432 builder.create<moore::ContinuousAssignOp>(loc, lhs, rhs);
437 LogicalResult visit(
const slang::ast::ProceduralBlockSymbol &procNode) {
438 auto procOp = builder.create<moore::ProcedureOp>(
440 procOp.getBodyRegion().emplaceBlock();
441 OpBuilder::InsertionGuard guard(builder);
442 builder.setInsertionPointToEnd(procOp.getBody());
448 LogicalResult visit(
const slang::ast::ParameterSymbol ¶mNode) {
449 auto type = cast<moore::IntType>(context.
convertType(paramNode.getType()));
453 auto valueInt = paramNode.getValue().integer().as<uint64_t>().value();
454 Value value = builder.create<moore::ConstantOp>(loc, type, valueInt);
456 auto namedConstantOp = builder.create<moore::NamedConstantOp>(
457 loc, type, builder.getStringAttr(paramNode.name),
458 paramNode.isLocalParam()
460 moore::NamedConst::LocalParameter)
461 : moore::NamedConstAttr::
get(context.getContext(),
462 moore::NamedConst::Parameter),
464 context.
valueSymbols.insert(¶mNode, namedConstantOp);
469 LogicalResult visit(
const slang::ast::SpecparamSymbol &spNode) {
470 auto type = cast<moore::IntType>(context.
convertType(spNode.getType()));
474 auto valueInt = spNode.getValue().integer().as<uint64_t>().value();
475 Value value = builder.create<moore::ConstantOp>(loc, type, valueInt);
477 auto namedConstantOp = builder.create<moore::NamedConstantOp>(
478 loc, type, builder.getStringAttr(spNode.name),
480 moore::NamedConst::SpecParameter),
487 LogicalResult visit(
const slang::ast::GenerateBlockSymbol &genNode) {
488 if (!genNode.isUninstantiated) {
489 for (
auto &member : genNode.members()) {
490 if (failed(member.visit(ModuleVisitor(context, loc))))
498 LogicalResult visit(
const slang::ast::GenerateBlockArraySymbol &genArrNode) {
499 for (
const auto *member : genArrNode.entries) {
500 if (failed(member->asSymbol().visit(ModuleVisitor(context, loc))))
512 LogicalResult visit(
const slang::ast::StatementBlockSymbol &) {
517 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
522 template <
typename T>
523 LogicalResult visit(T &&node) {
524 mlir::emitError(loc,
"unsupported construct: ")
543 for (
auto *unit : root.compilationUnits) {
544 for (
const auto &member : unit->members()) {
546 if (failed(member.visit(RootVisitor(*
this, loc))))
552 SmallVector<const slang::ast::InstanceSymbol *> topInstances;
553 for (
auto *inst : root.topInstances)
574 using slang::ast::ArgumentDirection;
575 using slang::ast::MultiPortSymbol;
576 using slang::ast::ParameterSymbol;
577 using slang::ast::PortSymbol;
578 using slang::ast::TypeParameterSymbol;
580 auto parameters = module->parameters;
581 bool hasModuleSame =
false;
585 for (
auto const &existingModule :
modules) {
586 if (module->getDeclaringDefinition() ==
587 existingModule.getFirst()->getDeclaringDefinition()) {
588 auto moduleParameters = existingModule.getFirst()->parameters;
589 hasModuleSame =
true;
590 for (
auto it1 = parameters.begin(), it2 = moduleParameters.begin();
591 it1 != parameters.end() && it2 != moduleParameters.end();
594 if (it1 == parameters.end() || it2 == moduleParameters.end()) {
595 hasModuleSame =
false;
598 const auto *para1 = (*it1)->symbol.as_if<ParameterSymbol>();
599 const auto *para2 = (*it2)->symbol.as_if<ParameterSymbol>();
601 if ((para1 ==
nullptr) ^ (para2 ==
nullptr)) {
602 hasModuleSame =
false;
606 if (para1 !=
nullptr) {
607 hasModuleSame = para1->getValue() == para2->getValue();
610 if (para1 ==
nullptr) {
612 (*it1)->symbol.as<TypeParameterSymbol>().getTypeAlias());
614 (*it2)->symbol.as<TypeParameterSymbol>().getTypeAlias());
615 hasModuleSame = para1Type == para2Type;
621 module = existingModule.first;
630 slot = std::make_unique<ModuleLowering>();
631 auto &lowering = *slot;
634 OpBuilder::InsertionGuard g(
builder);
639 if (module->getDefinition().definitionKind !=
640 slang::ast::DefinitionKind::Module) {
641 mlir::emitError(loc) <<
"unsupported construct: "
642 << module->getDefinition().getKindString();
647 auto block = std::make_unique<Block>();
648 SmallVector<hw::ModulePort> modulePorts;
649 for (
auto *symbol : module->getPortList()) {
650 auto handlePort = [&](
const PortSymbol &port) {
655 auto portName =
builder.getStringAttr(port.name);
657 if (port.direction == ArgumentDirection::Out) {
662 if (port.direction != slang::ast::ArgumentDirection::In)
665 arg = block->addArgument(type, portLoc);
667 lowering.ports.push_back({port, portLoc, arg});
671 if (
const auto *port = symbol->as_if<PortSymbol>()) {
672 if (failed(handlePort(*port)))
674 }
else if (
const auto *multiPort = symbol->as_if<MultiPortSymbol>()) {
675 for (
auto *port : multiPort->ports)
676 if (failed(handlePort(*port)))
680 <<
"unsupported module port `" << symbol->name <<
"` ("
693 builder.setInsertionPoint(it->second);
697 builder.create<moore::SVModuleOp>(loc, module->name, moduleType);
699 moduleOp.getBodyRegion().push_back(block.release());
700 lowering.op = moduleOp;
710 for (
const auto &port : lowering.ports)
711 lowering.portsBySyntaxNode.insert({port.ast.getSyntax(), &port.ast});
720 auto &lowering = *
modules[module];
721 OpBuilder::InsertionGuard g(
builder);
722 builder.setInsertionPointToEnd(lowering.op.getBody());
726 for (
auto &member : module->members()) {
728 if (failed(member.visit(ModuleVisitor(*
this, loc))))
735 SmallVector<Value> outputs;
736 for (
auto &port : lowering.ports) {
738 if (
auto *expr = port.ast.getInternalExpr()) {
740 }
else if (port.ast.internalSymbol) {
741 if (
const auto *sym =
742 port.ast.internalSymbol->as_if<slang::ast::ValueSymbol>())
746 return mlir::emitError(port.loc,
"unsupported port: `")
748 <<
"` does not map to an internal symbol or expression";
751 if (port.ast.direction == slang::ast::ArgumentDirection::Out) {
752 if (isa<moore::RefType>(value.getType()))
753 value =
builder.create<moore::ReadOp>(
755 cast<moore::RefType>(value.getType()).getNestedType(), value);
756 outputs.push_back(value);
762 Value portArg = port.arg;
763 if (port.ast.direction != slang::ast::ArgumentDirection::In)
764 portArg =
builder.create<moore::ReadOp>(
765 port.loc, cast<moore::RefType>(value.getType()).getNestedType(),
767 builder.create<moore::ContinuousAssignOp>(port.loc, value, portArg);
769 builder.create<moore::OutputOp>(lowering.op.getLoc(), outputs);
777 OpBuilder::InsertionGuard g(
builder);
780 for (
auto &member : package.members()) {
782 if (failed(member.visit(PackageVisitor(*
this, loc))))
789 SmallString<64> &prefix) {
790 if (symbol.kind == slang::ast::SymbolKind::Root)
793 if (!symbol.name.empty()) {
794 prefix += symbol.name;
803 using slang::ast::ArgumentDirection;
810 return lowering.get();
812 lowering = std::make_unique<FunctionLowering>();
817 OpBuilder::InsertionGuard g(
builder);
822 builder.setInsertionPoint(it->second);
825 if (subroutine.thisVar) {
826 mlir::emitError(loc) <<
"unsupported class method";
831 SmallVector<Type> inputTypes;
832 SmallVector<Type, 1> outputTypes;
834 for (
const auto *arg : subroutine.getArguments()) {
835 auto type = cast<moore::UnpackedType>(
convertType(arg->getType()));
838 if (arg->direction == ArgumentDirection::In) {
839 inputTypes.push_back(type);
845 if (!subroutine.getReturnType().isVoid()) {
846 auto type =
convertType(subroutine.getReturnType());
849 outputTypes.push_back(type);
856 SmallString<64> funcName;
858 funcName += subroutine.name;
861 auto funcOp =
builder.create<mlir::func::FuncOp>(loc, funcName, funcType);
862 SymbolTable::setSymbolVisibility(funcOp, SymbolTable::Visibility::Private);
864 lowering->op = funcOp;
870 return lowering.get();
883 auto &block = lowering->op.getBody().emplaceBlock();
884 for (
auto [astArg, type] :
885 llvm::zip(subroutine.getArguments(),
886 lowering->op.getFunctionType().getInputs())) {
888 auto blockArg = block.addArgument(type, loc);
893 OpBuilder::InsertionGuard g(
builder);
894 builder.setInsertionPointToEnd(&block);
897 if (subroutine.returnValVar) {
898 auto type =
convertType(*subroutine.returnValVar->getDeclaredType());
901 returnVar =
builder.create<moore::VariableOp>(
902 lowering->op.getLoc(),
905 valueSymbols.insert(subroutine.returnValVar, returnVar);
913 if (block.empty() || !block.back().hasTrait<OpTrait::IsTerminator>()) {
914 if (returnVar && !subroutine.getReturnType().isVoid()) {
915 returnVar =
builder.create<moore::ReadOp>(returnVar.getLoc(), returnVar);
916 builder.create<mlir::func::ReturnOp>(lowering->op.getLoc(), returnVar);
918 builder.create<mlir::func::ReturnOp>(lowering->op.getLoc(), ValueRange{});
921 if (returnVar.use_empty())
922 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.
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)
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
Value convertRvalueExpression(const slang::ast::Expression &expr)
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.