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 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>(loc, netOp);
294 portValues.insert({port, readOp});
295 }
else if (
const auto *var =
297 ->as_if<slang::ast::VariableSymbol>()) {
298 auto varOp = builder.create<moore::VariableOp>(
301 auto readOp = builder.create<moore::ReadOp>(loc, varOp);
302 portValues.insert({port, readOp});
304 return mlir::emitError(loc)
305 <<
"unsupported internal symbol for unconnected port `"
306 << port->name <<
"`";
313 case ArgumentDirection::Out:
318 return mlir::emitError(loc)
319 <<
"unsupported port `" << port->name <<
"` ("
326 if (
const auto *assign = expr->as_if<AssignmentExpression>())
327 expr = &assign->left();
332 if (
auto *port = con->port.as_if<PortSymbol>()) {
334 auto value = (port->direction == ArgumentDirection::In)
339 if (
auto *existingPort =
340 moduleLowering->portsBySyntaxNode.lookup(con->port.getSyntax()))
342 portValues.insert({port, value});
349 if (
const auto *multiPort = con->port.as_if<MultiPortSymbol>()) {
355 for (
const auto *port : llvm::reverse(multiPort->ports)) {
356 if (
auto *existingPort = moduleLowering->portsBySyntaxNode.lookup(
357 con->port.getSyntax()))
359 unsigned width = port->getType().getBitWidth();
360 auto sliceType = context.
convertType(port->getType());
363 Value slice = builder.create<moore::ExtractRefOp>(
367 if (port->direction == ArgumentDirection::In)
368 slice = builder.create<moore::ReadOp>(loc, slice);
369 portValues.insert({port, slice});
375 mlir::emitError(loc) <<
"unsupported instance port `" << con->port.name
382 SmallVector<Value> inputValues;
383 SmallVector<Value> outputValues;
384 inputValues.reserve(moduleType.getNumInputs());
385 outputValues.reserve(moduleType.getNumOutputs());
387 for (
auto &port : moduleLowering->ports) {
388 auto value = portValues.lookup(&port.ast);
389 if (port.ast.direction == ArgumentDirection::Out)
390 outputValues.push_back(value);
392 inputValues.push_back(value);
396 for (
auto [value, type] :
397 llvm::zip(inputValues, moduleType.getInputTypes()))
398 if (value.getType() != type)
400 builder.create<moore::ConversionOp>(value.getLoc(), type, value);
404 for (
const auto &hierPath : context.
hierPaths[&instNode.body])
405 if (
auto hierValue = context.
valueSymbols.lookup(hierPath.valueSym);
406 hierPath.hierName && hierPath.direction == ArgumentDirection::In)
407 inputValues.push_back(hierValue);
410 auto inputNames = builder.getArrayAttr(moduleType.getInputNames());
411 auto outputNames = builder.getArrayAttr(moduleType.getOutputNames());
412 auto inst = builder.create<moore::InstanceOp>(
413 loc, moduleType.getOutputTypes(), builder.getStringAttr(instNode.name),
415 inputNames, outputNames);
418 for (
const auto &hierPath : context.
hierPaths[&instNode.body])
419 if (hierPath.idx && hierPath.direction == ArgumentDirection::Out)
421 inst->getResult(*hierPath.idx));
424 for (
auto [lvalue, output] : llvm::zip(outputValues, inst.getOutputs())) {
427 Value rvalue = output;
428 auto dstType = cast<moore::RefType>(lvalue.getType()).getNestedType();
429 if (dstType != rvalue.getType())
430 rvalue = builder.create<moore::ConversionOp>(loc, dstType, rvalue);
431 builder.create<moore::ContinuousAssignOp>(loc, lvalue, rvalue);
438 LogicalResult visit(
const slang::ast::VariableSymbol &varNode) {
439 auto loweredType = context.
convertType(*varNode.getDeclaredType());
444 if (
const auto *init = varNode.getInitializer()) {
450 auto varOp = builder.create<moore::VariableOp>(
452 builder.getStringAttr(varNode.name), initial);
458 LogicalResult visit(
const slang::ast::NetSymbol &netNode) {
459 auto loweredType = context.
convertType(*netNode.getDeclaredType());
464 if (
const auto *init = netNode.getInitializer()) {
471 if (netkind == moore::NetKind::Interconnect ||
472 netkind == moore::NetKind::UserDefined ||
473 netkind == moore::NetKind::Unknown)
474 return mlir::emitError(loc,
"unsupported net kind `")
475 << netNode.netType.name <<
"`";
477 auto netOp = builder.create<moore::NetOp>(
479 builder.getStringAttr(netNode.name), netkind, assignment);
485 LogicalResult visit(
const slang::ast::ContinuousAssignSymbol &assignNode) {
486 if (
const auto *delay = assignNode.getDelay()) {
488 return mlir::emitError(loc,
489 "delayed continuous assignments not supported");
493 assignNode.getAssignment().as<slang::ast::AssignmentExpression>();
499 expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
503 builder.create<moore::ContinuousAssignOp>(loc, lhs, rhs);
508 LogicalResult visit(
const slang::ast::ProceduralBlockSymbol &procNode) {
509 auto procOp = builder.create<moore::ProcedureOp>(
511 OpBuilder::InsertionGuard guard(builder);
512 builder.setInsertionPointToEnd(&procOp.getBody().emplaceBlock());
516 if (builder.getBlock())
517 builder.create<moore::ReturnOp>(loc);
522 LogicalResult visit(
const slang::ast::GenerateBlockSymbol &genNode) {
523 if (!genNode.isUninstantiated) {
524 for (
auto &member : genNode.members()) {
525 if (failed(member.visit(ModuleVisitor(context, loc))))
533 LogicalResult visit(
const slang::ast::GenerateBlockArraySymbol &genArrNode) {
534 for (
const auto *member : genArrNode.entries) {
535 if (failed(member->asSymbol().visit(ModuleVisitor(context, loc))))
547 LogicalResult visit(
const slang::ast::StatementBlockSymbol &) {
552 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
557 template <
typename T>
558 LogicalResult visit(T &&node) {
559 mlir::emitError(loc,
"unsupported module member: ")
577 for (
auto *inst : root.topInstances)
584 for (
auto *unit : root.compilationUnits) {
585 for (
const auto &member : unit->members()) {
587 if (failed(member.visit(RootVisitor(*
this, loc))))
593 SmallVector<const slang::ast::InstanceSymbol *> topInstances;
594 for (
auto *inst : root.topInstances)
615 using slang::ast::ArgumentDirection;
616 using slang::ast::MultiPortSymbol;
617 using slang::ast::ParameterSymbol;
618 using slang::ast::PortSymbol;
619 using slang::ast::TypeParameterSymbol;
621 auto parameters = module->parameters;
622 bool hasModuleSame =
false;
626 for (
auto const &existingModule :
modules) {
627 if (module->getDeclaringDefinition() ==
628 existingModule.getFirst()->getDeclaringDefinition()) {
629 auto moduleParameters = existingModule.getFirst()->parameters;
630 hasModuleSame =
true;
631 for (
auto it1 = parameters.begin(), it2 = moduleParameters.begin();
632 it1 != parameters.end() && it2 != moduleParameters.end();
635 if (it1 == parameters.end() || it2 == moduleParameters.end()) {
636 hasModuleSame =
false;
639 const auto *para1 = (*it1)->symbol.as_if<ParameterSymbol>();
640 const auto *para2 = (*it2)->symbol.as_if<ParameterSymbol>();
642 if ((para1 ==
nullptr) ^ (para2 ==
nullptr)) {
643 hasModuleSame =
false;
647 if (para1 !=
nullptr) {
648 hasModuleSame = para1->getValue() == para2->getValue();
651 if (para1 ==
nullptr) {
653 (*it1)->symbol.as<TypeParameterSymbol>().getTypeAlias());
655 (*it2)->symbol.as<TypeParameterSymbol>().getTypeAlias());
656 hasModuleSame = para1Type == para2Type;
662 module = existingModule.first;
671 slot = std::make_unique<ModuleLowering>();
672 auto &lowering = *slot;
675 OpBuilder::InsertionGuard g(
builder);
680 if (module->getDefinition().definitionKind !=
681 slang::ast::DefinitionKind::Module) {
682 mlir::emitError(loc) <<
"unsupported definition: "
683 << module->getDefinition().getKindString();
688 auto block = std::make_unique<Block>();
689 SmallVector<hw::ModulePort> modulePorts;
692 unsigned int outputIdx = 0, inputIdx = 0;
693 for (
auto *symbol : module->getPortList()) {
694 auto handlePort = [&](
const PortSymbol &port) {
699 auto portName =
builder.getStringAttr(port.name);
701 if (port.direction == ArgumentDirection::Out) {
707 if (port.direction != ArgumentDirection::In)
710 arg = block->addArgument(type, portLoc);
713 lowering.ports.push_back({port, portLoc, arg});
717 if (
const auto *port = symbol->as_if<PortSymbol>()) {
718 if (failed(handlePort(*port)))
720 }
else if (
const auto *multiPort = symbol->as_if<MultiPortSymbol>()) {
721 for (
auto *port : multiPort->ports)
722 if (failed(handlePort(*port)))
726 <<
"unsupported module port `" << symbol->name <<
"` ("
733 for (
auto &hierPath :
hierPaths[module]) {
734 auto hierType =
convertType(hierPath.valueSym->getType());
738 if (
auto hierName = hierPath.hierName) {
741 if (hierPath.direction == ArgumentDirection::Out) {
742 hierPath.idx = outputIdx++;
745 hierPath.idx = inputIdx++;
748 block->addArgument(hierType, hierLoc);
760 builder.setInsertionPoint(it->second);
764 builder.create<moore::SVModuleOp>(loc, module->name, moduleType);
766 moduleOp.getBodyRegion().push_back(block.release());
767 lowering.op = moduleOp;
777 for (
const auto &port : lowering.ports)
778 lowering.portsBySyntaxNode.insert({port.ast.getSyntax(), &port.ast});
787 auto &lowering = *
modules[module];
788 OpBuilder::InsertionGuard g(
builder);
789 builder.setInsertionPointToEnd(lowering.op.getBody());
797 if (hierPath.direction == slang::ast::ArgumentDirection::In && hierPath.idx)
799 lowering.op.getBody()->getArgument(*hierPath.idx));
802 for (
auto &member : module->members()) {
804 if (failed(member.visit(ModuleVisitor(*
this, loc))))
811 SmallVector<Value> outputs;
812 for (
auto &port : lowering.ports) {
814 if (
auto *expr = port.ast.getInternalExpr()) {
816 }
else if (port.ast.internalSymbol) {
817 if (
const auto *sym =
818 port.ast.internalSymbol->as_if<slang::ast::ValueSymbol>())
822 return mlir::emitError(port.loc,
"unsupported port: `")
824 <<
"` does not map to an internal symbol or expression";
827 if (port.ast.direction == slang::ast::ArgumentDirection::Out) {
828 if (isa<moore::RefType>(value.getType()))
829 value =
builder.create<moore::ReadOp>(value.getLoc(), value);
830 outputs.push_back(value);
836 Value portArg = port.arg;
837 if (port.ast.direction != slang::ast::ArgumentDirection::In)
838 portArg =
builder.create<moore::ReadOp>(port.loc, port.arg);
839 builder.create<moore::ContinuousAssignOp>(port.loc, value, portArg);
845 if (
auto hierValue =
valueSymbols.lookup(hierPath.valueSym))
846 if (hierPath.direction == slang::ast::ArgumentDirection::Out)
847 outputs.push_back(hierValue);
849 builder.create<moore::OutputOp>(lowering.op.getLoc(), outputs);
856 OpBuilder::InsertionGuard g(
builder);
859 for (
auto &member : package.members()) {
861 if (failed(member.visit(PackageVisitor(*
this, loc))))
871 using slang::ast::ArgumentDirection;
878 return lowering.get();
880 lowering = std::make_unique<FunctionLowering>();
885 OpBuilder::InsertionGuard g(
builder);
890 builder.setInsertionPoint(it->second);
893 if (subroutine.thisVar) {
894 mlir::emitError(loc) <<
"unsupported class method";
899 SmallVector<Type> inputTypes;
900 SmallVector<Type, 1> outputTypes;
902 for (
const auto *arg : subroutine.getArguments()) {
903 auto type = cast<moore::UnpackedType>(
convertType(arg->getType()));
906 if (arg->direction == ArgumentDirection::In) {
907 inputTypes.push_back(type);
913 if (!subroutine.getReturnType().isVoid()) {
914 auto type =
convertType(subroutine.getReturnType());
917 outputTypes.push_back(type);
924 SmallString<64> funcName;
926 funcName += subroutine.name;
929 auto funcOp =
builder.create<mlir::func::FuncOp>(loc, funcName, funcType);
930 SymbolTable::setSymbolVisibility(funcOp, SymbolTable::Visibility::Private);
932 lowering->op = funcOp;
938 return lowering.get();
951 SmallVector<moore::VariableOp> argVariables;
952 auto &block = lowering->op.getBody().emplaceBlock();
953 for (
auto [astArg, type] :
954 llvm::zip(subroutine.getArguments(),
955 lowering->op.getFunctionType().getInputs())) {
957 auto blockArg = block.addArgument(type, loc);
959 if (isa<moore::RefType>(type)) {
963 OpBuilder::InsertionGuard g(
builder);
964 builder.setInsertionPointToEnd(&block);
966 auto shadowArg =
builder.create<moore::VariableOp>(
968 StringAttr{}, blockArg);
970 argVariables.push_back(shadowArg);
975 OpBuilder::InsertionGuard g(
builder);
976 builder.setInsertionPointToEnd(&block);
979 if (subroutine.returnValVar) {
980 auto type =
convertType(*subroutine.returnValVar->getDeclaredType());
983 returnVar =
builder.create<moore::VariableOp>(
984 lowering->op.getLoc(),
987 valueSymbols.insert(subroutine.returnValVar, returnVar);
996 if (returnVar && !subroutine.getReturnType().isVoid()) {
997 Value read =
builder.create<moore::ReadOp>(returnVar.getLoc(), returnVar);
998 builder.create<mlir::func::ReturnOp>(lowering->op.getLoc(), read);
1000 builder.create<mlir::func::ReturnOp>(lowering->op.getLoc(), ValueRange{});
1003 if (returnVar && returnVar.use_empty())
1004 returnVar.getDefiningOp()->erase();
1006 for (
auto var : argVariables) {
1007 if (llvm::all_of(var->getUsers(),
1008 [](
auto *user) {
return isa<moore::ReadOp>(user); })) {
1009 for (
auto *user : llvm::make_early_inc_range(var->getUsers())) {
1010 user->getResult(0).replaceAllUsesWith(var.getInitial());
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={})
LogicalResult traverseInstanceBody(const slang::ast::Symbol &symbol)
mlir::ModuleOp intoModuleOp
SymbolTable symbolTable
A symbol table of the MLIR module we are emitting into.
DenseMap< const slang::ast::InstanceBodySymbol *, SmallVector< HierPathInfo > > hierPaths
Collect all hierarchical names used for the per module/instance.
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.