11#include "slang/ast/Compilation.h"
12#include "slang/ast/symbols/ClassSymbols.h"
13#include "slang/ast/symbols/MemberSymbols.h"
14#include "slang/syntax/AllSyntax.h"
15#include "slang/syntax/SyntaxVisitor.h"
16#include "llvm/ADT/STLFunctionalExtras.h"
17#include "llvm/ADT/ScopeExit.h"
20using namespace ImportVerilog;
33 const slang::ast::Scope &scope,
34 const slang::syntax::SyntaxNode *syntax) {
38 auto visitor = slang::syntax::makeSyntaxVisitor(
39 [&](
auto &visitor,
const slang::syntax::DPIExportSyntax &exportSyntax) {
40 auto svName = exportSyntax.name.valueText();
44 const auto *symbol = scope.find(svName);
45 const auto *subroutine =
46 symbol ? symbol->as_if<slang::ast::SubroutineSymbol>() :
nullptr;
50 auto cName = exportSyntax.c_identifier.valueText();
53 context.dpiExportCNames[subroutine] = std::string(cName);
55 [](
auto &visitor,
const slang::syntax::SyntaxNode &node) {
56 visitor.visitDefault(node);
58 syntax->visit(visitor);
62 SmallString<64> &prefix) {
63 if (symbol.kind != slang::ast::SymbolKind::Package)
66 if (!symbol.name.empty()) {
67 prefix += symbol.name;
84 BaseVisitor(
Context &context, Location loc)
85 : context(context), loc(loc), builder(context.builder) {}
88 LogicalResult visit(
const slang::ast::EmptyMemberSymbol &) {
94 LogicalResult visit(
const slang::ast::TransparentMemberSymbol &) {
99 LogicalResult visit(
const slang::ast::ClassType &classdecl) {
109 LogicalResult visit(
const slang::ast::GenericClassDefSymbol &) {
114 LogicalResult visit(
const slang::ast::TypeAliasType &) {
return success(); }
115 LogicalResult visit(
const slang::ast::ForwardingTypedefSymbol &) {
120 LogicalResult visit(
const slang::ast::ExplicitImportSymbol &) {
123 LogicalResult visit(
const slang::ast::WildcardImportSymbol &) {
128 LogicalResult visit(
const slang::ast::TypeParameterSymbol &) {
133 LogicalResult visit(
const slang::ast::ElabSystemTaskSymbol &) {
138 LogicalResult visit(
const slang::ast::ParameterSymbol ¶m) {
139 visitParameter(param);
143 LogicalResult visit(
const slang::ast::SpecparamSymbol ¶m) {
144 visitParameter(param);
148 template <
class Node>
149 void visitParameter(
const Node ¶m) {
159 if (builder.getInsertionBlock()->getParentOp() == context.
intoModuleOp) {
166 SmallString<64> paramName;
168 paramName += param.name;
170 debug::VariableOp::create(builder, loc, builder.getStringAttr(paramName),
181struct RootVisitor :
public BaseVisitor {
182 using BaseVisitor::BaseVisitor;
183 using BaseVisitor::visit;
186 LogicalResult visit(
const slang::ast::PackageSymbol &package) {
187 return context.convertPackage(package);
191 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
192 if (!
context.declareFunction(subroutine))
198 LogicalResult visit(
const slang::ast::VariableSymbol &var) {
199 return context.convertGlobalVariable(var);
203 template <
typename T>
204 LogicalResult visit(T &&node) {
205 mlir::emitError(loc,
"unsupported construct: ")
206 << slang::ast::toString(node.kind);
217struct PackageVisitor :
public BaseVisitor {
218 using BaseVisitor::BaseVisitor;
219 using BaseVisitor::visit;
222 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
223 if (!
context.declareFunction(subroutine))
229 LogicalResult visit(
const slang::ast::VariableSymbol &var) {
230 return context.convertGlobalVariable(var);
234 template <
typename T>
235 LogicalResult visit(T &&node) {
236 mlir::emitError(loc,
"unsupported package member: ")
237 << slang::ast::toString(node.kind);
247static moore::ProcedureKind
250 case slang::ast::ProceduralBlockKind::Always:
251 return moore::ProcedureKind::Always;
252 case slang::ast::ProceduralBlockKind::AlwaysComb:
253 return moore::ProcedureKind::AlwaysComb;
254 case slang::ast::ProceduralBlockKind::AlwaysLatch:
255 return moore::ProcedureKind::AlwaysLatch;
256 case slang::ast::ProceduralBlockKind::AlwaysFF:
257 return moore::ProcedureKind::AlwaysFF;
258 case slang::ast::ProceduralBlockKind::Initial:
259 return moore::ProcedureKind::Initial;
260 case slang::ast::ProceduralBlockKind::Final:
261 return moore::ProcedureKind::Final;
263 llvm_unreachable(
"all procedure kinds handled");
268 case slang::ast::NetType::Supply0:
269 return moore::NetKind::Supply0;
270 case slang::ast::NetType::Supply1:
271 return moore::NetKind::Supply1;
272 case slang::ast::NetType::Tri:
273 return moore::NetKind::Tri;
274 case slang::ast::NetType::TriAnd:
275 return moore::NetKind::TriAnd;
276 case slang::ast::NetType::TriOr:
277 return moore::NetKind::TriOr;
278 case slang::ast::NetType::TriReg:
279 return moore::NetKind::TriReg;
280 case slang::ast::NetType::Tri0:
281 return moore::NetKind::Tri0;
282 case slang::ast::NetType::Tri1:
283 return moore::NetKind::Tri1;
284 case slang::ast::NetType::UWire:
285 return moore::NetKind::UWire;
286 case slang::ast::NetType::Wire:
287 return moore::NetKind::Wire;
288 case slang::ast::NetType::WAnd:
289 return moore::NetKind::WAnd;
290 case slang::ast::NetType::WOr:
291 return moore::NetKind::WOr;
292 case slang::ast::NetType::Interconnect:
293 return moore::NetKind::Interconnect;
294 case slang::ast::NetType::UserDefined:
295 return moore::NetKind::UserDefined;
296 case slang::ast::NetType::Unknown:
297 return moore::NetKind::Unknown;
299 llvm_unreachable(
"all net kinds handled");
303struct ModuleVisitor :
public BaseVisitor {
304 using BaseVisitor::visit;
308 StringRef blockNamePrefix;
310 ModuleVisitor(
Context &
context, Location loc, StringRef blockNamePrefix =
"")
311 : BaseVisitor(
context, loc), blockNamePrefix(blockNamePrefix) {}
314 LogicalResult visit(
const slang::ast::PortSymbol &) {
return success(); }
315 LogicalResult visit(
const slang::ast::MultiPortSymbol &) {
return success(); }
316 LogicalResult visit(
const slang::ast::InterfacePortSymbol &) {
321 LogicalResult visit(
const slang::ast::GenvarSymbol &genvarNode) {
326 LogicalResult visit(
const slang::ast::DefParamSymbol &) {
return success(); }
330 LogicalResult visit(
const slang::ast::TypeParameterSymbol &) {
338 expandInterfaceInstance(
const slang::ast::InstanceSymbol &instNode) {
339 auto prefix = (Twine(blockNamePrefix) + instNode.name +
"_").str();
340 auto lowering = std::make_unique<InterfaceLowering>();
341 Context::ValueSymbolScope scope(
context.valueSymbols);
343 auto recordMember = [&](
const slang::ast::Symbol &sym,
344 Value value) ->
void {
345 lowering->expandedMembers[&sym] = value;
346 auto nameAttr = builder.getStringAttr(sym.name);
347 lowering->expandedMembersByName[nameAttr] = value;
348 if (
auto *valueSym = sym.as_if<slang::ast::ValueSymbol>())
349 context.valueSymbols.insert(valueSym, value);
352 for (
const auto &member : instNode.body.members()) {
354 if (
const auto *nestedInst = member.as_if<slang::ast::InstanceSymbol>()) {
355 if (nestedInst->body.getDefinition().definitionKind ==
356 slang::ast::DefinitionKind::Interface)
357 return mlir::emitError(loc)
358 <<
"nested interface instances are not supported: `"
359 << nestedInst->name <<
"` inside `" << instNode.name <<
"`";
362 if (
const auto *var = member.as_if<slang::ast::VariableSymbol>()) {
363 auto loweredType =
context.convertType(*var->getDeclaredType());
366 auto varOp = moore::VariableOp::create(
368 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
369 builder.getStringAttr(Twine(prefix) + StringRef(var->name)),
371 recordMember(*var, varOp);
375 if (
const auto *net = member.as_if<slang::ast::NetSymbol>()) {
376 auto loweredType =
context.convertType(*net->getDeclaredType());
380 if (netKind == moore::NetKind::Interconnect ||
381 netKind == moore::NetKind::UserDefined ||
382 netKind == moore::NetKind::Unknown)
383 return mlir::emitError(loc,
"unsupported net kind `")
384 << net->netType.name <<
"`";
385 auto netOp = moore::NetOp::create(
387 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
388 builder.getStringAttr(Twine(prefix) + StringRef(net->name)),
390 recordMember(*net, netOp);
399 for (
const auto *con : instNode.getPortConnections()) {
400 const auto *expr = con->getExpression();
401 const auto *port = con->port.as_if<slang::ast::PortSymbol>();
409 Value lvalue =
context.convertLvalueExpression(*expr);
413 recordMember(*port, lvalue);
414 if (port->internalSymbol) {
415 recordMember(*port->internalSymbol, lvalue);
421 for (
const auto &member : instNode.body.members()) {
422 switch (member.kind) {
423 case slang::ast::SymbolKind::ContinuousAssign:
424 case slang::ast::SymbolKind::ProceduralBlock:
425 case slang::ast::SymbolKind::StatementBlock:
430 auto memberLoc =
context.convertLocation(member.location);
431 if (failed(member.visit(ModuleVisitor(
context, memberLoc, prefix))))
433 if (failed(
context.flushPendingMonitors()))
437 context.interfaceInstanceStorage.push_back(std::move(lowering));
438 context.interfaceInstances.insert(
439 &instNode,
context.interfaceInstanceStorage.back().get());
444 LogicalResult visit(
const slang::ast::InstanceSymbol &instNode) {
445 using slang::ast::ArgumentDirection;
446 using slang::ast::AssignmentExpression;
447 using slang::ast::MultiPortSymbol;
448 using slang::ast::PortSymbol;
450 if (
context.predeclaredInstances.contains(&instNode))
461 auto defKind = body->getDefinition().definitionKind;
462 if (defKind == slang::ast::DefinitionKind::Interface) {
463 if (
context.interfaceInstances.lookup(&instNode))
465 return expandInterfaceInstance(instNode);
468 auto *moduleLowering =
context.convertModuleHeader(body);
471 auto module = moduleLowering->op;
472 auto moduleType =
module.getModuleType();
475 SymbolTable::setSymbolVisibility(module, SymbolTable::Visibility::Private);
482 portValues.reserve(moduleType.getNumPorts());
486 const slang::ast::InstanceSymbol *>
489 for (
const auto *con : instNode.getPortConnections()) {
490 const auto *expr = con->getExpression();
495 auto *port = con->port.as_if<PortSymbol>();
496 if (
auto *existingPort =
497 moduleLowering->portsBySyntaxNode.lookup(port->getSyntax()))
500 switch (port->direction) {
501 case ArgumentDirection::In: {
502 auto refType = moore::RefType::get(
503 cast<moore::UnpackedType>(
context.convertType(port->getType())));
505 if (
const auto *net =
506 port->internalSymbol->as_if<slang::ast::NetSymbol>()) {
507 auto netOp = moore::NetOp::create(
508 builder, loc, refType,
509 StringAttr::get(builder.getContext(), net->name),
511 auto readOp = moore::ReadOp::create(builder, loc, netOp);
512 portValues.insert({port, readOp});
513 }
else if (
const auto *var =
515 ->as_if<slang::ast::VariableSymbol>()) {
516 auto varOp = moore::VariableOp::create(
517 builder, loc, refType,
518 StringAttr::get(builder.getContext(), var->name),
nullptr);
519 auto readOp = moore::ReadOp::create(builder, loc, varOp);
520 portValues.insert({port, readOp});
522 return mlir::emitError(loc)
523 <<
"unsupported internal symbol for unconnected port `"
524 << port->name <<
"`";
531 case ArgumentDirection::Out:
534 case ArgumentDirection::InOut:
535 case ArgumentDirection::Ref: {
536 auto refType = moore::RefType::get(
537 cast<moore::UnpackedType>(
context.convertType(port->getType())));
539 if (
const auto *net =
540 port->internalSymbol->as_if<slang::ast::NetSymbol>()) {
541 auto netOp = moore::NetOp::create(
542 builder, loc, refType,
543 StringAttr::get(builder.getContext(), net->name),
545 portValues.insert({port, netOp});
546 }
else if (
const auto *var =
548 ->as_if<slang::ast::VariableSymbol>()) {
549 auto varOp = moore::VariableOp::create(
550 builder, loc, refType,
551 StringAttr::get(builder.getContext(), var->name),
nullptr);
552 portValues.insert({port, varOp});
554 return mlir::emitError(loc)
555 <<
"unsupported internal symbol for unconnected port `"
556 << port->name <<
"`";
565 if (
const auto *assign = expr->as_if<AssignmentExpression>())
566 expr = &assign->left();
571 if (
auto *port = con->port.as_if<PortSymbol>()) {
573 auto value = (port->direction == ArgumentDirection::In)
574 ?
context.convertRvalueExpression(*expr)
575 :
context.convertLvalueExpression(*expr);
578 if (
auto *existingPort =
579 moduleLowering->portsBySyntaxNode.lookup(con->port.getSyntax()))
584 if (port->direction == ArgumentDirection::InOut) {
585 auto portType = moore::RefType::get(
586 cast<moore::UnpackedType>(
context.convertType(port->getType())));
587 if (value.getType() != portType)
588 return mlir::emitError(loc)
589 <<
"inout port `" << port->name <<
"` expects " << portType
590 <<
" but is connected to " << value.getType();
593 portValues.insert({port, value});
600 if (
const auto *multiPort = con->port.as_if<MultiPortSymbol>()) {
602 auto value =
context.convertLvalueExpression(*expr);
606 for (
const auto *port :
llvm::reverse(multiPort->ports)) {
607 if (
auto *existingPort = moduleLowering->portsBySyntaxNode.lookup(
608 con->port.getSyntax()))
610 unsigned width = port->getType().getBitWidth();
611 auto sliceType =
context.convertType(port->getType());
614 Value slice = moore::ExtractRefOp::create(
616 moore::RefType::get(cast<moore::UnpackedType>(sliceType)), value,
619 if (port->direction == ArgumentDirection::In)
620 slice = moore::ReadOp::create(builder, loc, slice);
621 portValues.insert({port, slice});
629 if (
const auto *ifacePort =
630 con->port.as_if<slang::ast::InterfacePortSymbol>()) {
631 auto ifaceConn = con->getIfaceConn();
632 const auto *connInst =
633 ifaceConn.first->as_if<slang::ast::InstanceSymbol>();
635 ifaceConnMap[ifacePort] = connInst;
639 mlir::emitError(loc) <<
"unsupported instance port `" << con->port.name
640 <<
"` (" << slang::ast::toString(con->port.kind)
648 SmallVector<Value> inputValues(moduleLowering->numExplicitInputs);
649 SmallVector<Value> outputValues(moduleLowering->numExplicitOutputs);
651 for (
auto &port : moduleLowering->ports) {
652 auto value = portValues.lookup(&port.ast);
653 if (port.ast.direction == ArgumentDirection::Out)
654 outputValues[*port.outputIdx] = value;
656 inputValues[*port.inputIdx] = value;
662 for (
auto &fp : moduleLowering->ifacePorts) {
663 if (!fp.bodySym || !fp.origin)
666 auto it = ifaceConnMap.find(fp.origin);
667 if (it == ifaceConnMap.end()) {
669 <<
"no interface connection for port `" << fp.name <<
"`";
672 const auto *connInst = it->second;
674 auto *ifaceLowering =
context.interfaceInstances.lookup(connInst);
675 if (!ifaceLowering) {
677 <<
"interface instance `" << connInst->name <<
"` was not expanded";
681 auto valIt = ifaceLowering->expandedMembers.find(fp.bodySym);
682 if (valIt == ifaceLowering->expandedMembers.end()) {
684 <<
"unresolved interface port signal `" << fp.name <<
"`";
687 Value val = valIt->second;
689 outputValues[*fp.outputIdx] = val;
693 if (isa<moore::RefType>(val.getType()) && !isa<moore::RefType>(fp.type))
694 val = moore::ReadOp::create(builder, loc, val);
695 inputValues[*fp.inputIdx] = val;
701 for (
auto [value, type] :
702 llvm::zip(inputValues, moduleType.getInputTypes())) {
706 value =
context.materializeConversion(type, value,
false, value.getLoc());
708 return mlir::emitError(loc) <<
"unsupported port";
715 for (
const auto &hierPath :
context.hierPaths[body]) {
716 assert(!hierPath.valueSyms.empty() &&
"hierPath must have valueSyms");
717 if (!hierPath.hierName || hierPath.direction != ArgumentDirection::In)
722 for (
auto &alias : hierPath.valueSyms)
723 if ((hierValue =
context.valueSymbols.lookup(alias.first)))
725 inputValues.push_back(hierValue);
729 for (
auto value : inputValues)
731 return
mlir::emitError(loc) <<
"unsupported port";
739 SmallString<64> instName(blockNamePrefix);
740 if (instNode.arrayPath.empty()) {
741 instName += instNode.name;
743 instName += instNode.getArrayName();
744 slang::SmallVector<slang::ConstantRange, 4> dims;
745 instNode.getArrayDimensions(dims);
746 for (
auto [dim, index] :
llvm::zip(dims, instNode.arrayPath)) {
748 Twine(dim.lower() + int32_t(index)).toVector(instName);
753 auto inputNames = builder.getArrayAttr(moduleType.getInputNames());
754 auto outputNames = builder.getArrayAttr(moduleType.getOutputNames());
755 auto inst = moore::InstanceOp::create(
756 builder, loc, moduleType.getOutputTypes(),
757 builder.getStringAttr(instName),
758 FlatSymbolRefAttr::get(module.getSymNameAttr()), inputValues,
759 inputNames, outputNames);
763 auto aliasReachedThroughInstance =
764 [&](
const slang::ast::InstanceBodySymbol *aliasBody) {
765 for (
auto *b = aliasBody; b && b->parentInstance;
766 b = b->parentInstance->getParentScope()->getContainingInstance())
767 if (b->parentInstance == &instNode)
777 for (
const auto &hierPath :
context.hierPaths[body])
778 if (hierPath.idx && hierPath.direction == ArgumentDirection::
Out) {
779 auto result = inst->getResult(*hierPath.idx);
780 for (
auto &alias : hierPath.valueSyms)
781 if (aliasReachedThroughInstance(alias.second))
782 context.valueSymbols.insert(alias.first, result);
783 context.hierValueSymbols[{&instNode, hierPath.hierName}] = result;
787 for (
auto [lvalue, output] :
llvm::zip(outputValues, inst.getOutputs())) {
790 Value rvalue = output;
791 auto dstType = cast<moore::RefType>(lvalue.getType()).getNestedType();
793 rvalue =
context.materializeConversion(dstType, rvalue,
false, loc);
796 moore::ContinuousAssignOp::create(builder, loc, lvalue, rvalue);
803 LogicalResult visit(
const slang::ast::VariableSymbol &varNode) {
804 auto ref =
context.valueSymbols.lookup(&varNode);
806 return mlir::emitError(loc)
807 <<
"internal error: missing predeclared variable `" << varNode.name
810 auto varOp = ref.getDefiningOp<moore::VariableOp>();
812 return mlir::emitError(loc)
813 <<
"internal error: predeclared variable `" << varNode.name
814 <<
"` is not a moore.variable";
816 if (
const auto *init = varNode.getInitializer()) {
817 auto loweredType = cast<moore::RefType>(ref.getType()).getNestedType();
818 auto initial =
context.convertRvalueExpression(*init, loweredType);
821 varOp.getInitialMutable().assign(initial);
828 LogicalResult visit(
const slang::ast::NetSymbol &netNode) {
829 auto ref =
context.valueSymbols.lookup(&netNode);
831 return mlir::emitError(loc) <<
"internal error: missing predeclared net `"
832 << netNode.name <<
"`";
834 auto netOp = ref.getDefiningOp<moore::NetOp>();
836 return mlir::emitError(loc) <<
"internal error: predeclared net `"
837 << netNode.name <<
"` is not a moore.net";
839 if (
const auto *init = netNode.getInitializer()) {
840 auto loweredType = cast<moore::RefType>(ref.getType()).getNestedType();
841 auto assignment =
context.convertRvalueExpression(*init, loweredType);
844 netOp.getAssignmentMutable().assign(assignment);
850 LogicalResult visit(
const slang::ast::ContinuousAssignSymbol &assignNode) {
852 assignNode.getAssignment().as<slang::ast::AssignmentExpression>();
853 auto lhs =
context.convertLvalueExpression(expr.left());
857 auto rhs =
context.convertRvalueExpression(
858 expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
863 if (
auto *timingCtrl = assignNode.getDelay()) {
864 if (
auto *ctrl = timingCtrl->as_if<slang::ast::DelayControl>()) {
865 auto delay =
context.convertRvalueExpression(
866 ctrl->expr, moore::TimeType::get(builder.getContext()));
869 moore::DelayedContinuousAssignOp::create(builder, loc, lhs, rhs, delay);
872 mlir::emitError(loc) <<
"unsupported delay with rise/fall/turn-off";
877 moore::ContinuousAssignOp::create(builder, loc, lhs, rhs);
882 LogicalResult convertProcedure(moore::ProcedureKind kind,
883 const slang::ast::Statement &body) {
884 if (body.as_if<slang::ast::ConcurrentAssertionStatement>())
885 return context.convertStatement(body);
886 auto procOp = moore::ProcedureOp::create(builder, loc, kind);
887 OpBuilder::InsertionGuard guard(builder);
888 builder.setInsertionPointToEnd(&procOp.getBody().emplaceBlock());
889 Context::ValueSymbolScope scope(
context.valueSymbols);
890 Context::VirtualInterfaceMemberScope vifMemberScope(
892 if (failed(
context.convertStatement(body)))
894 if (builder.getBlock())
895 moore::ReturnOp::create(builder, loc);
899 LogicalResult visit(
const slang::ast::ProceduralBlockSymbol &procNode) {
902 if (
auto *syntax = procNode.getSyntax();
904 syntax->kind == slang::syntax::SyntaxKind::ConcurrentAssertionMember)
905 return context.convertStatement(procNode.getBody());
909 if (
context.options.lowerAlwaysAtStarAsComb) {
910 auto *stmt = procNode.getBody().as_if<slang::ast::TimedStatement>();
911 if (procNode.procedureKind == slang::ast::ProceduralBlockKind::Always &&
913 stmt->timing.kind == slang::ast::TimingControlKind::ImplicitEvent)
914 return convertProcedure(moore::ProcedureKind::AlwaysComb, stmt->stmt);
922 LogicalResult visit(
const slang::ast::GenerateBlockSymbol &genNode) {
924 if (genNode.isUninstantiated)
928 SmallString<64> prefix = blockNamePrefix;
929 if (!genNode.name.empty() ||
930 genNode.getParentScope()->asSymbol().kind !=
931 slang::ast::SymbolKind::GenerateBlockArray) {
932 prefix += genNode.getExternalName();
937 for (
auto &member : genNode.members())
938 if (failed(member.visit(ModuleVisitor(
context, loc, prefix))))
944 LogicalResult visit(
const slang::ast::GenerateBlockArraySymbol &genArrNode) {
947 SmallString<64> prefix = blockNamePrefix;
948 prefix += genArrNode.getExternalName();
950 auto prefixBaseLen = prefix.size();
953 for (
const auto *entry : genArrNode.entries) {
955 prefix.resize(prefixBaseLen);
956 if (entry->arrayIndex)
957 prefix += entry->arrayIndex->toString();
959 Twine(entry->constructIndex).toVector(prefix);
963 if (failed(entry->asSymbol().visit(ModuleVisitor(
context, loc, prefix))))
975 LogicalResult visit(
const slang::ast::StatementBlockSymbol &) {
981 LogicalResult visit(
const slang::ast::SequenceSymbol &seqNode) {
987 LogicalResult visit(
const slang::ast::PropertySymbol &propNode) {
993 LogicalResult visit(
const slang::ast::ClockingBlockSymbol &) {
999 LogicalResult visit(
const slang::ast::LetDeclSymbol &) {
return success(); }
1002 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
1003 if (!
context.declareFunction(subroutine))
1009 LogicalResult visit(
const slang::ast::PrimitiveInstanceSymbol &prim) {
1010 return context.convertPrimitiveInstance(prim);
1014 LogicalResult visit(
const slang::ast::InstanceArraySymbol &arrNode) {
1016 for (
const auto *element : arrNode.elements)
1017 if (failed(element->visit(*this)))
1023 template <
typename T>
1024 LogicalResult visit(T &&node) {
1025 mlir::emitError(loc,
"unsupported module member: ")
1026 << slang::ast::toString(node.kind);
1031struct ModulePredeclaration {
1035 ModulePredeclaration(
Context &context)
1036 : context(context), builder(context.builder) {}
1038 LogicalResult declareVariable(
const slang::ast::VariableSymbol &varNode,
1039 Location loc, StringRef blockNamePrefix) {
1040 auto loweredType = context.
convertType(*varNode.getDeclaredType());
1044 auto varOp = moore::VariableOp::create(
1046 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
1047 builder.getStringAttr(Twine(blockNamePrefix) + varNode.name), Value{});
1050 const auto &canonTy = varNode.getType().getCanonicalType();
1051 if (
const auto *vi = canonTy.as_if<slang::ast::VirtualInterfaceType>())
1058 LogicalResult declareNet(
const slang::ast::NetSymbol &netNode, Location loc,
1059 StringRef blockNamePrefix) {
1060 auto loweredType = context.
convertType(*netNode.getDeclaredType());
1065 if (netkind == moore::NetKind::Interconnect ||
1066 netkind == moore::NetKind::UserDefined ||
1067 netkind == moore::NetKind::Unknown)
1068 return mlir::emitError(loc,
"unsupported net kind `")
1069 << netNode.netType.name <<
"`";
1071 auto netOp = moore::NetOp::create(
1073 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
1074 builder.getStringAttr(Twine(blockNamePrefix) + netNode.name), netkind,
1081 getGenerateBlockPrefix(
const slang::ast::GenerateBlockSymbol &genNode,
1082 StringRef blockNamePrefix) {
1083 SmallString<64> prefix = blockNamePrefix;
1084 if (!genNode.name.empty() ||
1085 genNode.getParentScope()->asSymbol().kind !=
1086 slang::ast::SymbolKind::GenerateBlockArray) {
1087 prefix += genNode.getExternalName();
1094 predeclareStorageGenerateBlock(
const slang::ast::GenerateBlockSymbol &genNode,
1095 StringRef blockNamePrefix) {
1096 if (genNode.isUninstantiated)
1098 return predeclareStorageScope(
1099 genNode, getGenerateBlockPrefix(genNode, blockNamePrefix));
1102 LogicalResult predeclareInterfaceGenerateBlock(
1103 const slang::ast::GenerateBlockSymbol &genNode,
1104 StringRef blockNamePrefix) {
1105 if (genNode.isUninstantiated)
1107 return predeclareInterfaceScope(
1108 genNode, getGenerateBlockPrefix(genNode, blockNamePrefix));
1111 LogicalResult predeclareModuleInstanceGenerateBlock(
1112 const slang::ast::GenerateBlockSymbol &genNode,
1113 StringRef blockNamePrefix) {
1114 if (genNode.isUninstantiated)
1116 return predeclareModuleInstanceScope(
1117 genNode, getGenerateBlockPrefix(genNode, blockNamePrefix));
1120 LogicalResult predeclareGenerateBlockArray(
1121 const slang::ast::GenerateBlockArraySymbol &genArrNode,
1122 StringRef blockNamePrefix,
1123 llvm::function_ref<LogicalResult(
const slang::ast::GenerateBlockSymbol &,
1126 SmallString<64> prefix = blockNamePrefix;
1127 prefix += genArrNode.getExternalName();
1129 auto prefixBaseLen = prefix.size();
1131 for (
const auto *entry : genArrNode.entries) {
1132 prefix.resize(prefixBaseLen);
1133 if (entry->arrayIndex)
1134 prefix += entry->arrayIndex->toString();
1136 Twine(entry->constructIndex).toVector(prefix);
1139 if (failed(predeclareBlock(*entry, prefix)))
1145 LogicalResult predeclareStorageMember(
const slang::ast::Symbol &member,
1146 StringRef blockNamePrefix) {
1148 if (
const auto *varNode = member.as_if<slang::ast::VariableSymbol>())
1149 return declareVariable(*varNode, loc, blockNamePrefix);
1151 if (
const auto *netNode = member.as_if<slang::ast::NetSymbol>())
1152 return declareNet(*netNode, loc, blockNamePrefix);
1154 if (
const auto *genNode = member.as_if<slang::ast::GenerateBlockSymbol>())
1155 return predeclareStorageGenerateBlock(*genNode, blockNamePrefix);
1157 if (
const auto *genArrNode =
1158 member.as_if<slang::ast::GenerateBlockArraySymbol>())
1159 return predeclareGenerateBlockArray(
1160 *genArrNode, blockNamePrefix,
1161 [&](
const slang::ast::GenerateBlockSymbol &gen, StringRef prefix) {
1162 return predeclareStorageGenerateBlock(gen, prefix);
1168 LogicalResult predeclareInterfaceMember(
const slang::ast::Symbol &member,
1169 StringRef blockNamePrefix) {
1171 if (
const auto *instNode = member.as_if<slang::ast::InstanceSymbol>()) {
1172 if (instNode->body.getDefinition().definitionKind ==
1173 slang::ast::DefinitionKind::Interface)
1174 return ModuleVisitor(context, loc, blockNamePrefix)
1175 .expandInterfaceInstance(*instNode);
1179 if (
const auto *genNode = member.as_if<slang::ast::GenerateBlockSymbol>())
1180 return predeclareInterfaceGenerateBlock(*genNode, blockNamePrefix);
1182 if (
const auto *genArrNode =
1183 member.as_if<slang::ast::GenerateBlockArraySymbol>())
1184 return predeclareGenerateBlockArray(
1185 *genArrNode, blockNamePrefix,
1186 [&](
const slang::ast::GenerateBlockSymbol &gen, StringRef prefix) {
1187 return predeclareInterfaceGenerateBlock(gen, prefix);
1193 LogicalResult predeclareModuleInstanceMember(
const slang::ast::Symbol &member,
1194 StringRef blockNamePrefix) {
1196 if (
const auto *instNode = member.as_if<slang::ast::InstanceSymbol>()) {
1197 if (instNode->body.getDefinition().definitionKind !=
1198 slang::ast::DefinitionKind::Interface) {
1200 ModuleVisitor(context, loc, blockNamePrefix).visit(*instNode)))
1207 if (
const auto *arrNode = member.as_if<slang::ast::InstanceArraySymbol>()) {
1208 for (
const auto *element : arrNode->elements)
1209 if (failed(predeclareModuleInstanceMember(*element, blockNamePrefix)))
1214 if (
const auto *genNode = member.as_if<slang::ast::GenerateBlockSymbol>())
1215 return predeclareModuleInstanceGenerateBlock(*genNode, blockNamePrefix);
1217 if (
const auto *genArrNode =
1218 member.as_if<slang::ast::GenerateBlockArraySymbol>())
1219 return predeclareGenerateBlockArray(
1220 *genArrNode, blockNamePrefix,
1221 [&](
const slang::ast::GenerateBlockSymbol &gen, StringRef prefix) {
1222 return predeclareModuleInstanceGenerateBlock(gen, prefix);
1228 LogicalResult predeclareStorageScope(
const slang::ast::Scope &scope,
1229 StringRef blockNamePrefix) {
1230 for (
auto &member : scope.members())
1231 if (failed(predeclareStorageMember(member, blockNamePrefix)))
1236 LogicalResult predeclareInterfaceScope(
const slang::ast::Scope &scope,
1237 StringRef blockNamePrefix) {
1238 for (
auto &member : scope.members())
1239 if (failed(predeclareInterfaceMember(member, blockNamePrefix)))
1244 LogicalResult predeclareModuleInstanceScope(
const slang::ast::Scope &scope,
1245 StringRef blockNamePrefix) {
1246 for (
auto &member : scope.members())
1247 if (failed(predeclareModuleInstanceMember(member, blockNamePrefix)))
1252 LogicalResult predeclareScope(
const slang::ast::Scope &scope,
1253 StringRef blockNamePrefix) {
1257 if (failed(predeclareStorageScope(scope, blockNamePrefix)))
1263 if (failed(predeclareInterfaceScope(scope, blockNamePrefix)))
1268 return predeclareModuleInstanceScope(scope, blockNamePrefix);
1279LogicalResult Context::convertCompilation() {
1285 timeScale = root.getTimeScale().value_or(slang::TimeScale());
1286 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
1290 SmallVector<AmbiguousHierCapture> ambiguousHierCaptures;
1292 for (
auto &ambiguous : ambiguousHierCaptures) {
1293 auto d = mlir::emitError(
convertLocation(ambiguous.function->location))
1294 <<
"hierarchical reference to `" << ambiguous.symbol->name
1295 <<
"` is ambiguous: this function reaches it through more than "
1296 "one instance of the same module, which is not yet supported";
1298 <<
"symbol declared here";
1300 if (!ambiguousHierCaptures.empty())
1305 for (
auto *inst : root.topInstances)
1315 for (
auto *unit : root.compilationUnits) {
1317 for (
const auto &member : unit->members()) {
1319 if (failed(member.visit(RootVisitor(*
this, loc))))
1327 SmallVector<const slang::ast::InstanceSymbol *> topInstances;
1328 for (
auto *inst : root.topInstances) {
1330 if (body->getDefinition().definitionKind !=
1331 slang::ast::DefinitionKind::Interface)
1338 auto *
module = moduleWorklist.front();
1346 SmallVector<const slang::ast::ClassType *, 16> classMethodWorklist;
1347 classMethodWorklist.reserve(
classes.size());
1349 classMethodWorklist.push_back(kv.first);
1351 for (
auto *inst : classMethodWorklist) {
1370 auto &block = varOp.getInitRegion().emplaceBlock();
1371 OpBuilder::InsertionGuard guard(
builder);
1372 builder.setInsertionPointToEnd(&block);
1377 moore::YieldOp::create(
builder, varOp.getLoc(), value);
1386 using slang::ast::ArgumentDirection;
1387 using slang::ast::MultiPortSymbol;
1388 using slang::ast::ParameterSymbol;
1389 using slang::ast::PortSymbol;
1390 using slang::ast::TypeParameterSymbol;
1395 timeScale =
module->getTimeScale().value_or(slang::TimeScale());
1396 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
1403 slot = std::make_unique<ModuleLowering>();
1404 auto &lowering = *slot;
1407 OpBuilder::InsertionGuard g(
builder);
1412 auto kind =
module->getDefinition().definitionKind;
1413 if (kind != slang::ast::DefinitionKind::Module &&
1414 kind != slang::ast::DefinitionKind::Program) {
1415 mlir::emitError(loc) <<
"unsupported definition: "
1416 <<
module->getDefinition().getKindString();
1421 auto block = std::make_unique<Block>();
1422 SmallVector<hw::ModulePort> modulePorts;
1425 unsigned int outputIdx = 0, inputIdx = 0;
1426 for (
auto *symbol :
module->getPortList()) {
1427 auto handlePort = [&](const PortSymbol &port) {
1428 auto portLoc = convertLocation(port.location);
1432 auto portName =
builder.getStringAttr(port.name);
1434 std::optional<unsigned> portOutputIdx;
1435 std::optional<unsigned> portInputIdx;
1436 if (port.direction == ArgumentDirection::Out) {
1438 portOutputIdx = outputIdx++;
1442 if (port.direction != ArgumentDirection::In)
1443 type = moore::RefType::get(cast<moore::UnpackedType>(type));
1445 arg = block->addArgument(type, portLoc);
1446 portInputIdx = inputIdx++;
1448 lowering.ports.push_back(
1449 {port, portLoc, arg, portOutputIdx, portInputIdx});
1456 auto handleIfacePort = [&](
const slang::ast::InterfacePortSymbol
1459 auto [connSym, modportSym] = ifacePort.getConnection();
1460 const auto *ifaceInst =
1461 connSym ? connSym->as_if<slang::ast::InstanceSymbol>() : nullptr;
1462 auto portPrefix = (Twine(ifacePort.name) +
"_").str();
1466 for (
const auto &member : modportSym->members()) {
1467 const auto *mpp = member.as_if<slang::ast::ModportPortSymbol>();
1474 builder.getStringAttr(Twine(portPrefix) + StringRef(mpp->name));
1477 std::optional<unsigned> ifaceOutputIdx;
1478 std::optional<unsigned> ifaceInputIdx;
1479 if (mpp->direction == ArgumentDirection::Out) {
1481 modulePorts.push_back({name, type, dir});
1482 ifaceOutputIdx = outputIdx++;
1485 if (mpp->direction != ArgumentDirection::In)
1486 type = moore::RefType::get(cast<moore::UnpackedType>(type));
1487 modulePorts.push_back({name, type, dir});
1488 arg = block->addArgument(type, portLoc);
1489 ifaceInputIdx = inputIdx++;
1491 lowering.ifacePorts.push_back(
1492 {name, dir, type, portLoc, arg, &ifacePort, mpp->internalSymbol,
1493 ifaceInst, mpp, ifaceOutputIdx, ifaceInputIdx});
1498 const auto *instSym = connSym->as_if<slang::ast::InstanceSymbol>();
1500 mlir::emitError(portLoc)
1501 <<
"unsupported interface port connection for `" << ifacePort.name
1505 for (
const auto &member : instSym->body.members()) {
1506 const slang::ast::Type *slangType =
nullptr;
1507 const slang::ast::Symbol *bodySym =
nullptr;
1508 if (
const auto *var = member.as_if<slang::ast::VariableSymbol>()) {
1509 slangType = &var->getType();
1511 }
else if (
const auto *net = member.as_if<slang::ast::NetSymbol>()) {
1512 slangType = &net->getType();
1520 auto name = builder.getStringAttr(Twine(portPrefix) +
1521 StringRef(bodySym->name));
1522 auto refType = moore::RefType::get(cast<moore::UnpackedType>(type));
1524 auto arg = block->addArgument(refType, portLoc);
1525 lowering.ifacePorts.push_back(
1527 bodySym, instSym,
nullptr, std::nullopt, inputIdx++});
1533 if (
const auto *port = symbol->as_if<PortSymbol>()) {
1534 if (failed(handlePort(*port)))
1536 }
else if (
const auto *multiPort = symbol->as_if<MultiPortSymbol>()) {
1537 for (
auto *port : multiPort->ports)
1538 if (failed(handlePort(*port)))
1540 }
else if (
const auto *ifacePort =
1541 symbol->as_if<slang::ast::InterfacePortSymbol>()) {
1542 if (failed(handleIfacePort(*ifacePort)))
1546 <<
"unsupported module port `" << symbol->name <<
"` ("
1547 << slang::ast::toString(symbol->kind) <<
")";
1553 lowering.numExplicitOutputs = outputIdx;
1554 lowering.numExplicitInputs = inputIdx;
1557 for (
auto &hierPath : hierPaths[module]) {
1558 assert(!hierPath.valueSyms.empty() &&
"hierPath must have valueSyms");
1559 auto hierType =
convertType(hierPath.valueSyms.front().first->getType());
1563 if (
auto hierName = hierPath.hierName) {
1565 hierType = moore::RefType::get(cast<moore::UnpackedType>(hierType));
1566 if (hierPath.direction == ArgumentDirection::Out) {
1567 hierPath.idx = outputIdx++;
1570 hierPath.idx = inputIdx++;
1574 block->addArgument(hierType, hierLoc);
1578 auto moduleType = hw::ModuleType::get(getContext(), modulePorts);
1583 auto it = orderedRootOps.upper_bound(key);
1584 if (it == orderedRootOps.end())
1585 builder.setInsertionPointToEnd(intoModuleOp.getBody());
1587 builder.setInsertionPoint(it->second);
1591 moore::SVModuleOp::create(builder, loc, module->name, moduleType);
1592 orderedRootOps.insert(it, {key, moduleOp});
1593 moduleOp.getBodyRegion().push_back(block.release());
1594 lowering.op = moduleOp;
1598 symbolTable.insert(moduleOp);
1601 moduleWorklist.push(module);
1604 for (
const auto &port : lowering.ports)
1605 lowering.portsBySyntaxNode.insert({port.ast.getSyntax(), &port.ast});
1612 auto &lowering = *
modules[module];
1615 llvm::scope_exit currentDefinitionGuard(
1619 OpBuilder::InsertionGuard g(
builder);
1620 builder.setInsertionPointToEnd(lowering.op.getBody());
1629 timeScale =
module->getTimeScale().value_or(slang::TimeScale());
1630 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
1635 for (
auto &hierPath :
hierPaths[module])
1636 if (hierPath.direction == slang::ast::ArgumentDirection::In &&
1638 auto arg = lowering.op.getBody()->getArgument(*hierPath.idx);
1639 for (
auto &alias : hierPath.valueSyms)
1647 DenseMap<const slang::ast::InstanceSymbol *, InterfaceLowering *>
1650 auto getIfacePortLowering =
1656 if (
auto it = ifacePortLowerings.find(ifaceInst);
1657 it != ifacePortLowerings.end())
1660 auto lowering = std::make_unique<InterfaceLowering>();
1664 ifacePortLowerings.try_emplace(ifaceInst, ptr);
1668 for (
auto &fp : lowering.ifacePorts) {
1671 auto *valueSym = fp.bodySym->as_if<slang::ast::ValueSymbol>();
1680 portValue = moore::VariableOp::create(
1682 moore::RefType::get(cast<moore::UnpackedType>(fp.type)), fp.name,
1691 if (fp.modportPortSym)
1692 if (
auto *mppSym = fp.modportPortSym->as_if<slang::ast::ValueSymbol>())
1693 if (mppSym != valueSym)
1696 if (!fp.ifaceInstance)
1699 auto *ifaceLowering = getIfacePortLowering(fp.ifaceInstance);
1702 ifaceLowering->expandedMembers[fp.bodySym] = val;
1704 ->expandedMembersByName[
builder.getStringAttr(fp.bodySym->name)] =
1710 llvm::scope_exit predeclaredInstancesGuard(
1720 if (failed(ModulePredeclaration(*this).predeclareScope(*module,
"")))
1724 for (
auto &member :
module->members()) {
1725 auto loc = convertLocation(member.location);
1726 if (failed(member.visit(ModuleVisitor(*
this, loc))))
1738 SmallVector<Value> outputs(lowering.numExplicitOutputs);
1739 for (
auto &port : lowering.ports) {
1741 if (
auto *expr = port.ast.getInternalExpr()) {
1742 value = convertLvalueExpression(*expr);
1743 }
else if (port.ast.internalSymbol) {
1744 if (
const auto *sym =
1745 port.ast.internalSymbol->as_if<slang::ast::ValueSymbol>())
1746 value = valueSymbols.lookup(sym);
1749 return mlir::emitError(port.loc,
"unsupported port: `")
1751 <<
"` does not map to an internal symbol or expression";
1754 if (port.ast.direction == slang::ast::ArgumentDirection::Out) {
1755 if (isa<moore::RefType>(value.getType()))
1756 value = moore::ReadOp::create(builder, value.getLoc(), value);
1757 outputs[*port.outputIdx] = value;
1763 Value portArg = port.arg;
1764 if (port.ast.direction != slang::ast::ArgumentDirection::In)
1765 portArg = moore::ReadOp::create(builder, port.loc, port.arg);
1766 moore::ContinuousAssignOp::create(builder, port.loc, value, portArg);
1771 for (
auto &fp : lowering.ifacePorts) {
1775 fp.bodySym ? fp.bodySym->as_if<slang::ast::ValueSymbol>() : nullptr;
1778 Value ref = valueSymbols.lookup(valueSym);
1781 outputs[*fp.outputIdx] =
1782 moore::ReadOp::create(builder, fp.loc, ref).getResult();
1787 for (
auto &hierPath : hierPaths[module]) {
1788 assert(!hierPath.valueSyms.empty() &&
"hierPath must have valueSyms");
1789 if (hierPath.direction != slang::ast::ArgumentDirection::Out)
1793 for (
auto &alias : hierPath.valueSyms)
1794 if ((hierValue = valueSymbols.lookup(alias.first)))
1799 auto name = hierPath.hierName.getValue();
1800 if (
auto dot = name.find(
"."); dot != llvm::StringRef::npos) {
1801 auto innerName = builder.getStringAttr(name.drop_front(dot + 1));
1802 for (
auto &member : module->members())
1803 if (auto *inst = member.as_if<
slang::ast::InstanceSymbol>())
1804 if (
llvm::StringRef(inst->name.
data(), inst->name.size()) ==
1805 name.take_front(dot)) {
1806 hierValue = hierValueSymbols.lookup({inst, innerName});
1809 }
else if (
auto *sym =
1810 module->find(std::string_view(name.data(), name.size()))) {
1812 if (
auto *valueSym = sym->as_if<slang::ast::ValueSymbol>())
1813 hierValue = valueSymbols.lookup(valueSym);
1817 return mlir::emitError(lowering.op.getLoc())
1818 <<
"unable to resolve hierarchical output `"
1819 << hierPath.hierName.getValue() <<
"` in module `" <<
module->name
1821 outputs.push_back(hierValue);
1824 moore::OutputOp::create(builder, lowering.op.getLoc(), outputs);
1834 timeScale = package.getTimeScale().value_or(slang::TimeScale());
1835 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
1839 OpBuilder::InsertionGuard g(
builder);
1842 for (
auto &member : package.members()) {
1844 if (failed(member.visit(PackageVisitor(*
this, loc))))
1855 auto &lowering =
functions[&subroutine];
1857 if (!lowering->op.getOperation())
1859 return lowering.get();
1862 if (!subroutine.thisVar) {
1864 SmallString<64> name;
1866 name += subroutine.name;
1868 SmallVector<Type, 1> noThis = {};
1875 const slang::ast::Type &thisTy = subroutine.thisVar->getType();
1876 moore::ClassDeclOp ownerDecl;
1878 if (
auto *classTy = thisTy.as_if<slang::ast::ClassType>()) {
1879 auto &ownerLowering =
classes[classTy];
1880 ownerDecl = ownerLowering->op;
1882 mlir::emitError(loc) <<
"expected 'this' to be a class type, got "
1883 << thisTy.toString();
1888 SmallString<64> qualName;
1889 qualName += ownerDecl.getSymName();
1891 qualName += subroutine.name;
1894 SmallVector<Type, 1> extraParams;
1896 auto classSym = mlir::FlatSymbolRefAttr::get(ownerDecl.getSymNameAttr());
1897 auto handleTy = moore::ClassHandleType::get(
getContext(), classSym);
1898 extraParams.push_back(handleTy);
1908 Context &
context,
const slang::ast::SubroutineSymbol &subroutine,
1909 ArrayRef<Type> prefixParams, ArrayRef<Type> suffixParams = {}) {
1910 using slang::ast::ArgumentDirection;
1912 SmallVector<Type> inputTypes;
1913 inputTypes.append(prefixParams.begin(), prefixParams.end());
1914 SmallVector<Type, 1> outputTypes;
1916 for (
const auto *arg : subroutine.getArguments()) {
1917 auto type =
context.convertType(arg->getType());
1920 if (arg->direction == ArgumentDirection::In) {
1921 inputTypes.push_back(type);
1923 inputTypes.push_back(
1924 moore::RefType::get(cast<moore::UnpackedType>(type)));
1928 inputTypes.append(suffixParams.begin(), suffixParams.end());
1930 const auto &returnType = subroutine.getReturnType();
1931 if (!returnType.isVoid()) {
1932 auto type =
context.convertType(returnType);
1935 outputTypes.push_back(type);
1938 return FunctionType::get(
context.getContext(), inputTypes, outputTypes);
1941static FailureOr<SmallVector<moore::DPIArgInfo>>
1943 const slang::ast::SubroutineSymbol &subroutine) {
1944 using slang::ast::ArgumentDirection;
1946 SmallVector<moore::DPIArgInfo> args;
1947 args.reserve(subroutine.getArguments().size() +
1948 (!subroutine.getReturnType().isVoid() ? 1 : 0));
1950 for (
const auto *arg : subroutine.getArguments()) {
1951 auto type =
context.convertType(arg->getType());
1954 moore::DPIArgDirection dir;
1955 switch (arg->direction) {
1956 case ArgumentDirection::In:
1957 dir = moore::DPIArgDirection::In;
1959 case ArgumentDirection::Out:
1960 dir = moore::DPIArgDirection::Out;
1962 case ArgumentDirection::InOut:
1963 dir = moore::DPIArgDirection::InOut;
1965 case ArgumentDirection::Ref:
1966 llvm_unreachable(
"'ref' is not legal for DPI functions");
1969 {StringAttr::get(
context.getContext(), arg->name), type, dir});
1972 if (!subroutine.getReturnType().isVoid()) {
1973 auto type =
context.convertType(subroutine.getReturnType());
1976 args.push_back({StringAttr::get(
context.getContext(),
"return"), type,
1977 moore::DPIArgDirection::Return});
1987 mlir::StringRef qualifiedName,
1988 llvm::SmallVectorImpl<Type> &extraParams) {
1992 OpBuilder::InsertionGuard g(
builder);
1998 builder.setInsertionPoint(it->second);
2003 SmallVector<Type> captureTypes;
2006 for (
auto *sym : capturesIt->second) {
2010 captureTypes.push_back(
2011 moore::RefType::get(cast<moore::UnpackedType>(type)));
2020 std::unique_ptr<FunctionLowering> lowering;
2021 Operation *insertedOp =
nullptr;
2026 auto setVisibilityAndExportAttr = [&](Operation *op) {
2029 builder.getStringAttr(dpiExportIt->second));
2030 SymbolTable::setSymbolVisibility(op, SymbolTable::Visibility::Public);
2033 SymbolTable::setSymbolVisibility(op, SymbolTable::Visibility::Private);
2035 if (!subroutine.thisVar &&
2036 subroutine.flags.has(slang::ast::MethodFlags::DPIImport)) {
2042 auto dpiOp = moore::DPIFuncOp::create(
2045 StringAttr::get(
getContext(), subroutine.name));
2046 setVisibilityAndExportAttr(dpiOp);
2047 lowering = std::make_unique<FunctionLowering>(dpiOp);
2049 }
else if (subroutine.subroutineKind == slang::ast::SubroutineKind::Task) {
2051 auto op = moore::CoroutineOp::create(
builder, loc, qualifiedName, funcTy);
2052 setVisibilityAndExportAttr(op);
2053 lowering = std::make_unique<FunctionLowering>(op);
2058 mlir::func::FuncOp::create(
builder, loc, qualifiedName, funcTy);
2059 setVisibilityAndExportAttr(funcOp);
2060 lowering = std::make_unique<FunctionLowering>(funcOp);
2061 insertedOp = funcOp;
2067 lowering->capturedSymbols.assign(capturesIt->second.begin(),
2068 capturesIt->second.end());
2073 functions[&subroutine] = std::move(lowering);
2087 auto *lowering =
functions.at(&subroutine).get();
2092 timeScale = subroutine.getTimeScale().value_or(slang::TimeScale());
2093 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
2098 if (subroutine.flags.has(slang::ast::MethodFlags::DPIImport))
2101 const bool isMethod = (subroutine.thisVar !=
nullptr);
2106 if (
const auto *classTy =
2107 subroutine.thisVar->getType().as_if<slang::ast::ClassType>()) {
2108 for (
auto &member : classTy->members()) {
2109 const auto *prop = member.as_if<slang::ast::ClassPropertySymbol>();
2112 const auto &propCanon = prop->getType().getCanonicalType();
2113 if (
const auto *vi =
2114 propCanon.as_if<slang::ast::VirtualInterfaceType>()) {
2124 SmallVector<moore::VariableOp> argVariables;
2125 auto &block = lowering->op.getFunctionBody().emplaceBlock();
2132 cast<FunctionType>(lowering->op.getFunctionType()).getInput(0);
2133 auto thisArg = block.addArgument(thisType, thisLoc);
2141 auto inputs = cast<FunctionType>(lowering->op.getFunctionType()).getInputs();
2142 auto astArgs = subroutine.getArguments();
2143 unsigned prefixCount = isMethod ? 1 : 0;
2144 auto valInputs = llvm::ArrayRef<Type>(inputs)
2145 .drop_front(prefixCount)
2146 .take_front(astArgs.size());
2148 for (
auto [astArg, type] : llvm::zip(astArgs, valInputs)) {
2150 auto blockArg = block.addArgument(type, loc);
2152 if (isa<moore::RefType>(type)) {
2155 OpBuilder::InsertionGuard g(
builder);
2156 builder.setInsertionPointToEnd(&block);
2158 auto shadowArg = moore::VariableOp::create(
2159 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
2160 StringAttr{}, blockArg);
2162 argVariables.push_back(shadowArg);
2165 const auto &argCanon = astArg->getType().getCanonicalType();
2166 if (
const auto *vi = argCanon.as_if<slang::ast::VirtualInterfaceType>())
2172 OpBuilder::InsertionGuard g(
builder);
2173 builder.setInsertionPointToEnd(&block);
2176 if (subroutine.returnValVar) {
2177 auto type =
convertType(*subroutine.returnValVar->getDeclaredType());
2180 returnVar = moore::VariableOp::create(
2181 builder, lowering->op->getLoc(),
2182 moore::RefType::get(cast<moore::UnpackedType>(type)), StringAttr{},
2184 valueSymbols.insert(subroutine.returnValVar, returnVar);
2192 for (
auto *sym : lowering->capturedSymbols) {
2196 auto refType = moore::RefType::get(cast<moore::UnpackedType>(type));
2198 auto blockArg = block.addArgument(refType, loc);
2204 llvm::scope_exit restoreThis([&] {
currentThisRef = savedThis; });
2208 llvm::scope_exit restoreFunctionLowering(
2217 if (isa<moore::CoroutineOp>(lowering->op.getOperation())) {
2218 moore::ReturnOp::create(
builder, lowering->op->getLoc());
2219 }
else if (returnVar && !subroutine.getReturnType().isVoid()) {
2221 moore::ReadOp::create(
builder, returnVar.getLoc(), returnVar);
2222 mlir::func::ReturnOp::create(
builder, lowering->op->getLoc(), read);
2224 mlir::func::ReturnOp::create(
builder, lowering->op->getLoc(),
2228 if (returnVar && returnVar.use_empty())
2229 returnVar.getDefiningOp()->erase();
2231 for (
auto var : argVariables) {
2232 if (llvm::all_of(var->getUsers(),
2233 [](
auto *user) { return isa<moore::ReadOp>(user); })) {
2234 for (
auto *user : llvm::make_early_inc_range(var->getUsers())) {
2235 user->getResult(0).replaceAllUsesWith(var.getInitial());
2247 const slang::ast::TimingControl *delay,
2250 const slang::ast::Expression *delayExpr;
2251 if (
const auto *delay3 = delay->as_if<slang::ast::Delay3Control>()) {
2252 if (delay3->expr2 || delay3->expr3)
2253 return mlir::emitError(loc) <<
"only primitives that specify a "
2254 "single delay are currently supported.";
2255 delayExpr = &delay3->expr1;
2256 }
else if (
const auto *delayControl =
2257 delay->as_if<slang::ast::DelayControl>()) {
2258 delayExpr = &delayControl->expr;
2260 llvm_unreachable(
"unexpected delay control type in primitive instance");
2263 *delayExpr, moore::TimeType::get(
getContext()));
2266 moore::DelayedContinuousAssignOp::create(
builder, loc, outputVal,
2267 assignment, delayVal);
2269 moore::ContinuousAssignOp::create(
builder, loc, outputVal, assignment);
2277 const slang::ast::PrimitiveInstanceSymbol &prim) {
2278 if (prim.getDriveStrength().first.has_value() ||
2279 prim.getDriveStrength().second.has_value())
2281 <<
"primitive instances with explicit drive strengths are not "
2284 switch (prim.primitiveType.primitiveKind) {
2285 case slang::ast::PrimitiveSymbol::PrimitiveKind::NInput:
2288 case slang::ast::PrimitiveSymbol::PrimitiveKind::NOutput:
2291 case slang::ast::PrimitiveSymbol::PrimitiveKind::Fixed:
2296 <<
"unsupported instance of primitive `" << prim.primitiveType.name
2302 const slang::ast::PrimitiveInstanceSymbol &prim) {
2304 auto primName = prim.primitiveType.name;
2306 auto portConns = prim.getPortConnections();
2307 assert(portConns.size() >= 2 &&
2308 "n-input primitives should have at least 2 ports");
2312 portConns[0]->as<slang::ast::AssignmentExpression>().left();
2318 SmallVector<Value> inputVals;
2319 inputVals.reserve(portConns.size() - 1);
2320 for (
const auto *inputConn : portConns.subspan(1, portConns.size() - 1)) {
2324 inputVals.push_back(inputVal);
2327 Value nextInput = inputVals.front();
2329 llvm::StringSwitch<std::function<Value()>>(prim.primitiveType.name)
2331 for (Value inputVal : llvm::drop_begin(inputVals))
2333 moore::AndOp::create(
builder, loc, nextInput, inputVal);
2337 for (Value inputVal : llvm::drop_begin(inputVals))
2339 moore::OrOp::create(
builder, loc, nextInput, inputVal);
2343 for (Value inputVal : llvm::drop_begin(inputVals))
2345 moore::XorOp::create(
builder, loc, nextInput, inputVal);
2348 .Case(
"nand", ([&] {
2349 for (Value inputVal : llvm::drop_begin(inputVals))
2351 moore::AndOp::create(
builder, loc, nextInput, inputVal);
2352 return moore::NotOp::create(
builder, loc, nextInput);
2355 for (Value inputVal : llvm::drop_begin(inputVals))
2357 moore::OrOp::create(
builder, loc, nextInput, inputVal);
2358 return moore::NotOp::create(
builder, loc, nextInput);
2360 .Case(
"xnor", ([&] {
2361 for (Value inputVal : llvm::drop_begin(inputVals))
2363 moore::XorOp::create(
builder, loc, nextInput, inputVal);
2364 return moore::NotOp::create(
builder, loc, nextInput);
2367 mlir::emitError(loc)
2368 <<
"unsupported primitive `" << primName <<
"`";
2375 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
2384 const slang::ast::PrimitiveInstanceSymbol &prim) {
2386 auto primName = prim.primitiveType.name;
2388 auto portConns = prim.getPortConnections();
2389 assert(portConns.size() >= 2 &&
2390 "n-output primitives should have at least 2 ports");
2393 SmallVector<Value> outputVals;
2394 outputVals.reserve(portConns.size() - 1);
2395 for (
const auto *outputConn : portConns.subspan(0, portConns.size() - 1)) {
2396 auto &output = outputConn->as<slang::ast::AssignmentExpression>().left();
2400 outputVals.push_back(outputVal);
2408 llvm::StringSwitch<std::function<Value()>>(prim.primitiveType.name)
2410 ([&] {
return moore::NotOp::create(
builder, loc, inputVal); }))
2412 return moore::BoolCastOp::create(
builder, loc, inputVal);
2415 mlir::emitError(loc)
2416 <<
"unsupported primitive `" << primName <<
"`";
2423 for (
auto outputVal : outputVals) {
2424 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
2437 const slang::ast::PrimitiveInstanceSymbol &prim) {
2438 auto primName = prim.primitiveType.name;
2443 if (primName ==
"pullup" || primName ==
"pulldown")
2446 if (primName ==
"bufif0" || primName ==
"bufif1" || primName ==
"notif0" ||
2447 primName ==
"notif1")
2450 if (primName ==
"nmos" || primName ==
"pmos" || primName ==
"rnmos" ||
2451 primName ==
"rpmos") {
2455 if (primName ==
"cmos" || primName ==
"rcmos")
2459 mlir::emitError(loc) <<
"unsupported primitive `" << primName <<
"`";
2464 const slang::ast::PrimitiveInstanceSymbol &prim) {
2465 assert((prim.primitiveType.name ==
"pullup" ||
2466 prim.primitiveType.name ==
"pulldown") &&
2467 "expected pullup or pulldown primitive");
2469 assert(!prim.getDelay() &&
2470 "SystemVerilog does not allow pull gate primitives with delays");
2472 auto primName = prim.primitiveType.name;
2474 auto portConns = prim.getPortConnections();
2476 assert(portConns.size() == 1 &&
2477 "pullup/pulldown primitives should have exactly one port");
2480 portConns.front()->as<slang::ast::AssignmentExpression>().left());
2482 auto dstType = cast<moore::RefType>(portVal.getType()).getNestedType();
2483 auto dstTypeWidth = dstType.getBitSize();
2486 "expected fixed-width type for pullup/pulldown primitive");
2487 auto constVal = primName ==
"pullup" ? -1 : 0;
2488 auto c = moore::ConstantOp::create(
2490 moore::IntType::getInt(this->
getContext(), dstTypeWidth.value()),
2496 moore::ContinuousAssignOp::create(
builder, loc, portVal, converted);
2507 auto dstIntType = cast<moore::IntType>(dstType);
2509 moore::ConstantOp::create(builder, loc, dstIntType,
FVInt::getAllX(1));
2511 moore::ConstantOp::create(builder, loc, dstIntType,
FVInt::getAllZ(1));
2513 auto isZ = moore::CaseEqOp::create(builder, loc, data, zVal);
2514 auto condOp = moore::ConditionalOp::create(builder, loc, dstType, isZ);
2515 auto &trueBlk = condOp.getTrueRegion().emplaceBlock();
2516 auto &falseBlk = condOp.getFalseRegion().emplaceBlock();
2518 OpBuilder::InsertionGuard g(builder);
2519 builder.setInsertionPointToStart(&trueBlk);
2520 moore::YieldOp::create(builder, loc, xVal);
2521 builder.setInsertionPointToStart(&falseBlk);
2522 moore::YieldOp::create(builder, loc, data);
2524 return condOp.getResult();
2528 const slang::ast::PrimitiveInstanceSymbol &prim) {
2530 auto primName = prim.primitiveType.name;
2532 auto portConns = prim.getPortConnections();
2533 assert(portConns.size() == 3 &&
2534 "Expected exactly 3 ports in three-state gate primitives");
2537 portConns[0]->as<slang::ast::AssignmentExpression>().left();
2544 if (!inVal || !enVal)
2547 auto enType = cast<moore::IntType>(enVal.getType());
2548 auto enWidth = enType.getBitSize();
2550 assert(enWidth && *enWidth == 1 &&
2551 "enable signal of a three-state gate primitive must be 1 bit");
2553 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
2554 auto dstWidth = dstType.getBitSize();
2555 if (!dstWidth || *dstWidth != 1)
2556 return mlir::emitError(loc)
2557 <<
"output of a three-state gate primitive must be 1 bit";
2559 if (primName ==
"notif0" || primName ==
"notif1")
2560 inVal = moore::NotOp::create(
builder, loc, inVal);
2567 int inactiveLevel = (primName ==
"bufif1" || primName ==
"notif1") ? 0 : 1;
2568 Value inactiveConst =
2569 moore::ConstantOp::create(
builder, loc, enType, inactiveLevel,
false);
2571 auto dstIntType = cast<moore::IntType>(dstType);
2578 moore::CaseEqOp::create(
builder, loc, enVal, inactiveConst);
2580 moore::ConditionalOp::create(
builder, loc, dstType, condInactive);
2581 auto &inactiveTrue = inactiveOp.getTrueRegion().emplaceBlock();
2582 auto &inactiveFalse = inactiveOp.getFalseRegion().emplaceBlock();
2585 OpBuilder::InsertionGuard g(
builder);
2586 builder.setInsertionPointToStart(&inactiveTrue);
2587 moore::YieldOp::create(
builder, loc, zVal);
2588 builder.setInsertionPointToStart(&inactiveFalse);
2589 moore::YieldOp::create(
builder, loc,
2593 Value result = inactiveOp.getResult();
2599 const slang::ast::PrimitiveInstanceSymbol &prim) {
2601 prim.primitiveType.name ==
"nmos" || prim.primitiveType.name ==
"pmos" ||
2602 prim.primitiveType.name ==
"rnmos" || prim.primitiveType.name ==
"rpmos");
2605 auto primName = prim.primitiveType.name;
2607 auto portConns = prim.getPortConnections();
2609 assert(portConns.size() == 3 &&
"mos primitive should have exactly 3 ports");
2612 portConns[0]->as<slang::ast::AssignmentExpression>().left();
2623 assert(cast<moore::IntType>(inputVal.getType()).getBitSize() == 1 &&
2624 "MOS switch input must be 1 bit");
2632 auto controlType = cast<moore::IntType>(control.getType());
2633 assert(controlType.getBitSize() == 1 &&
"MOS switch control must be 1 bit");
2635 int offLevel = (primName ==
"nmos" || primName ==
"rnmos") ? 0 : 1;
2636 auto offValue = moore::ConstantOp::create(
2637 builder, loc, controlType,
FVInt(1,
static_cast<uint64_t
>(offLevel)));
2639 controlIsOff = moore::CaseEqOp::create(
builder, loc, control, offValue);
2640 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
2642 auto dstIntType = dyn_cast<moore::IntType>(dstType);
2643 if (!dstIntType || dstIntType.getBitSize() != 1)
2644 return mlir::emitError(loc) <<
"MOS switch output must be 1 bit";
2647 if (!convertedInput)
2654 moore::ConditionalOp::create(
builder, loc, dstType, controlIsOff);
2656 auto &trueBlock = condOp.getTrueRegion().emplaceBlock();
2657 auto &falseBlock = condOp.getFalseRegion().emplaceBlock();
2659 builder.setInsertionPointToStart(&trueBlock);
2660 moore::YieldOp::create(
builder, loc, zVal);
2662 builder.setInsertionPointToStart(&falseBlock);
2663 moore::YieldOp::create(
builder, loc, convertedInput);
2665 builder.setInsertionPointAfter(condOp);
2668 prim.getDelay(), loc);
2672 const slang::ast::PrimitiveInstanceSymbol &prim) {
2673 assert(prim.primitiveType.name ==
"cmos" ||
2674 prim.primitiveType.name ==
"rcmos");
2677 auto portConns = prim.getPortConnections();
2678 assert(portConns.size() == 4 &&
"cmos primitive should have exactly 4 ports");
2681 portConns[0]->as<slang::ast::AssignmentExpression>().left();
2690 assert(cast<moore::IntType>(dataVal.getType()).getBitSize() == 1 &&
2691 "CMOS switch input must be 1 bit");
2696 assert(cast<moore::IntType>(ncontrolVal.getType()).getBitSize() == 1 &&
2697 "CMOS switch ncontrol must be 1 bit");
2702 assert(cast<moore::IntType>(pcontrolVal.getType()).getBitSize() == 1 &&
2703 "CMOS switch pcontrol must be 1 bit");
2705 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
2706 auto dstIntType = dyn_cast<moore::IntType>(dstType);
2707 if (!dstIntType || dstIntType.getBitSize() != 1)
2708 return mlir::emitError(loc) <<
"CMOS switch output must be 1 bit";
2714 auto logicType = moore::IntType::getLogic(
getContext(), 1);
2716 auto makeConst = [&](
FVInt val) -> Value {
2717 Value c = moore::ConstantOp::create(
builder, loc, logicType, val);
2725 auto makeLevelConstant = [&](Value value,
int level) -> Value {
2726 auto type = cast<moore::IntType>(value.getType());
2727 return moore::ConstantOp::create(
builder, loc, type,
2728 FVInt(1,
static_cast<uint64_t
>(level)));
2731 auto muxZOrData = [&](Value cond) -> Value {
2732 auto condOp = moore::ConditionalOp::create(
builder, loc, dstType, cond);
2733 auto &trueBlk = condOp.getTrueRegion().emplaceBlock();
2734 auto &falseBlk = condOp.getFalseRegion().emplaceBlock();
2735 builder.setInsertionPointToStart(&trueBlk);
2736 moore::YieldOp::create(
builder, loc, zVal);
2737 builder.setInsertionPointToStart(&falseBlk);
2738 moore::YieldOp::create(
builder, loc, convertedData);
2739 builder.setInsertionPointAfter(condOp);
2740 return condOp.getResult();
2744 auto nOff = makeLevelConstant(ncontrolVal, 0);
2745 auto nIsOff = moore::CaseEqOp::create(
builder, loc, ncontrolVal, nOff);
2746 Value nResult = muxZOrData(nIsOff);
2749 auto pOff = makeLevelConstant(pcontrolVal, 1);
2750 auto pIsOff = moore::CaseEqOp::create(
builder, loc, pcontrolVal, pOff);
2751 Value pResult = muxZOrData(pIsOff);
2753 auto agree = moore::CaseEqOp::create(
builder, loc, nResult, pResult);
2754 auto nIsZ = moore::CaseEqOp::create(
builder, loc, nResult, zVal);
2755 auto pIsZ = moore::CaseEqOp::create(
builder, loc, pResult, zVal);
2757 auto outerCond = moore::ConditionalOp::create(
builder, loc, dstType, agree);
2758 auto &outerTrue = outerCond.getTrueRegion().emplaceBlock();
2759 auto &outerFalse = outerCond.getFalseRegion().emplaceBlock();
2762 OpBuilder::InsertionGuard guard(
builder);
2763 builder.setInsertionPointToStart(&outerTrue);
2764 moore::YieldOp::create(
builder, loc, nResult);
2766 builder.setInsertionPointToStart(&outerFalse);
2767 auto middleCond = moore::ConditionalOp::create(
builder, loc, dstType, nIsZ);
2768 auto &middleTrue = middleCond.getTrueRegion().emplaceBlock();
2769 auto &middleFalse = middleCond.getFalseRegion().emplaceBlock();
2770 moore::YieldOp::create(
builder, loc, middleCond.getResult());
2772 builder.setInsertionPointToStart(&middleTrue);
2773 moore::YieldOp::create(
builder, loc, pResult);
2775 builder.setInsertionPointToStart(&middleFalse);
2776 auto innerCond = moore::ConditionalOp::create(
builder, loc, dstType, pIsZ);
2777 auto &innerTrue = innerCond.getTrueRegion().emplaceBlock();
2778 auto &innerFalse = innerCond.getFalseRegion().emplaceBlock();
2780 builder.setInsertionPointToStart(&innerTrue);
2781 moore::YieldOp::create(
builder, loc, nResult);
2782 builder.setInsertionPointToStart(&innerFalse);
2783 moore::YieldOp::create(
builder, loc, xVal);
2785 builder.setInsertionPointAfter(innerCond);
2786 moore::YieldOp::create(
builder, loc, innerCond.getResult());
2790 prim.getDelay(), loc);
2797mlir::StringAttr fullyQualifiedClassName(
Context &ctx,
2798 const slang::ast::Type &ty) {
2799 SmallString<64> name;
2800 SmallVector<llvm::StringRef, 8> parts;
2802 const slang::ast::Scope *scope = ty.getParentScope();
2804 const auto &sym = scope->asSymbol();
2806 case slang::ast::SymbolKind::Root:
2809 case slang::ast::SymbolKind::InstanceBody:
2810 case slang::ast::SymbolKind::Instance:
2811 case slang::ast::SymbolKind::Package:
2812 case slang::ast::SymbolKind::ClassType:
2813 if (!sym.name.empty())
2814 parts.push_back(sym.name);
2819 scope = sym.getParentScope();
2822 for (
auto p :
llvm::reverse(parts)) {
2827 return mlir::StringAttr::get(ctx.
getContext(), name);
2832std::pair<mlir::SymbolRefAttr, mlir::ArrayAttr>
2834 const slang::ast::ClassType &cls) {
2838 mlir::SymbolRefAttr base;
2839 if (
const auto *b = cls.getBaseClass())
2840 base = mlir::SymbolRefAttr::get(fullyQualifiedClassName(
context, *b));
2843 SmallVector<mlir::Attribute> impls;
2844 if (
auto ifaces = cls.getDeclaredInterfaces(); !ifaces.empty()) {
2845 impls.reserve(ifaces.size());
2846 for (
const auto *iface : ifaces)
2847 impls.push_back(
mlir::FlatSymbolRefAttr::
get(
2848 fullyQualifiedClassName(
context, *iface)));
2851 mlir::ArrayAttr implArr =
2852 impls.empty() ? mlir::ArrayAttr() :
mlir::ArrayAttr::
get(ctx, impls);
2854 return {base, implArr};
2859struct ClassDeclVisitorBase {
2865 :
context(ctx), builder(ctx.builder), classLowering(lowering) {}
2869 return context.convertLocation(sloc);
2875struct ClassPropertyVisitor : ClassDeclVisitorBase {
2876 using ClassDeclVisitorBase::ClassDeclVisitorBase;
2879 LogicalResult
run(
const slang::ast::ClassType &classAST) {
2880 if (!classLowering.
op.getBody().empty())
2883 OpBuilder::InsertionGuard ig(builder);
2885 Block *body = &classLowering.
op.getBody().emplaceBlock();
2886 builder.setInsertionPointToEnd(body);
2889 for (
const auto &mem : classAST.members()) {
2890 if (
const auto *prop = mem.as_if<slang::ast::ClassPropertySymbol>()) {
2891 if (failed(prop->visit(*
this)))
2900 LogicalResult visit(
const slang::ast::ClassPropertySymbol &prop) {
2902 auto ty =
context.convertType(prop.getType());
2906 if (prop.lifetime == slang::ast::VariableLifetime::Automatic) {
2907 moore::ClassPropertyDeclOp::create(builder, loc, prop.name,
2916 if (!
context.globalVariables.lookup(&prop))
2917 return context.convertGlobalVariable(prop);
2922 LogicalResult visit(
const slang::ast::ClassType &cls) {
2923 return context.buildClassProperties(cls);
2927 template <
typename T>
2928 LogicalResult visit(T &&) {
2935struct ClassMethodVisitor : ClassDeclVisitorBase {
2936 using ClassDeclVisitorBase::ClassDeclVisitorBase;
2939 LogicalResult
run(
const slang::ast::ClassType &classAST) {
2943 if (classLowering.
op.getBody().empty())
2946 OpBuilder::InsertionGuard ig(builder);
2947 builder.setInsertionPointToEnd(&classLowering.
op.getBody().front());
2950 for (
const auto &mem : classAST.members()) {
2951 if (failed(mem.visit(*
this)))
2960 LogicalResult visit(
const slang::ast::ClassPropertySymbol &) {
2966 LogicalResult visit(
const slang::ast::ParameterSymbol &) {
return success(); }
2970 LogicalResult visit(
const slang::ast::TypeParameterSymbol &) {
2976 LogicalResult visit(
const slang::ast::TypeAliasType &) {
return success(); }
2979 LogicalResult visit(
const slang::ast::GenericClassDefSymbol &) {
2984 LogicalResult visit(
const slang::ast::TransparentMemberSymbol &) {
2989 LogicalResult visit(
const slang::ast::EmptyMemberSymbol &) {
2994 LogicalResult visit(
const slang::ast::SubroutineSymbol &fn) {
2995 if (fn.flags & slang::ast::MethodFlags::BuiltIn) {
2996 static bool remarkEmitted =
false;
3000 mlir::emitRemark(classLowering.
op.getLoc())
3001 <<
"Class builtin functions (needed for randomization, constraints, "
3002 "and covergroups) are not yet supported and will be dropped "
3004 remarkEmitted =
true;
3008 const mlir::UnitAttr isVirtual =
3009 (fn.flags & slang::ast::MethodFlags::Virtual)
3010 ? UnitAttr::get(
context.getContext())
3017 if (fn.flags & slang::ast::MethodFlags::Pure) {
3019 SmallVector<Type, 1> extraParams;
3021 mlir::FlatSymbolRefAttr::get(classLowering.
op.getSymNameAttr());
3023 moore::ClassHandleType::get(
context.getContext(), classSym);
3024 extraParams.push_back(handleTy);
3028 mlir::emitError(loc) <<
"Invalid function signature for " << fn.name;
3032 moore::ClassMethodDeclOp::create(builder, loc, fn.name,
3033 {}, funcTy,
nullptr);
3037 auto *lowering =
context.declareFunction(fn);
3046 FunctionType fnTy = cast<FunctionType>(lowering->op.getFunctionType());
3048 moore::ClassMethodDeclOp::create(
3049 builder, loc, fn.name, {}, fnTy,
3050 SymbolRefAttr::get(lowering->op.getNameAttr()));
3067 LogicalResult visit(
const slang::ast::MethodPrototypeSymbol &fn) {
3068 const auto *externImpl = fn.getSubroutine();
3072 <<
"Didn't find an implementation matching the forward declaration "
3077 return visit(*externImpl);
3081 LogicalResult visit(
const slang::ast::ClassType &cls) {
3082 if (failed(
context.buildClassProperties(cls)))
3084 return context.materializeClassMethods(cls);
3088 template <
typename T>
3089 LogicalResult visit(T &&node) {
3090 Location loc = UnknownLoc::get(
context.getContext());
3091 if constexpr (
requires { node.location; })
3093 mlir::emitError(loc) <<
"unsupported construct in ClassType members: "
3094 << slang::ast::toString(node.kind);
3102 auto &lowering =
classes[&cls];
3104 return lowering.get();
3105 lowering = std::make_unique<ClassLowering>();
3110 OpBuilder::InsertionGuard g(
builder);
3116 builder.setInsertionPoint(it->second);
3118 auto symName = fullyQualifiedClassName(*
this, cls);
3120 auto [base, impls] = buildBaseAndImplementsAttrs(*
this, cls);
3121 auto classDeclOp = moore::ClassDeclOp::create(
3122 builder, loc, symName, {}, base, impls);
3124 SymbolTable::setSymbolVisibility(classDeclOp,
3125 SymbolTable::Visibility::Public);
3127 lowering->op = classDeclOp;
3130 return lowering.get();
3137 timeScale = classdecl.getTimeScale().value_or(slang::TimeScale());
3138 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
3145 if (classdecl.getBaseClass()) {
3146 if (
const auto *baseClassDecl =
3147 classdecl.getBaseClass()->as_if<slang::ast::ClassType>()) {
3158 return ClassPropertyVisitor(*
this, *lowering).run(classdecl);
3165 timeScale = classdecl.getTimeScale().value_or(slang::TimeScale());
3166 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
3169 auto *lowering =
classes[&classdecl].get();
3176 if (classdecl.getBaseClass()) {
3177 if (
const auto *baseClassDecl =
3178 classdecl.getBaseClass()->as_if<slang::ast::ClassType>()) {
3184 return ClassMethodVisitor(*
this, *lowering).run(classdecl);
3194 OpBuilder::InsertionGuard g(
builder);
3200 builder.setInsertionPoint(it->second);
3204 SmallString<64> symName;
3208 if (
const auto *classVar = var.as_if<slang::ast::ClassPropertySymbol>()) {
3209 if (
const auto *parentScope = classVar->getParentScope()) {
3210 if (
const auto *parentClass =
3211 parentScope->asSymbol().as_if<slang::ast::ClassType>())
3212 symName = fullyQualifiedClassName(*
this, *parentClass);
3214 mlir::emitError(loc)
3215 <<
"Could not access parent class of class property "
3220 mlir::emitError(loc) <<
"Could not get parent scope of class property "
3225 symName += var.name;
3228 symName += var.name;
3237 auto varOp = moore::GlobalVariableOp::create(
builder, loc, symName,
3239 cast<moore::UnpackedType>(type));
3250 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 Value collapseZToX(OpBuilder &builder, Location loc, Value data, Type dstType)
Yields data unless it is exactly Z, in which case yields X instead.
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 constexpr StringLiteral dpiExportAttrName
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 void recordDPIExportDirectives(Context &context, const slang::ast::Scope &scope, const slang::syntax::SyntaxNode *syntax)
Record export "DPI-C" directives in the given scope so that callable declarations can be tagged with ...
static moore::NetKind convertNetKind(slang::ast::NetType::NetKind kind)
Four-valued arbitrary precision integers.
static FVInt getAllZ(unsigned numBits)
Construct an FVInt with all bits set to Z.
static FVInt getAllX(unsigned numBits)
Construct an FVInt with all bits set to X.
const slang::ast::InstanceBodySymbol * getCanonicalBody(const slang::ast::InstanceSymbol &inst)
Get the slang canonical body for the given instance, if there is one.
CaptureMap analyzeFunctionCaptures(const slang::ast::RootSymbol &root, SmallVectorImpl< AmbiguousHierCapture > &ambiguous)
Analyze the AST rooted at root to determine which variables each function captures: symbols reference...
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, List[str] 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.
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...
void populateSampledValueClocks()
Generates a map from sampled value system calls to clocks using Slang's analysis.
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.
const slang::ast::DefinitionSymbol * currentDefinition
The definition symbol of the module body currently being converted.
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)
LogicalResult assignPrimOutputWithDelay(Value outputVal, Value assignment, const slang::ast::TimingControl *delay, Location loc)
Creates a delayed assignment based on the given TimingControl delay (or without a delay if the Timing...
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.
DenseSet< const slang::ast::InstanceSymbol * > predeclaredInstances
Module instances already emitted by the predeclaration pass.
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
DenseMap< const slang::ast::SubroutineSymbol *, std::string > dpiExportCNames
DPI-C export directives keyed by the SystemVerilog subroutine they expose.
LogicalResult convertCMOSSwitchPrimitive(const slang::ast::PrimitiveInstanceSymbol &prim)
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.
LogicalResult convertMOSSwitchPrimitive(const slang::ast::PrimitiveInstanceSymbol &prim)
VirtualInterfaceMembers virtualIfaceMembers
Value currentThisRef
Variable to track the value of the current function's implicit this reference.
const slang::SourceManager & sourceManager
Value materializeConversion(Type type, Value value, bool isSigned, Location loc, bool fallible=false)
Helper function to insert the necessary operations to cast a value from one type to another.
void traverseInstanceBody(const slang::ast::InstanceSymbol &symbol)
std::map< LocationKey, Operation * > orderedRootOps
The top-level operations ordered by their Slang source location.
FunctionLowering * currentFunctionLowering
The function currently being converted, if any.
InterfaceInstances::ScopeTy InterfaceInstanceScope
LogicalResult convertThreeStateGatePrimitive(const slang::ast::PrimitiveInstanceSymbol &prim)
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.
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.