10#include "slang/ast/Compilation.h"
11#include "slang/ast/symbols/ClassSymbols.h"
12#include "llvm/ADT/ScopeExit.h"
15using namespace ImportVerilog;
22 SmallString<64> &prefix) {
23 if (symbol.kind != slang::ast::SymbolKind::Package)
26 if (!symbol.name.empty()) {
27 prefix += symbol.name;
44 BaseVisitor(
Context &context, Location loc)
45 : context(context), loc(loc), builder(context.builder) {}
48 LogicalResult visit(
const slang::ast::EmptyMemberSymbol &) {
54 LogicalResult visit(
const slang::ast::TransparentMemberSymbol &) {
59 LogicalResult visit(
const slang::ast::ClassType &classdecl) {
69 LogicalResult visit(
const slang::ast::GenericClassDefSymbol &) {
74 LogicalResult visit(
const slang::ast::TypeAliasType &) {
return success(); }
75 LogicalResult visit(
const slang::ast::ForwardingTypedefSymbol &) {
80 LogicalResult visit(
const slang::ast::ExplicitImportSymbol &) {
83 LogicalResult visit(
const slang::ast::WildcardImportSymbol &) {
88 LogicalResult visit(
const slang::ast::TypeParameterSymbol &) {
93 LogicalResult visit(
const slang::ast::ElabSystemTaskSymbol &) {
98 LogicalResult visit(
const slang::ast::ParameterSymbol ¶m) {
99 visitParameter(param);
103 LogicalResult visit(
const slang::ast::SpecparamSymbol ¶m) {
104 visitParameter(param);
108 template <
class Node>
109 void visitParameter(
const Node ¶m) {
119 if (builder.getInsertionBlock()->getParentOp() == context.
intoModuleOp) {
126 SmallString<64> paramName;
128 paramName += param.name;
130 debug::VariableOp::create(builder, loc, builder.getStringAttr(paramName),
141struct RootVisitor :
public BaseVisitor {
142 using BaseVisitor::BaseVisitor;
143 using BaseVisitor::visit;
146 LogicalResult visit(
const slang::ast::PackageSymbol &package) {
147 return context.convertPackage(package);
151 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
152 if (!
context.declareFunction(subroutine))
158 LogicalResult visit(
const slang::ast::VariableSymbol &var) {
159 return context.convertGlobalVariable(var);
163 template <
typename T>
164 LogicalResult visit(T &&node) {
165 mlir::emitError(loc,
"unsupported construct: ")
166 << slang::ast::toString(node.kind);
177struct PackageVisitor :
public BaseVisitor {
178 using BaseVisitor::BaseVisitor;
179 using BaseVisitor::visit;
182 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
183 if (!
context.declareFunction(subroutine))
189 LogicalResult visit(
const slang::ast::VariableSymbol &var) {
190 return context.convertGlobalVariable(var);
194 template <
typename T>
195 LogicalResult visit(T &&node) {
196 mlir::emitError(loc,
"unsupported package member: ")
197 << slang::ast::toString(node.kind);
207static moore::ProcedureKind
210 case slang::ast::ProceduralBlockKind::Always:
211 return moore::ProcedureKind::Always;
212 case slang::ast::ProceduralBlockKind::AlwaysComb:
213 return moore::ProcedureKind::AlwaysComb;
214 case slang::ast::ProceduralBlockKind::AlwaysLatch:
215 return moore::ProcedureKind::AlwaysLatch;
216 case slang::ast::ProceduralBlockKind::AlwaysFF:
217 return moore::ProcedureKind::AlwaysFF;
218 case slang::ast::ProceduralBlockKind::Initial:
219 return moore::ProcedureKind::Initial;
220 case slang::ast::ProceduralBlockKind::Final:
221 return moore::ProcedureKind::Final;
223 llvm_unreachable(
"all procedure kinds handled");
228 case slang::ast::NetType::Supply0:
229 return moore::NetKind::Supply0;
230 case slang::ast::NetType::Supply1:
231 return moore::NetKind::Supply1;
232 case slang::ast::NetType::Tri:
233 return moore::NetKind::Tri;
234 case slang::ast::NetType::TriAnd:
235 return moore::NetKind::TriAnd;
236 case slang::ast::NetType::TriOr:
237 return moore::NetKind::TriOr;
238 case slang::ast::NetType::TriReg:
239 return moore::NetKind::TriReg;
240 case slang::ast::NetType::Tri0:
241 return moore::NetKind::Tri0;
242 case slang::ast::NetType::Tri1:
243 return moore::NetKind::Tri1;
244 case slang::ast::NetType::UWire:
245 return moore::NetKind::UWire;
246 case slang::ast::NetType::Wire:
247 return moore::NetKind::Wire;
248 case slang::ast::NetType::WAnd:
249 return moore::NetKind::WAnd;
250 case slang::ast::NetType::WOr:
251 return moore::NetKind::WOr;
252 case slang::ast::NetType::Interconnect:
253 return moore::NetKind::Interconnect;
254 case slang::ast::NetType::UserDefined:
255 return moore::NetKind::UserDefined;
256 case slang::ast::NetType::Unknown:
257 return moore::NetKind::Unknown;
259 llvm_unreachable(
"all net kinds handled");
263struct ModuleVisitor :
public BaseVisitor {
264 using BaseVisitor::visit;
268 StringRef blockNamePrefix;
270 ModuleVisitor(
Context &
context, Location loc, StringRef blockNamePrefix =
"")
271 : BaseVisitor(
context, loc), blockNamePrefix(blockNamePrefix) {}
274 LogicalResult visit(
const slang::ast::PortSymbol &) {
return success(); }
275 LogicalResult visit(
const slang::ast::MultiPortSymbol &) {
return success(); }
276 LogicalResult visit(
const slang::ast::InterfacePortSymbol &) {
281 LogicalResult visit(
const slang::ast::GenvarSymbol &genvarNode) {
286 LogicalResult visit(
const slang::ast::DefParamSymbol &) {
return success(); }
290 LogicalResult visit(
const slang::ast::TypeParameterSymbol &) {
298 expandInterfaceInstance(
const slang::ast::InstanceSymbol &instNode) {
299 auto prefix = (Twine(blockNamePrefix) + instNode.name +
"_").str();
300 auto lowering = std::make_unique<InterfaceLowering>();
301 Context::ValueSymbolScope scope(
context.valueSymbols);
303 auto recordMember = [&](
const slang::ast::Symbol &sym,
304 Value value) ->
void {
305 lowering->expandedMembers[&sym] = value;
306 auto nameAttr = builder.getStringAttr(sym.name);
307 lowering->expandedMembersByName[nameAttr] = value;
308 if (
auto *valueSym = sym.as_if<slang::ast::ValueSymbol>())
309 context.valueSymbols.insert(valueSym, value);
312 for (
const auto &member : instNode.body.members()) {
314 if (
const auto *nestedInst = member.as_if<slang::ast::InstanceSymbol>()) {
315 if (nestedInst->body.getDefinition().definitionKind ==
316 slang::ast::DefinitionKind::Interface)
317 return mlir::emitError(loc)
318 <<
"nested interface instances are not supported: `"
319 << nestedInst->name <<
"` inside `" << instNode.name <<
"`";
322 if (
const auto *var = member.as_if<slang::ast::VariableSymbol>()) {
323 auto loweredType =
context.convertType(*var->getDeclaredType());
326 auto varOp = moore::VariableOp::create(
328 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
329 builder.getStringAttr(Twine(prefix) + StringRef(var->name)),
331 recordMember(*var, varOp);
335 if (
const auto *net = member.as_if<slang::ast::NetSymbol>()) {
336 auto loweredType =
context.convertType(*net->getDeclaredType());
340 if (netKind == moore::NetKind::Interconnect ||
341 netKind == moore::NetKind::UserDefined ||
342 netKind == moore::NetKind::Unknown)
343 return mlir::emitError(loc,
"unsupported net kind `")
344 << net->netType.name <<
"`";
345 auto netOp = moore::NetOp::create(
347 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
348 builder.getStringAttr(Twine(prefix) + StringRef(net->name)),
350 recordMember(*net, netOp);
359 for (
const auto *con : instNode.getPortConnections()) {
360 const auto *expr = con->getExpression();
361 const auto *port = con->port.as_if<slang::ast::PortSymbol>();
369 Value lvalue =
context.convertLvalueExpression(*expr);
373 recordMember(*port, lvalue);
374 if (port->internalSymbol) {
375 recordMember(*port->internalSymbol, lvalue);
381 for (
const auto &member : instNode.body.members()) {
382 switch (member.kind) {
383 case slang::ast::SymbolKind::ContinuousAssign:
384 case slang::ast::SymbolKind::ProceduralBlock:
385 case slang::ast::SymbolKind::StatementBlock:
390 auto memberLoc =
context.convertLocation(member.location);
391 if (failed(member.visit(ModuleVisitor(
context, memberLoc, prefix))))
393 if (failed(
context.flushPendingMonitors()))
397 context.interfaceInstanceStorage.push_back(std::move(lowering));
398 context.interfaceInstances.insert(
399 &instNode,
context.interfaceInstanceStorage.back().get());
404 LogicalResult visit(
const slang::ast::InstanceSymbol &instNode) {
405 using slang::ast::ArgumentDirection;
406 using slang::ast::AssignmentExpression;
407 using slang::ast::MultiPortSymbol;
408 using slang::ast::PortSymbol;
412 auto defKind = instNode.body.getDefinition().definitionKind;
413 if (defKind == slang::ast::DefinitionKind::Interface)
414 return expandInterfaceInstance(instNode);
416 auto *moduleLowering =
context.convertModuleHeader(&instNode.body);
419 auto module = moduleLowering->op;
420 auto moduleType =
module.getModuleType();
423 SymbolTable::setSymbolVisibility(module, SymbolTable::Visibility::Private);
430 portValues.reserve(moduleType.getNumPorts());
434 const slang::ast::InstanceSymbol *>
437 for (
const auto *con : instNode.getPortConnections()) {
438 const auto *expr = con->getExpression();
443 auto *port = con->port.as_if<PortSymbol>();
444 if (
auto *existingPort =
445 moduleLowering->portsBySyntaxNode.lookup(port->getSyntax()))
448 switch (port->direction) {
449 case ArgumentDirection::In: {
450 auto refType = moore::RefType::get(
451 cast<moore::UnpackedType>(
context.convertType(port->getType())));
453 if (
const auto *net =
454 port->internalSymbol->as_if<slang::ast::NetSymbol>()) {
455 auto netOp = moore::NetOp::create(
456 builder, loc, refType,
457 StringAttr::get(builder.getContext(), net->name),
459 auto readOp = moore::ReadOp::create(builder, loc, netOp);
460 portValues.insert({port, readOp});
461 }
else if (
const auto *var =
463 ->as_if<slang::ast::VariableSymbol>()) {
464 auto varOp = moore::VariableOp::create(
465 builder, loc, refType,
466 StringAttr::get(builder.getContext(), var->name),
nullptr);
467 auto readOp = moore::ReadOp::create(builder, loc, varOp);
468 portValues.insert({port, readOp});
470 return mlir::emitError(loc)
471 <<
"unsupported internal symbol for unconnected port `"
472 << port->name <<
"`";
479 case ArgumentDirection::Out:
482 case ArgumentDirection::InOut:
483 case ArgumentDirection::Ref: {
484 auto refType = moore::RefType::get(
485 cast<moore::UnpackedType>(
context.convertType(port->getType())));
487 if (
const auto *net =
488 port->internalSymbol->as_if<slang::ast::NetSymbol>()) {
489 auto netOp = moore::NetOp::create(
490 builder, loc, refType,
491 StringAttr::get(builder.getContext(), net->name),
493 portValues.insert({port, netOp});
494 }
else if (
const auto *var =
496 ->as_if<slang::ast::VariableSymbol>()) {
497 auto varOp = moore::VariableOp::create(
498 builder, loc, refType,
499 StringAttr::get(builder.getContext(), var->name),
nullptr);
500 portValues.insert({port, varOp});
502 return mlir::emitError(loc)
503 <<
"unsupported internal symbol for unconnected port `"
504 << port->name <<
"`";
513 if (
const auto *assign = expr->as_if<AssignmentExpression>())
514 expr = &assign->left();
519 if (
auto *port = con->port.as_if<PortSymbol>()) {
521 auto value = (port->direction == ArgumentDirection::In)
522 ?
context.convertRvalueExpression(*expr)
523 :
context.convertLvalueExpression(*expr);
526 if (
auto *existingPort =
527 moduleLowering->portsBySyntaxNode.lookup(con->port.getSyntax()))
529 portValues.insert({port, value});
536 if (
const auto *multiPort = con->port.as_if<MultiPortSymbol>()) {
538 auto value =
context.convertLvalueExpression(*expr);
542 for (
const auto *port :
llvm::reverse(multiPort->ports)) {
543 if (
auto *existingPort = moduleLowering->portsBySyntaxNode.lookup(
544 con->port.getSyntax()))
546 unsigned width = port->getType().getBitWidth();
547 auto sliceType =
context.convertType(port->getType());
550 Value slice = moore::ExtractRefOp::create(
552 moore::RefType::get(cast<moore::UnpackedType>(sliceType)), value,
555 if (port->direction == ArgumentDirection::In)
556 slice = moore::ReadOp::create(builder, loc, slice);
557 portValues.insert({port, slice});
565 if (
const auto *ifacePort =
566 con->port.as_if<slang::ast::InterfacePortSymbol>()) {
567 auto ifaceConn = con->getIfaceConn();
568 const auto *connInst =
569 ifaceConn.first->as_if<slang::ast::InstanceSymbol>();
571 ifaceConnMap[ifacePort] = connInst;
575 mlir::emitError(loc) <<
"unsupported instance port `" << con->port.name
576 <<
"` (" << slang::ast::toString(con->port.kind)
582 SmallVector<Value> inputValues;
583 SmallVector<Value> outputValues;
584 inputValues.reserve(moduleType.getNumInputs());
585 outputValues.reserve(moduleType.getNumOutputs());
587 for (
auto &port : moduleLowering->ports) {
588 auto value = portValues.lookup(&port.ast);
589 if (port.ast.direction == ArgumentDirection::Out)
590 outputValues.push_back(value);
592 inputValues.push_back(value);
598 for (
auto &fp : moduleLowering->ifacePorts) {
599 if (!fp.bodySym || !fp.origin)
602 auto it = ifaceConnMap.find(fp.origin);
603 if (it == ifaceConnMap.end()) {
605 <<
"no interface connection for port `" << fp.name <<
"`";
608 const auto *connInst = it->second;
610 auto *ifaceLowering =
context.interfaceInstances.lookup(connInst);
611 if (!ifaceLowering) {
613 <<
"interface instance `" << connInst->name <<
"` was not expanded";
617 auto valIt = ifaceLowering->expandedMembers.find(fp.bodySym);
618 if (valIt == ifaceLowering->expandedMembers.end()) {
620 <<
"unresolved interface port signal `" << fp.name <<
"`";
623 Value val = valIt->second;
625 outputValues.push_back(val);
629 if (isa<moore::RefType>(val.getType()) && !isa<moore::RefType>(fp.type))
630 val = moore::ReadOp::create(builder, loc, val);
631 inputValues.push_back(val);
636 for (
auto [value, type] :
637 llvm::zip(inputValues, moduleType.getInputTypes())) {
639 value =
context.materializeConversion(type, value,
false, value.getLoc());
641 return mlir::emitError(loc) <<
"unsupported port";
646 for (
const auto &hierPath :
context.hierPaths[&instNode.body])
647 if (auto hierValue =
context.valueSymbols.lookup(hierPath.valueSym);
648 hierPath.hierName && hierPath.direction == ArgumentDirection::In)
649 inputValues.push_back(hierValue);
652 for (
auto value : inputValues)
654 return
mlir::emitError(loc) <<
"unsupported port";
657 auto inputNames = builder.getArrayAttr(moduleType.getInputNames());
658 auto outputNames = builder.getArrayAttr(moduleType.getOutputNames());
659 auto inst = moore::InstanceOp::create(
660 builder, loc, moduleType.getOutputTypes(),
661 builder.getStringAttr(Twine(blockNamePrefix) + instNode.name),
662 FlatSymbolRefAttr::get(module.getSymNameAttr()), inputValues,
663 inputNames, outputNames);
666 for (
const auto &hierPath :
context.hierPaths[&instNode.body])
667 if (hierPath.idx && hierPath.direction == ArgumentDirection::
Out)
668 context.valueSymbols.insert(hierPath.valueSym,
669 inst->getResult(*hierPath.idx));
672 for (
auto [lvalue, output] :
llvm::zip(outputValues, inst.getOutputs())) {
675 Value rvalue = output;
676 auto dstType = cast<moore::RefType>(lvalue.getType()).getNestedType();
678 rvalue =
context.materializeConversion(dstType, rvalue,
false, loc);
679 moore::ContinuousAssignOp::create(builder, loc, lvalue, rvalue);
686 LogicalResult visit(
const slang::ast::VariableSymbol &varNode) {
687 auto loweredType =
context.convertType(*varNode.getDeclaredType());
692 if (
const auto *init = varNode.getInitializer()) {
693 initial =
context.convertRvalueExpression(*init, loweredType);
698 auto varOp = moore::VariableOp::create(
700 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
701 builder.getStringAttr(Twine(blockNamePrefix) + varNode.name), initial);
702 context.valueSymbols.insert(&varNode, varOp);
703 const auto &canonTy = varNode.getType().getCanonicalType();
704 if (
const auto *vi = canonTy.as_if<slang::ast::VirtualInterfaceType>())
705 if (failed(
context.registerVirtualInterfaceMembers(varNode, *vi, loc)))
711 LogicalResult visit(
const slang::ast::NetSymbol &netNode) {
712 auto loweredType =
context.convertType(*netNode.getDeclaredType());
717 if (
const auto *init = netNode.getInitializer()) {
718 assignment =
context.convertRvalueExpression(*init, loweredType);
724 if (netkind == moore::NetKind::Interconnect ||
725 netkind == moore::NetKind::UserDefined ||
726 netkind == moore::NetKind::Unknown)
727 return mlir::emitError(loc,
"unsupported net kind `")
728 << netNode.netType.name <<
"`";
730 auto netOp = moore::NetOp::create(
732 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
733 builder.getStringAttr(Twine(blockNamePrefix) + netNode.name), netkind,
735 context.valueSymbols.insert(&netNode, netOp);
740 LogicalResult visit(
const slang::ast::ContinuousAssignSymbol &assignNode) {
742 assignNode.getAssignment().as<slang::ast::AssignmentExpression>();
743 auto lhs =
context.convertLvalueExpression(expr.left());
747 auto rhs =
context.convertRvalueExpression(
748 expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
753 if (
auto *timingCtrl = assignNode.getDelay()) {
754 if (
auto *ctrl = timingCtrl->as_if<slang::ast::DelayControl>()) {
755 auto delay =
context.convertRvalueExpression(
756 ctrl->expr, moore::TimeType::get(builder.getContext()));
759 moore::DelayedContinuousAssignOp::create(builder, loc, lhs, rhs, delay);
762 mlir::emitError(loc) <<
"unsupported delay with rise/fall/turn-off";
767 moore::ContinuousAssignOp::create(builder, loc, lhs, rhs);
772 LogicalResult convertProcedure(moore::ProcedureKind kind,
773 const slang::ast::Statement &body) {
774 if (body.as_if<slang::ast::ConcurrentAssertionStatement>())
775 return context.convertStatement(body);
776 auto procOp = moore::ProcedureOp::create(builder, loc, kind);
777 OpBuilder::InsertionGuard guard(builder);
778 builder.setInsertionPointToEnd(&procOp.getBody().emplaceBlock());
779 Context::ValueSymbolScope scope(
context.valueSymbols);
780 Context::VirtualInterfaceMemberScope vifMemberScope(
782 if (failed(
context.convertStatement(body)))
784 if (builder.getBlock())
785 moore::ReturnOp::create(builder, loc);
789 LogicalResult visit(
const slang::ast::ProceduralBlockSymbol &procNode) {
792 if (
context.options.lowerAlwaysAtStarAsComb) {
793 auto *stmt = procNode.getBody().as_if<slang::ast::TimedStatement>();
794 if (procNode.procedureKind == slang::ast::ProceduralBlockKind::Always &&
796 stmt->timing.kind == slang::ast::TimingControlKind::ImplicitEvent)
797 return convertProcedure(moore::ProcedureKind::AlwaysComb, stmt->stmt);
805 LogicalResult visit(
const slang::ast::GenerateBlockSymbol &genNode) {
807 if (genNode.isUninstantiated)
811 SmallString<64> prefix = blockNamePrefix;
812 if (!genNode.name.empty() ||
813 genNode.getParentScope()->asSymbol().kind !=
814 slang::ast::SymbolKind::GenerateBlockArray) {
815 prefix += genNode.getExternalName();
820 for (
auto &member : genNode.members())
821 if (failed(member.visit(ModuleVisitor(
context, loc, prefix))))
827 LogicalResult visit(
const slang::ast::GenerateBlockArraySymbol &genArrNode) {
830 SmallString<64> prefix = blockNamePrefix;
831 prefix += genArrNode.getExternalName();
833 auto prefixBaseLen = prefix.size();
836 for (
const auto *entry : genArrNode.entries) {
838 prefix.resize(prefixBaseLen);
839 if (entry->arrayIndex)
840 prefix += entry->arrayIndex->toString();
842 Twine(entry->constructIndex).toVector(prefix);
846 if (failed(entry->asSymbol().visit(ModuleVisitor(
context, loc, prefix))))
858 LogicalResult visit(
const slang::ast::StatementBlockSymbol &) {
864 LogicalResult visit(
const slang::ast::SequenceSymbol &seqNode) {
870 LogicalResult visit(
const slang::ast::PropertySymbol &propNode) {
875 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
876 if (!
context.declareFunction(subroutine))
882 LogicalResult visit(
const slang::ast::PrimitiveInstanceSymbol &prim) {
883 return context.convertPrimitiveInstance(prim);
887 template <
typename T>
888 LogicalResult visit(T &&node) {
889 mlir::emitError(loc,
"unsupported module member: ")
890 << slang::ast::toString(node.kind);
902LogicalResult Context::convertCompilation() {
908 timeScale = root.getTimeScale().value_or(slang::TimeScale());
909 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
917 for (
auto *inst : root.topInstances)
923 for (
auto *unit : root.compilationUnits) {
924 for (
const auto &member : unit->members()) {
926 if (failed(member.visit(RootVisitor(*
this, loc))))
934 SmallVector<const slang::ast::InstanceSymbol *> topInstances;
935 for (
auto *inst : root.topInstances)
936 if (inst->body.getDefinition().definitionKind !=
937 slang::ast::DefinitionKind::Interface)
943 auto *
module = moduleWorklist.front();
951 SmallVector<const slang::ast::ClassType *, 16> classMethodWorklist;
952 classMethodWorklist.reserve(
classes.size());
954 classMethodWorklist.push_back(kv.first);
956 for (
auto *inst : classMethodWorklist) {
975 auto &block = varOp.getInitRegion().emplaceBlock();
976 OpBuilder::InsertionGuard guard(
builder);
977 builder.setInsertionPointToEnd(&block);
982 moore::YieldOp::create(
builder, varOp.getLoc(), value);
995 using slang::ast::ArgumentDirection;
996 using slang::ast::MultiPortSymbol;
997 using slang::ast::ParameterSymbol;
998 using slang::ast::PortSymbol;
999 using slang::ast::TypeParameterSymbol;
1004 timeScale =
module->getTimeScale().value_or(slang::TimeScale());
1005 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
1007 auto parameters =
module->getParameters();
1008 bool hasModuleSame =
false;
1012 for (
auto const &existingModule :
modules) {
1013 if (module->getDeclaringDefinition() ==
1014 existingModule.getFirst()->getDeclaringDefinition()) {
1015 auto moduleParameters = existingModule.getFirst()->getParameters();
1016 hasModuleSame =
true;
1017 for (
auto it1 = parameters.begin(), it2 = moduleParameters.begin();
1018 it1 != parameters.end() && it2 != moduleParameters.end();
1021 if (it1 == parameters.end() || it2 == moduleParameters.end()) {
1022 hasModuleSame =
false;
1025 const auto *para1 = (*it1)->symbol.as_if<ParameterSymbol>();
1026 const auto *para2 = (*it2)->symbol.as_if<ParameterSymbol>();
1028 if ((para1 ==
nullptr) ^ (para2 ==
nullptr)) {
1029 hasModuleSame =
false;
1033 if (para1 !=
nullptr) {
1034 hasModuleSame = para1->getValue() == para2->getValue();
1037 if (para1 ==
nullptr) {
1039 (*it1)->symbol.as<TypeParameterSymbol>().getTypeAlias());
1041 (*it2)->symbol.as<TypeParameterSymbol>().getTypeAlias());
1042 hasModuleSame = para1Type == para2Type;
1047 if (hasModuleSame) {
1048 module = existingModule.first;
1057 slot = std::make_unique<ModuleLowering>();
1058 auto &lowering = *slot;
1061 OpBuilder::InsertionGuard g(
builder);
1066 auto kind =
module->getDefinition().definitionKind;
1067 if (kind != slang::ast::DefinitionKind::Module &&
1068 kind != slang::ast::DefinitionKind::Program) {
1069 mlir::emitError(loc) <<
"unsupported definition: "
1070 <<
module->getDefinition().getKindString();
1075 auto block = std::make_unique<Block>();
1076 SmallVector<hw::ModulePort> modulePorts;
1079 unsigned int outputIdx = 0, inputIdx = 0;
1080 for (
auto *symbol :
module->getPortList()) {
1081 auto handlePort = [&](const PortSymbol &port) {
1082 auto portLoc = convertLocation(port.location);
1086 auto portName =
builder.getStringAttr(port.name);
1088 if (port.direction == ArgumentDirection::Out) {
1094 if (port.direction != ArgumentDirection::In)
1095 type = moore::RefType::get(cast<moore::UnpackedType>(type));
1097 arg = block->addArgument(type, portLoc);
1100 lowering.ports.push_back({port, portLoc, arg});
1107 auto handleIfacePort = [&](
const slang::ast::InterfacePortSymbol
1110 auto [connSym, modportSym] = ifacePort.getConnection();
1111 const auto *ifaceInst =
1112 connSym ? connSym->as_if<slang::ast::InstanceSymbol>() : nullptr;
1113 auto portPrefix = (Twine(ifacePort.name) +
"_").str();
1117 for (
const auto &member : modportSym->members()) {
1118 const auto *mpp = member.as_if<slang::ast::ModportPortSymbol>();
1125 builder.getStringAttr(Twine(portPrefix) + StringRef(mpp->name));
1128 if (mpp->direction == ArgumentDirection::Out) {
1130 modulePorts.push_back({name, type, dir});
1134 if (mpp->direction != ArgumentDirection::In)
1135 type = moore::RefType::get(cast<moore::UnpackedType>(type));
1136 modulePorts.push_back({name, type, dir});
1137 arg = block->addArgument(type, portLoc);
1140 lowering.ifacePorts.push_back({name, dir, type, portLoc, arg,
1141 &ifacePort, mpp->internalSymbol,
1147 const auto *instSym = connSym->as_if<slang::ast::InstanceSymbol>();
1149 mlir::emitError(portLoc)
1150 <<
"unsupported interface port connection for `" << ifacePort.name
1154 for (
const auto &member : instSym->body.members()) {
1155 const slang::ast::Type *slangType =
nullptr;
1156 const slang::ast::Symbol *bodySym =
nullptr;
1157 if (
const auto *var = member.as_if<slang::ast::VariableSymbol>()) {
1158 slangType = &var->getType();
1160 }
else if (
const auto *net = member.as_if<slang::ast::NetSymbol>()) {
1161 slangType = &net->getType();
1169 auto name = builder.getStringAttr(Twine(portPrefix) +
1170 StringRef(bodySym->name));
1171 auto refType = moore::RefType::get(cast<moore::UnpackedType>(type));
1173 auto arg = block->addArgument(refType, portLoc);
1176 portLoc, arg, &ifacePort, bodySym,
1183 if (
const auto *port = symbol->as_if<PortSymbol>()) {
1184 if (failed(handlePort(*port)))
1186 }
else if (
const auto *multiPort = symbol->as_if<MultiPortSymbol>()) {
1187 for (
auto *port : multiPort->ports)
1188 if (failed(handlePort(*port)))
1190 }
else if (
const auto *ifacePort =
1191 symbol->as_if<slang::ast::InterfacePortSymbol>()) {
1192 if (failed(handleIfacePort(*ifacePort)))
1196 <<
"unsupported module port `" << symbol->name <<
"` ("
1197 << slang::ast::toString(symbol->kind) <<
")";
1203 for (
auto &hierPath : hierPaths[module]) {
1204 auto hierType =
convertType(hierPath.valueSym->getType());
1208 if (
auto hierName = hierPath.hierName) {
1210 hierType = moore::RefType::get(cast<moore::UnpackedType>(hierType));
1211 if (hierPath.direction == ArgumentDirection::Out) {
1212 hierPath.idx = outputIdx++;
1215 hierPath.idx = inputIdx++;
1218 block->addArgument(hierType, hierLoc);
1222 auto moduleType = hw::ModuleType::get(getContext(), modulePorts);
1227 auto it = orderedRootOps.upper_bound(key);
1228 if (it == orderedRootOps.end())
1229 builder.setInsertionPointToEnd(intoModuleOp.getBody());
1231 builder.setInsertionPoint(it->second);
1235 moore::SVModuleOp::create(builder, loc, module->name, moduleType);
1236 orderedRootOps.insert(it, {key, moduleOp});
1237 moduleOp.getBodyRegion().push_back(block.release());
1238 lowering.op = moduleOp;
1242 symbolTable.insert(moduleOp);
1245 moduleWorklist.push(module);
1248 for (
const auto &port : lowering.ports)
1249 lowering.portsBySyntaxNode.insert({port.ast.getSyntax(), &port.ast});
1258 auto &lowering = *
modules[module];
1259 OpBuilder::InsertionGuard g(
builder);
1260 builder.setInsertionPointToEnd(lowering.op.getBody());
1269 timeScale =
module->getTimeScale().value_or(slang::TimeScale());
1270 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
1275 for (
auto &hierPath :
hierPaths[module])
1276 if (hierPath.direction == slang::ast::ArgumentDirection::In && hierPath.idx)
1278 lowering.op.getBody()->getArgument(*hierPath.idx));
1284 DenseMap<const slang::ast::InstanceSymbol *, InterfaceLowering *>
1287 auto getIfacePortLowering =
1293 if (
auto it = ifacePortLowerings.find(ifaceInst);
1294 it != ifacePortLowerings.end())
1297 auto lowering = std::make_unique<InterfaceLowering>();
1301 ifacePortLowerings.try_emplace(ifaceInst, ptr);
1305 for (
auto &fp : lowering.ifacePorts) {
1308 auto *valueSym = fp.bodySym->as_if<slang::ast::ValueSymbol>();
1316 auto varOp = moore::VariableOp::create(
1318 moore::RefType::get(cast<moore::UnpackedType>(fp.type)), fp.name,
1325 if (!fp.ifaceInstance)
1328 auto *ifaceLowering = getIfacePortLowering(fp.ifaceInstance);
1331 ifaceLowering->expandedMembers[fp.bodySym] = val;
1333 ->expandedMembersByName[
builder.getStringAttr(fp.bodySym->name)] =
1339 for (
auto &member :
module->members()) {
1340 auto loc = convertLocation(member.location);
1341 if (failed(member.visit(ModuleVisitor(*
this, loc))))
1352 SmallVector<Value> outputs;
1353 for (
auto &port : lowering.ports) {
1355 if (
auto *expr = port.ast.getInternalExpr()) {
1356 value = convertLvalueExpression(*expr);
1357 }
else if (port.ast.internalSymbol) {
1358 if (
const auto *sym =
1359 port.ast.internalSymbol->as_if<slang::ast::ValueSymbol>())
1360 value = valueSymbols.lookup(sym);
1363 return mlir::emitError(port.loc,
"unsupported port: `")
1365 <<
"` does not map to an internal symbol or expression";
1368 if (port.ast.direction == slang::ast::ArgumentDirection::Out) {
1369 if (isa<moore::RefType>(value.getType()))
1370 value = moore::ReadOp::create(builder, value.getLoc(), value);
1371 outputs.push_back(value);
1377 Value portArg = port.arg;
1378 if (port.ast.direction != slang::ast::ArgumentDirection::In)
1379 portArg = moore::ReadOp::create(builder, port.loc, port.arg);
1380 moore::ContinuousAssignOp::create(builder, port.loc, value, portArg);
1385 for (
auto &fp : lowering.ifacePorts) {
1389 fp.bodySym ? fp.bodySym->as_if<slang::ast::ValueSymbol>() : nullptr;
1392 Value ref = valueSymbols.lookup(valueSym);
1395 outputs.push_back(moore::ReadOp::create(builder, fp.loc, ref).getResult());
1400 for (
auto &hierPath : hierPaths[module])
1401 if (auto hierValue = valueSymbols.lookup(hierPath.valueSym))
1402 if (hierPath.direction ==
slang::ast::ArgumentDirection::
Out)
1403 outputs.push_back(hierValue);
1405 moore::OutputOp::create(builder, lowering.op.getLoc(), outputs);
1415 timeScale = package.getTimeScale().value_or(slang::TimeScale());
1416 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
1418 OpBuilder::InsertionGuard g(
builder);
1421 for (
auto &member : package.members()) {
1423 if (failed(member.visit(PackageVisitor(*
this, loc))))
1434 auto &lowering =
functions[&subroutine];
1436 if (!lowering->op.getOperation())
1438 return lowering.get();
1441 if (!subroutine.thisVar) {
1443 SmallString<64> name;
1445 name += subroutine.name;
1447 SmallVector<Type, 1> noThis = {};
1454 const slang::ast::Type &thisTy = subroutine.thisVar->getType();
1455 moore::ClassDeclOp ownerDecl;
1457 if (
auto *classTy = thisTy.as_if<slang::ast::ClassType>()) {
1458 auto &ownerLowering =
classes[classTy];
1459 ownerDecl = ownerLowering->op;
1461 mlir::emitError(loc) <<
"expected 'this' to be a class type, got "
1462 << thisTy.toString();
1467 SmallString<64> qualName;
1468 qualName += ownerDecl.getSymName();
1470 qualName += subroutine.name;
1473 SmallVector<Type, 1> extraParams;
1475 auto classSym = mlir::FlatSymbolRefAttr::get(ownerDecl.getSymNameAttr());
1476 auto handleTy = moore::ClassHandleType::get(
getContext(), classSym);
1477 extraParams.push_back(handleTy);
1487 Context &
context,
const slang::ast::SubroutineSymbol &subroutine,
1488 ArrayRef<Type> prefixParams, ArrayRef<Type> suffixParams = {}) {
1489 using slang::ast::ArgumentDirection;
1491 SmallVector<Type> inputTypes;
1492 inputTypes.append(prefixParams.begin(), prefixParams.end());
1493 SmallVector<Type, 1> outputTypes;
1495 for (
const auto *arg : subroutine.getArguments()) {
1496 auto type =
context.convertType(arg->getType());
1499 if (arg->direction == ArgumentDirection::In) {
1500 inputTypes.push_back(type);
1502 inputTypes.push_back(
1503 moore::RefType::get(cast<moore::UnpackedType>(type)));
1507 inputTypes.append(suffixParams.begin(), suffixParams.end());
1509 const auto &returnType = subroutine.getReturnType();
1510 if (!returnType.isVoid()) {
1511 auto type =
context.convertType(returnType);
1514 outputTypes.push_back(type);
1517 return FunctionType::get(
context.getContext(), inputTypes, outputTypes);
1520static FailureOr<SmallVector<moore::DPIArgInfo>>
1522 const slang::ast::SubroutineSymbol &subroutine) {
1523 using slang::ast::ArgumentDirection;
1525 SmallVector<moore::DPIArgInfo> args;
1526 args.reserve(subroutine.getArguments().size() +
1527 (!subroutine.getReturnType().isVoid() ? 1 : 0));
1529 for (
const auto *arg : subroutine.getArguments()) {
1530 auto type =
context.convertType(arg->getType());
1533 moore::DPIArgDirection dir;
1534 switch (arg->direction) {
1535 case ArgumentDirection::In:
1536 dir = moore::DPIArgDirection::In;
1538 case ArgumentDirection::Out:
1539 dir = moore::DPIArgDirection::Out;
1541 case ArgumentDirection::InOut:
1542 dir = moore::DPIArgDirection::InOut;
1544 case ArgumentDirection::Ref:
1545 llvm_unreachable(
"'ref' is not legal for DPI functions");
1548 {StringAttr::get(
context.getContext(), arg->name), type, dir});
1551 if (!subroutine.getReturnType().isVoid()) {
1552 auto type =
context.convertType(subroutine.getReturnType());
1555 args.push_back({StringAttr::get(
context.getContext(),
"return"), type,
1556 moore::DPIArgDirection::Return});
1566 mlir::StringRef qualifiedName,
1567 llvm::SmallVectorImpl<Type> &extraParams) {
1571 OpBuilder::InsertionGuard g(
builder);
1577 builder.setInsertionPoint(it->second);
1582 SmallVector<Type> captureTypes;
1585 for (
auto *sym : capturesIt->second) {
1589 captureTypes.push_back(
1590 moore::RefType::get(cast<moore::UnpackedType>(type)));
1599 std::unique_ptr<FunctionLowering> lowering;
1600 Operation *insertedOp =
nullptr;
1601 if (!subroutine.thisVar &&
1602 subroutine.flags.has(slang::ast::MethodFlags::DPIImport)) {
1608 auto dpiOp = moore::DPIFuncOp::create(
1611 StringAttr::get(
getContext(), subroutine.name));
1612 SymbolTable::setSymbolVisibility(dpiOp, SymbolTable::Visibility::Private);
1613 lowering = std::make_unique<FunctionLowering>(dpiOp);
1615 }
else if (subroutine.subroutineKind == slang::ast::SubroutineKind::Task) {
1617 auto op = moore::CoroutineOp::create(
builder, loc, qualifiedName, funcTy);
1618 SymbolTable::setSymbolVisibility(op, SymbolTable::Visibility::Private);
1619 lowering = std::make_unique<FunctionLowering>(op);
1624 mlir::func::FuncOp::create(
builder, loc, qualifiedName, funcTy);
1625 SymbolTable::setSymbolVisibility(funcOp, SymbolTable::Visibility::Private);
1626 lowering = std::make_unique<FunctionLowering>(funcOp);
1627 insertedOp = funcOp;
1633 lowering->capturedSymbols.assign(capturesIt->second.begin(),
1634 capturesIt->second.end());
1639 functions[&subroutine] = std::move(lowering);
1653 auto *lowering =
functions.at(&subroutine).get();
1658 timeScale = subroutine.getTimeScale().value_or(slang::TimeScale());
1659 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
1664 if (subroutine.flags.has(slang::ast::MethodFlags::DPIImport))
1667 const bool isMethod = (subroutine.thisVar !=
nullptr);
1672 if (
const auto *classTy =
1673 subroutine.thisVar->getType().as_if<slang::ast::ClassType>()) {
1674 for (
auto &member : classTy->members()) {
1675 const auto *prop = member.as_if<slang::ast::ClassPropertySymbol>();
1678 const auto &propCanon = prop->getType().getCanonicalType();
1679 if (
const auto *vi =
1680 propCanon.as_if<slang::ast::VirtualInterfaceType>()) {
1690 SmallVector<moore::VariableOp> argVariables;
1691 auto &block = lowering->op.getFunctionBody().emplaceBlock();
1698 cast<FunctionType>(lowering->op.getFunctionType()).getInput(0);
1699 auto thisArg = block.addArgument(thisType, thisLoc);
1707 auto inputs = cast<FunctionType>(lowering->op.getFunctionType()).getInputs();
1708 auto astArgs = subroutine.getArguments();
1709 unsigned prefixCount = isMethod ? 1 : 0;
1710 auto valInputs = llvm::ArrayRef<Type>(inputs)
1711 .drop_front(prefixCount)
1712 .take_front(astArgs.size());
1714 for (
auto [astArg, type] : llvm::zip(astArgs, valInputs)) {
1716 auto blockArg = block.addArgument(type, loc);
1718 if (isa<moore::RefType>(type)) {
1721 OpBuilder::InsertionGuard g(
builder);
1722 builder.setInsertionPointToEnd(&block);
1724 auto shadowArg = moore::VariableOp::create(
1725 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
1726 StringAttr{}, blockArg);
1728 argVariables.push_back(shadowArg);
1731 const auto &argCanon = astArg->getType().getCanonicalType();
1732 if (
const auto *vi = argCanon.as_if<slang::ast::VirtualInterfaceType>())
1738 OpBuilder::InsertionGuard g(
builder);
1739 builder.setInsertionPointToEnd(&block);
1742 if (subroutine.returnValVar) {
1743 auto type =
convertType(*subroutine.returnValVar->getDeclaredType());
1746 returnVar = moore::VariableOp::create(
1747 builder, lowering->op->getLoc(),
1748 moore::RefType::get(cast<moore::UnpackedType>(type)), StringAttr{},
1750 valueSymbols.insert(subroutine.returnValVar, returnVar);
1758 for (
auto *sym : lowering->capturedSymbols) {
1762 auto refType = moore::RefType::get(cast<moore::UnpackedType>(type));
1764 auto blockArg = block.addArgument(refType, loc);
1770 llvm::scope_exit restoreThis([&] {
currentThisRef = savedThis; });
1778 if (isa<moore::CoroutineOp>(lowering->op.getOperation())) {
1779 moore::ReturnOp::create(
builder, lowering->op->getLoc());
1780 }
else if (returnVar && !subroutine.getReturnType().isVoid()) {
1782 moore::ReadOp::create(
builder, returnVar.getLoc(), returnVar);
1783 mlir::func::ReturnOp::create(
builder, lowering->op->getLoc(), read);
1785 mlir::func::ReturnOp::create(
builder, lowering->op->getLoc(),
1789 if (returnVar && returnVar.use_empty())
1790 returnVar.getDefiningOp()->erase();
1792 for (
auto var : argVariables) {
1793 if (llvm::all_of(var->getUsers(),
1794 [](
auto *user) { return isa<moore::ReadOp>(user); })) {
1795 for (
auto *user : llvm::make_early_inc_range(var->getUsers())) {
1796 user->getResult(0).replaceAllUsesWith(var.getInitial());
1808 const slang::ast::PrimitiveInstanceSymbol &prim) {
1809 if (prim.getDriveStrength().first.has_value() ||
1810 prim.getDriveStrength().second.has_value())
1812 <<
"primitive instances with explicit drive strengths are not "
1815 if (prim.getDelay())
1817 <<
"primitive instances with delays are not yet supported.";
1819 switch (prim.primitiveType.primitiveKind) {
1820 case slang::ast::PrimitiveSymbol::PrimitiveKind::NInput:
1823 case slang::ast::PrimitiveSymbol::PrimitiveKind::NOutput:
1826 case slang::ast::PrimitiveSymbol::PrimitiveKind::Fixed:
1831 <<
"unsupported instance of primitive `" << prim.primitiveType.name
1837 const slang::ast::PrimitiveInstanceSymbol &prim) {
1839 auto primName = prim.primitiveType.name;
1841 auto portConns = prim.getPortConnections();
1842 assert(portConns.size() >= 2 &&
1843 "n-input primitives should have at least 2 ports");
1847 portConns[0]->as<slang::ast::AssignmentExpression>().left();
1853 SmallVector<Value> inputVals;
1854 inputVals.reserve(portConns.size() - 1);
1855 for (
const auto *inputConn : portConns.subspan(1, portConns.size() - 1)) {
1859 inputVals.push_back(inputVal);
1862 Value nextInput = inputVals.front();
1864 llvm::StringSwitch<std::function<Value()>>(prim.primitiveType.name)
1866 for (Value inputVal : llvm::drop_begin(inputVals))
1868 moore::AndOp::create(
builder, loc, nextInput, inputVal);
1872 for (Value inputVal : llvm::drop_begin(inputVals))
1874 moore::OrOp::create(
builder, loc, nextInput, inputVal);
1878 for (Value inputVal : llvm::drop_begin(inputVals))
1880 moore::XorOp::create(
builder, loc, nextInput, inputVal);
1883 .Case(
"nand", ([&] {
1884 for (Value inputVal : llvm::drop_begin(inputVals))
1886 moore::AndOp::create(
builder, loc, nextInput, inputVal);
1887 return moore::NotOp::create(
builder, loc, nextInput);
1890 for (Value inputVal : llvm::drop_begin(inputVals))
1892 moore::OrOp::create(
builder, loc, nextInput, inputVal);
1893 return moore::NotOp::create(
builder, loc, nextInput);
1895 .Case(
"xnor", ([&] {
1896 for (Value inputVal : llvm::drop_begin(inputVals))
1898 moore::XorOp::create(
builder, loc, nextInput, inputVal);
1899 return moore::NotOp::create(
builder, loc, nextInput);
1902 mlir::emitError(loc)
1903 <<
"unsupported primitive `" << primName <<
"`";
1910 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
1915 moore::ContinuousAssignOp::create(
builder, loc, outputVal, result);
1920 const slang::ast::PrimitiveInstanceSymbol &prim) {
1922 auto primName = prim.primitiveType.name;
1924 auto portConns = prim.getPortConnections();
1925 assert(portConns.size() >= 2 &&
1926 "n-output primitives should have at least 2 ports");
1929 SmallVector<Value> outputVals;
1930 outputVals.reserve(portConns.size() - 1);
1931 for (
const auto *outputConn : portConns.subspan(0, portConns.size() - 1)) {
1932 auto &output = outputConn->as<slang::ast::AssignmentExpression>().left();
1936 outputVals.push_back(outputVal);
1944 llvm::StringSwitch<std::function<Value()>>(prim.primitiveType.name)
1946 ([&] {
return moore::NotOp::create(
builder, loc, inputVal); }))
1948 return moore::BoolCastOp::create(
builder, loc, inputVal);
1951 mlir::emitError(loc)
1952 <<
"unsupported primitive `" << primName <<
"`";
1959 for (
auto outputVal : outputVals) {
1960 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
1964 moore::ContinuousAssignOp::create(
builder, loc, outputVal, converted);
1970 const slang::ast::PrimitiveInstanceSymbol &prim) {
1971 auto primName = prim.primitiveType.name;
1976 if (primName ==
"pullup" || primName ==
"pulldown")
1980 mlir::emitError(loc) <<
"unsupported primitive `" << primName <<
"`";
1985 const slang::ast::PrimitiveInstanceSymbol &prim) {
1986 assert((prim.primitiveType.name ==
"pullup" ||
1987 prim.primitiveType.name ==
"pulldown") &&
1988 "expected pullup or pulldown primitive");
1990 auto primName = prim.primitiveType.name;
1992 auto portConns = prim.getPortConnections();
1994 assert(portConns.size() == 1 &&
1995 "pullup/pulldown primitives should have exactly one port");
1998 portConns.front()->as<slang::ast::AssignmentExpression>().left());
2000 auto dstType = cast<moore::RefType>(portVal.getType()).getNestedType();
2001 auto dstTypeWidth = dstType.getBitSize();
2004 "expected fixed-width type for pullup/pulldown primitive");
2005 auto constVal = primName ==
"pullup" ? -1 : 0;
2006 auto c = moore::ConstantOp::create(
2008 moore::IntType::getInt(this->
getContext(), dstTypeWidth.value()),
2014 moore::ContinuousAssignOp::create(
builder, loc, portVal, converted);
2022mlir::StringAttr fullyQualifiedClassName(
Context &ctx,
2023 const slang::ast::Type &ty) {
2024 SmallString<64> name;
2025 SmallVector<llvm::StringRef, 8> parts;
2027 const slang::ast::Scope *scope = ty.getParentScope();
2029 const auto &sym = scope->asSymbol();
2031 case slang::ast::SymbolKind::Root:
2034 case slang::ast::SymbolKind::InstanceBody:
2035 case slang::ast::SymbolKind::Instance:
2036 case slang::ast::SymbolKind::Package:
2037 case slang::ast::SymbolKind::ClassType:
2038 if (!sym.name.empty())
2039 parts.push_back(sym.name);
2044 scope = sym.getParentScope();
2047 for (
auto p :
llvm::reverse(parts)) {
2052 return mlir::StringAttr::get(ctx.
getContext(), name);
2057std::pair<mlir::SymbolRefAttr, mlir::ArrayAttr>
2059 const slang::ast::ClassType &cls) {
2063 mlir::SymbolRefAttr base;
2064 if (
const auto *b = cls.getBaseClass())
2065 base = mlir::SymbolRefAttr::get(fullyQualifiedClassName(
context, *b));
2068 SmallVector<mlir::Attribute> impls;
2069 if (
auto ifaces = cls.getDeclaredInterfaces(); !ifaces.empty()) {
2070 impls.reserve(ifaces.size());
2071 for (
const auto *iface : ifaces)
2072 impls.push_back(
mlir::FlatSymbolRefAttr::
get(
2073 fullyQualifiedClassName(
context, *iface)));
2076 mlir::ArrayAttr implArr =
2077 impls.empty() ? mlir::ArrayAttr() :
mlir::ArrayAttr::
get(ctx, impls);
2079 return {base, implArr};
2084struct ClassDeclVisitorBase {
2090 :
context(ctx), builder(ctx.builder), classLowering(lowering) {}
2094 return context.convertLocation(sloc);
2100struct ClassPropertyVisitor : ClassDeclVisitorBase {
2101 using ClassDeclVisitorBase::ClassDeclVisitorBase;
2104 LogicalResult
run(
const slang::ast::ClassType &classAST) {
2105 if (!classLowering.
op.getBody().empty())
2108 OpBuilder::InsertionGuard ig(builder);
2110 Block *body = &classLowering.
op.getBody().emplaceBlock();
2111 builder.setInsertionPointToEnd(body);
2114 for (
const auto &mem : classAST.members()) {
2115 if (
const auto *prop = mem.as_if<slang::ast::ClassPropertySymbol>()) {
2116 if (failed(prop->visit(*
this)))
2125 LogicalResult visit(
const slang::ast::ClassPropertySymbol &prop) {
2127 auto ty =
context.convertType(prop.getType());
2131 if (prop.lifetime == slang::ast::VariableLifetime::Automatic) {
2132 moore::ClassPropertyDeclOp::create(builder, loc, prop.name, ty);
2140 if (!
context.globalVariables.lookup(&prop))
2141 return context.convertGlobalVariable(prop);
2146 LogicalResult visit(
const slang::ast::ClassType &cls) {
2147 return context.buildClassProperties(cls);
2151 template <
typename T>
2152 LogicalResult visit(T &&) {
2159struct ClassMethodVisitor : ClassDeclVisitorBase {
2160 using ClassDeclVisitorBase::ClassDeclVisitorBase;
2163 LogicalResult
run(
const slang::ast::ClassType &classAST) {
2167 if (classLowering.
op.getBody().empty())
2170 OpBuilder::InsertionGuard ig(builder);
2171 builder.setInsertionPointToEnd(&classLowering.
op.getBody().front());
2174 for (
const auto &mem : classAST.members()) {
2175 if (failed(mem.visit(*
this)))
2184 LogicalResult visit(
const slang::ast::ClassPropertySymbol &) {
2190 LogicalResult visit(
const slang::ast::ParameterSymbol &) {
return success(); }
2194 LogicalResult visit(
const slang::ast::TypeParameterSymbol &) {
2200 LogicalResult visit(
const slang::ast::TypeAliasType &) {
return success(); }
2203 LogicalResult visit(
const slang::ast::GenericClassDefSymbol &) {
2208 LogicalResult visit(
const slang::ast::TransparentMemberSymbol &) {
2213 LogicalResult visit(
const slang::ast::EmptyMemberSymbol &) {
2218 LogicalResult visit(
const slang::ast::SubroutineSymbol &fn) {
2219 if (fn.flags & slang::ast::MethodFlags::BuiltIn) {
2220 static bool remarkEmitted =
false;
2224 mlir::emitRemark(classLowering.
op.getLoc())
2225 <<
"Class builtin functions (needed for randomization, constraints, "
2226 "and covergroups) are not yet supported and will be dropped "
2228 remarkEmitted =
true;
2232 const mlir::UnitAttr isVirtual =
2233 (fn.flags & slang::ast::MethodFlags::Virtual)
2234 ? UnitAttr::get(
context.getContext())
2241 if (fn.flags & slang::ast::MethodFlags::Pure) {
2243 SmallVector<Type, 1> extraParams;
2245 mlir::FlatSymbolRefAttr::get(classLowering.
op.getSymNameAttr());
2247 moore::ClassHandleType::get(
context.getContext(), classSym);
2248 extraParams.push_back(handleTy);
2252 mlir::emitError(loc) <<
"Invalid function signature for " << fn.name;
2256 moore::ClassMethodDeclOp::create(builder, loc, fn.name, funcTy,
nullptr);
2260 auto *lowering =
context.declareFunction(fn);
2269 FunctionType fnTy = cast<FunctionType>(lowering->op.getFunctionType());
2271 moore::ClassMethodDeclOp::create(
2272 builder, loc, fn.name, fnTy,
2273 SymbolRefAttr::get(lowering->op.getNameAttr()));
2290 LogicalResult visit(
const slang::ast::MethodPrototypeSymbol &fn) {
2291 const auto *externImpl = fn.getSubroutine();
2295 <<
"Didn't find an implementation matching the forward declaration "
2300 return visit(*externImpl);
2304 LogicalResult visit(
const slang::ast::ClassType &cls) {
2305 if (failed(
context.buildClassProperties(cls)))
2307 return context.materializeClassMethods(cls);
2311 template <
typename T>
2312 LogicalResult visit(T &&node) {
2313 Location loc = UnknownLoc::get(
context.getContext());
2314 if constexpr (
requires { node.location; })
2316 mlir::emitError(loc) <<
"unsupported construct in ClassType members: "
2317 << slang::ast::toString(node.kind);
2325 auto &lowering =
classes[&cls];
2327 return lowering.get();
2328 lowering = std::make_unique<ClassLowering>();
2333 OpBuilder::InsertionGuard g(
builder);
2339 builder.setInsertionPoint(it->second);
2341 auto symName = fullyQualifiedClassName(*
this, cls);
2343 auto [base, impls] = buildBaseAndImplementsAttrs(*
this, cls);
2345 moore::ClassDeclOp::create(
builder, loc, symName, base, impls);
2347 SymbolTable::setSymbolVisibility(classDeclOp,
2348 SymbolTable::Visibility::Public);
2350 lowering->op = classDeclOp;
2353 return lowering.get();
2360 timeScale = classdecl.getTimeScale().value_or(slang::TimeScale());
2361 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
2368 if (classdecl.getBaseClass()) {
2369 if (
const auto *baseClassDecl =
2370 classdecl.getBaseClass()->as_if<slang::ast::ClassType>()) {
2381 return ClassPropertyVisitor(*
this, *lowering).run(classdecl);
2388 timeScale = classdecl.getTimeScale().value_or(slang::TimeScale());
2389 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
2392 auto *lowering =
classes[&classdecl].get();
2399 if (classdecl.getBaseClass()) {
2400 if (
const auto *baseClassDecl =
2401 classdecl.getBaseClass()->as_if<slang::ast::ClassType>()) {
2407 return ClassMethodVisitor(*
this, *lowering).run(classdecl);
2417 OpBuilder::InsertionGuard g(
builder);
2423 builder.setInsertionPoint(it->second);
2427 SmallString<64> symName;
2431 if (
const auto *classVar = var.as_if<slang::ast::ClassPropertySymbol>()) {
2432 if (
const auto *parentScope = classVar->getParentScope()) {
2433 if (
const auto *parentClass =
2434 parentScope->asSymbol().as_if<slang::ast::ClassType>())
2435 symName = fullyQualifiedClassName(*
this, *parentClass);
2437 mlir::emitError(loc)
2438 <<
"Could not access parent class of class property "
2443 mlir::emitError(loc) <<
"Could not get parent scope of class property "
2448 symName += var.name;
2451 symName += var.name;
2460 auto varOp = moore::GlobalVariableOp::create(
builder, loc, symName,
2461 cast<moore::UnpackedType>(type));
2472 if (var.getInitializer())
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
static Location convertLocation(MLIRContext *context, const slang::SourceManager &sourceManager, slang::SourceLocation loc)
Convert a slang SourceLocation to an MLIR Location.
static moore::ProcedureKind convertProcedureKind(slang::ast::ProceduralBlockKind kind)
static FailureOr< SmallVector< moore::DPIArgInfo > > getDPISignature(Context &context, const slang::ast::SubroutineSymbol &subroutine)
static void guessNamespacePrefix(const slang::ast::Symbol &symbol, SmallString< 64 > &prefix)
static FunctionType getFunctionSignature(Context &context, const slang::ast::SubroutineSymbol &subroutine, ArrayRef< Type > prefixParams, ArrayRef< Type > suffixParams={})
Helper function to generate the function signature from a SubroutineSymbol and optional extra argumen...
static moore::NetKind convertNetKind(slang::ast::NetType::NetKind kind)
CaptureMap analyzeFunctionCaptures(const slang::ast::RootSymbol &root)
Analyze the AST rooted at root to determine which variables each function captures.
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.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
bool debugInfo
Generate debug information in the form of debug dialect ops in the IR.
circt::moore::ClassDeclOp op
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
Value materializeConversion(Type type, Value value, bool isSigned, Location loc)
Helper function to insert the necessary operations to cast a value from one type to another.
FunctionLowering * declareCallableImpl(const slang::ast::SubroutineSymbol &subroutine, mlir::StringRef qualifiedName, llvm::SmallVectorImpl< Type > &extraParams)
Helper function to extract the commonalities in lowering of functions and methods.
ModuleLowering * convertModuleHeader(const slang::ast::InstanceBodySymbol *module)
Convert a module and its ports to an empty module op in the IR.
std::queue< const slang::ast::SubroutineSymbol * > functionWorklist
A list of functions for which the declaration has been created, but the body has not been defined yet...
Value convertLvalueExpression(const slang::ast::Expression &expr)
LogicalResult registerVirtualInterfaceMembers(const slang::ast::ValueSymbol &base, const slang::ast::VirtualInterfaceType &type, Location loc)
Register the interface members of a virtual interface base symbol for use in later expression convers...
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.
LogicalResult materializeClassMethods(const slang::ast::ClassType &classdecl)
DenseMap< const slang::ast::ValueSymbol *, moore::GlobalVariableOp > globalVariables
A table of defined global variables that may be referred to by name in expressions.
slang::ast::Compilation & compilation
LogicalResult flushPendingMonitors()
Process any pending $monitor calls and generate the monitoring procedures at module level.
LogicalResult convertNInputPrimitive(const slang::ast::PrimitiveInstanceSymbol &prim)
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.
LogicalResult convertGlobalVariable(const slang::ast::VariableSymbol &var)
Convert a variable to a moore.global_variable operation.
CaptureMap functionCaptures
Pre-computed capture analysis: maps each function to the set of non-local, non-global variables it ca...
DenseMap< const slang::ast::ClassType *, std::unique_ptr< ClassLowering > > classes
Classes that have already been converted.
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.
slang::TimeScale timeScale
The time scale currently in effect.
ClassLowering * declareClass(const slang::ast::ClassType &cls)
VirtualInterfaceMembers::ScopeTy VirtualInterfaceMemberScope
LogicalResult convertFixedPrimitive(const slang::ast::PrimitiveInstanceSymbol &prim)
ValueSymbols valueSymbols
ValueSymbols::ScopeTy ValueSymbolScope
const ImportVerilogOptions & options
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
SmallVector< std::unique_ptr< InterfaceLowering > > interfaceInstanceStorage
Owning storage for InterfaceLowering objects because ScopedHashTable stores values by copy.
VirtualInterfaceMembers virtualIfaceMembers
Value currentThisRef
Variable to track the value of the current function's implicit this reference.
const slang::SourceManager & sourceManager
std::map< LocationKey, Operation * > orderedRootOps
The top-level operations ordered by their Slang source location.
InterfaceInstances::ScopeTy InterfaceInstanceScope
LogicalResult convertPrimitiveInstance(const slang::ast::PrimitiveInstanceSymbol &prim)
Convert a primitive instance.
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 convertNOutputPrimitive(const slang::ast::PrimitiveInstanceSymbol &prim)
InterfaceInstances interfaceInstances
LogicalResult buildClassProperties(const slang::ast::ClassType &classdecl)
LogicalResult convertPackage(const slang::ast::PackageSymbol &package)
Convert a package and its contents.
MLIRContext * getContext()
Return the MLIR context.
LogicalResult defineFunction(const slang::ast::SubroutineSymbol &subroutine)
Define a function’s body.
LogicalResult convertPullGatePrimitive(const slang::ast::PrimitiveInstanceSymbol &prim)
LogicalResult convertStatement(const slang::ast::Statement &stmt)
SmallVector< const slang::ast::ValueSymbol * > globalVariableWorklist
A list of global variables that still need their initializers to be converted.
void traverseInstanceBody(const slang::ast::Symbol &symbol)
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.
Lowering information for an expanded interface instance.
static LocationKey get(const slang::SourceLocation &loc, const slang::SourceManager &mgr)
Module lowering information.