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"
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) {
100 if (failed(context.buildClassProperties(classdecl)))
102 return context.materializeClassMethods(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::PrimitiveSymbol &primitive) {
139 return context.convertUserDefinedPrimitiveDef(primitive);
143 LogicalResult visit(
const slang::ast::ParameterSymbol ¶m) {
144 visitParameter(param);
148 LogicalResult visit(
const slang::ast::SpecparamSymbol ¶m) {
149 visitParameter(param);
153 template <
class Node>
154 void visitParameter(
const Node ¶m) {
158 if (!context.options.debugInfo)
161 context.materializeConstant(param.getValue(), param.getType(), loc);
164 if (builder.getInsertionBlock()->getParentOp() == context.intoModuleOp) {
166 context.orderedRootOps.insert({key, value.getDefiningOp()});
171 SmallString<64> paramName;
173 paramName += param.name;
175 debug::VariableOp::create(builder, loc, builder.getStringAttr(paramName),
186struct RootVisitor :
public BaseVisitor {
187 using BaseVisitor::BaseVisitor;
188 using BaseVisitor::visit;
191 LogicalResult visit(
const slang::ast::PackageSymbol &package) {
192 return context.convertPackage(package);
196 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
197 if (!
context.declareFunction(subroutine))
203 LogicalResult visit(
const slang::ast::VariableSymbol &var) {
204 return context.convertGlobalVariable(var);
208 template <
typename T>
209 LogicalResult visit(T &&node) {
210 mlir::emitError(loc,
"unsupported construct: ")
211 << slang::ast::toString(node.kind);
222struct PackageVisitor :
public BaseVisitor {
223 using BaseVisitor::BaseVisitor;
224 using BaseVisitor::visit;
227 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
228 if (!
context.declareFunction(subroutine))
234 LogicalResult visit(
const slang::ast::VariableSymbol &var) {
235 return context.convertGlobalVariable(var);
239 template <
typename T>
240 LogicalResult visit(T &&node) {
241 mlir::emitError(loc,
"unsupported package member: ")
242 << slang::ast::toString(node.kind);
252static moore::ProcedureKind
255 case slang::ast::ProceduralBlockKind::Always:
256 return moore::ProcedureKind::Always;
257 case slang::ast::ProceduralBlockKind::AlwaysComb:
258 return moore::ProcedureKind::AlwaysComb;
259 case slang::ast::ProceduralBlockKind::AlwaysLatch:
260 return moore::ProcedureKind::AlwaysLatch;
261 case slang::ast::ProceduralBlockKind::AlwaysFF:
262 return moore::ProcedureKind::AlwaysFF;
263 case slang::ast::ProceduralBlockKind::Initial:
264 return moore::ProcedureKind::Initial;
265 case slang::ast::ProceduralBlockKind::Final:
266 return moore::ProcedureKind::Final;
268 llvm_unreachable(
"all procedure kinds handled");
273 case slang::ast::NetType::Supply0:
274 return moore::NetKind::Supply0;
275 case slang::ast::NetType::Supply1:
276 return moore::NetKind::Supply1;
277 case slang::ast::NetType::Tri:
278 return moore::NetKind::Tri;
279 case slang::ast::NetType::TriAnd:
280 return moore::NetKind::TriAnd;
281 case slang::ast::NetType::TriOr:
282 return moore::NetKind::TriOr;
283 case slang::ast::NetType::TriReg:
284 return moore::NetKind::TriReg;
285 case slang::ast::NetType::Tri0:
286 return moore::NetKind::Tri0;
287 case slang::ast::NetType::Tri1:
288 return moore::NetKind::Tri1;
289 case slang::ast::NetType::UWire:
290 return moore::NetKind::UWire;
291 case slang::ast::NetType::Wire:
292 return moore::NetKind::Wire;
293 case slang::ast::NetType::WAnd:
294 return moore::NetKind::WAnd;
295 case slang::ast::NetType::WOr:
296 return moore::NetKind::WOr;
297 case slang::ast::NetType::Interconnect:
298 return moore::NetKind::Interconnect;
299 case slang::ast::NetType::UserDefined:
300 return moore::NetKind::UserDefined;
301 case slang::ast::NetType::Unknown:
302 return moore::NetKind::Unknown;
304 llvm_unreachable(
"all net kinds handled");
308struct ModuleVisitor :
public BaseVisitor {
309 using BaseVisitor::visit;
313 StringRef blockNamePrefix;
315 ModuleVisitor(Context &
context, Location loc, StringRef blockNamePrefix =
"")
316 : BaseVisitor(
context, loc), blockNamePrefix(blockNamePrefix) {}
319 LogicalResult visit(
const slang::ast::PortSymbol &) {
return success(); }
320 LogicalResult visit(
const slang::ast::MultiPortSymbol &) {
return success(); }
321 LogicalResult visit(
const slang::ast::InterfacePortSymbol &) {
326 LogicalResult visit(
const slang::ast::GenvarSymbol &genvarNode) {
331 LogicalResult visit(
const slang::ast::DefParamSymbol &) {
return success(); }
335 LogicalResult visit(
const slang::ast::TypeParameterSymbol &) {
343 expandInterfaceInstance(
const slang::ast::InstanceSymbol &instNode) {
344 auto prefix = (Twine(blockNamePrefix) + instNode.name +
"_").str();
345 auto lowering = std::make_unique<InterfaceLowering>();
346 Context::ValueSymbolScope scope(
context.valueSymbols);
348 auto recordMember = [&](
const slang::ast::Symbol &sym,
349 Value value) ->
void {
350 lowering->expandedMembers[&sym] = value;
351 auto nameAttr = builder.getStringAttr(sym.name);
352 lowering->expandedMembersByName[nameAttr] = value;
353 if (
auto *valueSym = sym.as_if<slang::ast::ValueSymbol>())
354 context.valueSymbols.insert(valueSym, value);
357 for (
const auto &member : instNode.body.members()) {
359 if (
const auto *nestedInst = member.as_if<slang::ast::InstanceSymbol>()) {
360 if (nestedInst->body.getDefinition().definitionKind ==
361 slang::ast::DefinitionKind::Interface)
362 return mlir::emitError(loc)
363 <<
"nested interface instances are not supported: `"
364 << nestedInst->name <<
"` inside `" << instNode.name <<
"`";
367 if (
const auto *var = member.as_if<slang::ast::VariableSymbol>()) {
368 auto loweredType =
context.convertType(*var->getDeclaredType());
371 auto varOp = moore::VariableOp::create(
373 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
374 builder.getStringAttr(Twine(prefix) + StringRef(var->name)),
376 recordMember(*var, varOp);
380 if (
const auto *net = member.as_if<slang::ast::NetSymbol>()) {
381 auto loweredType =
context.convertType(*net->getDeclaredType());
385 if (netKind == moore::NetKind::Interconnect ||
386 netKind == moore::NetKind::UserDefined ||
387 netKind == moore::NetKind::Unknown)
388 return mlir::emitError(loc,
"unsupported net kind `")
389 << net->netType.name <<
"`";
390 auto netOp = moore::NetOp::create(
392 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
393 builder.getStringAttr(Twine(prefix) + StringRef(net->name)),
395 recordMember(*net, netOp);
404 for (
const auto *con : instNode.getPortConnections()) {
405 const auto *expr = con->getExpression();
406 const auto *port = con->port.as_if<slang::ast::PortSymbol>();
414 Value lvalue =
context.convertLvalueExpression(*expr);
418 recordMember(*port, lvalue);
419 if (port->internalSymbol) {
420 recordMember(*port->internalSymbol, lvalue);
426 for (
const auto &member : instNode.body.members()) {
427 switch (member.kind) {
428 case slang::ast::SymbolKind::ContinuousAssign:
429 case slang::ast::SymbolKind::ProceduralBlock:
430 case slang::ast::SymbolKind::StatementBlock:
435 auto memberLoc =
context.convertLocation(member.location);
436 if (failed(member.visit(ModuleVisitor(
context, memberLoc, prefix))))
438 if (failed(
context.flushPendingMonitors()))
442 context.interfaceInstanceStorage.push_back(std::move(lowering));
443 context.interfaceInstances.insert(
444 &instNode,
context.interfaceInstanceStorage.back().get());
449 LogicalResult visit(
const slang::ast::InstanceSymbol &instNode) {
450 using slang::ast::ArgumentDirection;
451 using slang::ast::AssignmentExpression;
452 using slang::ast::MultiPortSymbol;
453 using slang::ast::PortSymbol;
455 if (
context.predeclaredInstances.contains(&instNode))
466 auto defKind = body->getDefinition().definitionKind;
467 if (defKind == slang::ast::DefinitionKind::Interface) {
468 if (
context.interfaceInstances.lookup(&instNode))
470 return expandInterfaceInstance(instNode);
473 auto *moduleLowering =
context.convertModuleHeader(body);
476 auto module = moduleLowering->op;
477 auto moduleType =
module.getModuleType();
480 SymbolTable::setSymbolVisibility(module, SymbolTable::Visibility::Private);
486 SmallDenseMap<const PortSymbol *, Value> portValues;
487 portValues.reserve(moduleType.getNumPorts());
490 SmallDenseMap<
const slang::ast::InterfacePortSymbol *,
491 const slang::ast::InstanceSymbol *>
494 for (
const auto *con : instNode.getPortConnections()) {
495 const auto *expr = con->getExpression();
500 auto *port = con->port.as_if<PortSymbol>();
501 if (
auto *existingPort =
502 moduleLowering->portsBySyntaxNode.lookup(port->getSyntax()))
505 switch (port->direction) {
506 case ArgumentDirection::In: {
507 auto refType = moore::RefType::get(
508 cast<moore::UnpackedType>(
context.convertType(port->getType())));
510 if (
const auto *net =
511 port->internalSymbol->as_if<slang::ast::NetSymbol>()) {
512 auto netOp = moore::NetOp::create(
513 builder, loc, refType,
514 StringAttr::get(builder.getContext(), net->name),
516 auto readOp = moore::ReadOp::create(builder, loc, netOp);
517 portValues.insert({port, readOp});
518 }
else if (
const auto *var =
520 ->as_if<slang::ast::VariableSymbol>()) {
521 auto varOp = moore::VariableOp::create(
522 builder, loc, refType,
523 StringAttr::get(builder.getContext(), var->name),
nullptr);
524 auto readOp = moore::ReadOp::create(builder, loc, varOp);
525 portValues.insert({port, readOp});
527 return mlir::emitError(loc)
528 <<
"unsupported internal symbol for unconnected port `"
529 << port->name <<
"`";
536 case ArgumentDirection::Out:
539 case ArgumentDirection::InOut:
540 case ArgumentDirection::Ref: {
541 auto refType = moore::RefType::get(
542 cast<moore::UnpackedType>(
context.convertType(port->getType())));
544 if (
const auto *net =
545 port->internalSymbol->as_if<slang::ast::NetSymbol>()) {
546 auto netOp = moore::NetOp::create(
547 builder, loc, refType,
548 StringAttr::get(builder.getContext(), net->name),
550 portValues.insert({port, netOp});
551 }
else if (
const auto *var =
553 ->as_if<slang::ast::VariableSymbol>()) {
554 auto varOp = moore::VariableOp::create(
555 builder, loc, refType,
556 StringAttr::get(builder.getContext(), var->name),
nullptr);
557 portValues.insert({port, varOp});
559 return mlir::emitError(loc)
560 <<
"unsupported internal symbol for unconnected port `"
561 << port->name <<
"`";
570 if (
const auto *assign = expr->as_if<AssignmentExpression>())
571 expr = &assign->left();
576 if (
auto *port = con->port.as_if<PortSymbol>()) {
578 auto value = (port->direction == ArgumentDirection::In)
579 ?
context.convertRvalueExpression(*expr)
580 :
context.convertLvalueExpression(*expr);
583 if (
auto *existingPort =
584 moduleLowering->portsBySyntaxNode.lookup(con->port.getSyntax()))
589 if (port->direction == ArgumentDirection::InOut) {
590 auto portType = moore::RefType::get(
591 cast<moore::UnpackedType>(
context.convertType(port->getType())));
592 if (value.getType() != portType)
593 return mlir::emitError(loc)
594 <<
"inout port `" << port->name <<
"` expects " << portType
595 <<
" but is connected to " << value.getType();
598 portValues.insert({port, value});
605 if (
const auto *multiPort = con->port.as_if<MultiPortSymbol>()) {
607 auto value =
context.convertLvalueExpression(*expr);
611 for (
const auto *port : llvm::reverse(multiPort->ports)) {
612 if (
auto *existingPort = moduleLowering->portsBySyntaxNode.lookup(
613 con->port.getSyntax()))
615 unsigned width = port->getType().getBitWidth();
616 auto sliceType =
context.convertType(port->getType());
619 Value slice = moore::ExtractRefOp::create(
621 moore::RefType::get(cast<moore::UnpackedType>(sliceType)), value,
624 if (port->direction == ArgumentDirection::In)
625 slice = moore::ReadOp::create(builder, loc, slice);
626 portValues.insert({port, slice});
634 if (
const auto *ifacePort =
635 con->port.as_if<slang::ast::InterfacePortSymbol>()) {
636 auto ifaceConn = con->getIfaceConn();
637 const auto *connInst =
638 ifaceConn.first->as_if<slang::ast::InstanceSymbol>();
640 ifaceConnMap[ifacePort] = connInst;
644 mlir::emitError(loc) <<
"unsupported instance port `" << con->port.name
645 <<
"` (" << slang::ast::toString(con->port.kind)
653 SmallVector<Value> inputValues(moduleLowering->numExplicitInputs);
654 SmallVector<Value> outputValues(moduleLowering->numExplicitOutputs);
656 for (
auto &port : moduleLowering->ports) {
657 auto value = portValues.lookup(&port.ast);
658 if (port.ast.direction == ArgumentDirection::Out)
659 outputValues[*port.outputIdx] = value;
661 inputValues[*port.inputIdx] = value;
667 for (
auto &fp : moduleLowering->ifacePorts) {
668 if (!fp.bodySym || !fp.origin)
671 auto it = ifaceConnMap.find(fp.origin);
672 if (it == ifaceConnMap.end()) {
674 <<
"no interface connection for port `" << fp.name <<
"`";
677 const auto *connInst = it->second;
679 auto *ifaceLowering =
context.interfaceInstances.lookup(connInst);
680 if (!ifaceLowering) {
682 <<
"interface instance `" << connInst->name <<
"` was not expanded";
686 auto valIt = ifaceLowering->expandedMembers.find(fp.bodySym);
687 if (valIt == ifaceLowering->expandedMembers.end()) {
689 <<
"unresolved interface port signal `" << fp.name <<
"`";
692 Value val = valIt->second;
694 outputValues[*fp.outputIdx] = val;
698 if (isa<moore::RefType>(val.getType()) && !isa<moore::RefType>(fp.type))
699 val = moore::ReadOp::create(builder, loc, val);
700 inputValues[*fp.inputIdx] = val;
706 for (
auto [value, type] :
707 llvm::zip(inputValues, moduleType.getInputTypes())) {
711 value =
context.materializeConversion(type, value,
false, value.getLoc());
713 return mlir::emitError(loc) <<
"unsupported port";
720 for (
const auto &hierPath :
context.hierPaths[body]) {
721 assert(!hierPath.valueSyms.empty() &&
"hierPath must have valueSyms");
722 if (!hierPath.hierName || hierPath.direction != ArgumentDirection::In)
727 for (
auto &alias : hierPath.valueSyms)
728 if ((hierValue =
context.valueSymbols.lookup(alias.first)))
730 inputValues.push_back(hierValue);
734 for (
auto value : inputValues)
736 return mlir::emitError(loc) <<
"unsupported port";
744 SmallString<64> instName(blockNamePrefix);
745 if (instNode.arrayPath.empty()) {
746 instName += instNode.name;
748 instName += instNode.getArrayName();
749 slang::SmallVector<slang::ConstantRange, 4> dims;
750 instNode.getArrayDimensions(dims);
751 for (
auto [dim, index] : llvm::zip(dims, instNode.arrayPath)) {
753 Twine(dim.lower() + int32_t(index)).toVector(instName);
758 auto inputNames = builder.getArrayAttr(moduleType.getInputNames());
759 auto outputNames = builder.getArrayAttr(moduleType.getOutputNames());
760 auto inst = moore::InstanceOp::create(
761 builder, loc, moduleType.getOutputTypes(),
762 builder.getStringAttr(instName),
763 FlatSymbolRefAttr::get(module.getSymNameAttr()), inputValues,
764 inputNames, outputNames);
768 auto aliasReachedThroughInstance =
769 [&](
const slang::ast::InstanceBodySymbol *aliasBody) {
770 for (
auto *b = aliasBody; b && b->parentInstance;
771 b = b->parentInstance->getParentScope()->getContainingInstance())
772 if (b->parentInstance == &instNode)
782 for (
const auto &hierPath :
context.hierPaths[body])
783 if (hierPath.idx && hierPath.direction == ArgumentDirection::Out) {
784 auto result = inst->getResult(*hierPath.idx);
785 for (
auto &alias : hierPath.valueSyms)
786 if (aliasReachedThroughInstance(alias.second))
787 context.valueSymbols.insert(alias.first, result);
788 context.hierValueSymbols[{&instNode, hierPath.hierName}] = result;
792 for (
auto [lvalue, output] : llvm::zip(outputValues, inst.getOutputs())) {
795 Value rvalue = output;
796 auto dstType = cast<moore::RefType>(lvalue.getType()).getNestedType();
798 rvalue =
context.materializeConversion(dstType, rvalue,
false, loc);
801 moore::ContinuousAssignOp::create(builder, loc, lvalue, rvalue);
808 LogicalResult visit(
const slang::ast::VariableSymbol &varNode) {
809 auto ref =
context.valueSymbols.lookup(&varNode);
811 return mlir::emitError(loc)
812 <<
"internal error: missing predeclared variable `" << varNode.name
815 auto varOp = ref.getDefiningOp<moore::VariableOp>();
817 return mlir::emitError(loc)
818 <<
"internal error: predeclared variable `" << varNode.name
819 <<
"` is not a moore.variable";
821 if (
const auto *init = varNode.getInitializer()) {
822 auto loweredType = cast<moore::RefType>(ref.getType()).getNestedType();
823 auto initial =
context.convertRvalueExpression(*init, loweredType);
826 varOp.getInitialMutable().assign(initial);
833 LogicalResult visit(
const slang::ast::NetSymbol &netNode) {
834 auto ref =
context.valueSymbols.lookup(&netNode);
836 return mlir::emitError(loc) <<
"internal error: missing predeclared net `"
837 << netNode.name <<
"`";
839 auto netOp = ref.getDefiningOp<moore::NetOp>();
841 return mlir::emitError(loc) <<
"internal error: predeclared net `"
842 << netNode.name <<
"` is not a moore.net";
844 if (
const auto *init = netNode.getInitializer()) {
845 auto loweredType = cast<moore::RefType>(ref.getType()).getNestedType();
846 auto assignment =
context.convertRvalueExpression(*init, loweredType);
849 netOp.getAssignmentMutable().assign(assignment);
855 LogicalResult visit(
const slang::ast::ContinuousAssignSymbol &assignNode) {
857 assignNode.getAssignment().as<slang::ast::AssignmentExpression>();
858 auto lhs =
context.convertLvalueExpression(expr.left());
862 auto rhs =
context.convertRvalueExpression(
863 expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
868 if (
auto *timingCtrl = assignNode.getDelay()) {
869 if (
auto *ctrl = timingCtrl->as_if<slang::ast::DelayControl>()) {
870 auto delay =
context.convertRvalueExpression(
871 ctrl->expr, moore::TimeType::get(builder.getContext()));
874 moore::DelayedContinuousAssignOp::create(builder, loc, lhs, rhs, delay);
877 mlir::emitError(loc) <<
"unsupported delay with rise/fall/turn-off";
882 moore::ContinuousAssignOp::create(builder, loc, lhs, rhs);
887 LogicalResult convertProcedure(moore::ProcedureKind kind,
888 const slang::ast::Statement &body) {
889 if (body.as_if<slang::ast::ConcurrentAssertionStatement>())
890 return context.convertStatement(body);
891 auto procOp = moore::ProcedureOp::create(builder, loc, kind);
892 OpBuilder::InsertionGuard guard(builder);
893 builder.setInsertionPointToEnd(&procOp.getBody().emplaceBlock());
894 Context::ValueSymbolScope scope(
context.valueSymbols);
895 Context::VirtualInterfaceMemberScope vifMemberScope(
897 if (failed(
context.convertStatement(body)))
899 if (builder.getBlock())
900 moore::ReturnOp::create(builder, loc);
904 LogicalResult visit(
const slang::ast::ProceduralBlockSymbol &procNode) {
907 if (
auto *syntax = procNode.getSyntax();
909 syntax->kind == slang::syntax::SyntaxKind::ConcurrentAssertionMember)
910 return context.convertStatement(procNode.getBody());
914 if (
context.options.lowerAlwaysAtStarAsComb) {
915 auto *stmt = procNode.getBody().as_if<slang::ast::TimedStatement>();
916 if (procNode.procedureKind == slang::ast::ProceduralBlockKind::Always &&
918 stmt->timing.kind == slang::ast::TimingControlKind::ImplicitEvent)
919 return convertProcedure(moore::ProcedureKind::AlwaysComb, stmt->stmt);
927 LogicalResult visit(
const slang::ast::GenerateBlockSymbol &genNode) {
929 if (genNode.isUninstantiated)
933 SmallString<64> prefix = blockNamePrefix;
934 if (!genNode.name.empty() ||
935 genNode.getParentScope()->asSymbol().kind !=
936 slang::ast::SymbolKind::GenerateBlockArray) {
937 prefix += genNode.getExternalName();
942 for (
auto &member : genNode.members())
943 if (failed(member.visit(ModuleVisitor(
context, loc, prefix))))
949 LogicalResult visit(
const slang::ast::GenerateBlockArraySymbol &genArrNode) {
952 SmallString<64> prefix = blockNamePrefix;
953 prefix += genArrNode.getExternalName();
955 auto prefixBaseLen = prefix.size();
958 for (
const auto *entry : genArrNode.entries) {
960 prefix.resize(prefixBaseLen);
961 if (entry->arrayIndex)
962 prefix += entry->arrayIndex->toString();
964 Twine(entry->constructIndex).toVector(prefix);
968 if (failed(entry->asSymbol().visit(ModuleVisitor(
context, loc, prefix))))
980 LogicalResult visit(
const slang::ast::StatementBlockSymbol &) {
986 LogicalResult visit(
const slang::ast::SequenceSymbol &seqNode) {
992 LogicalResult visit(
const slang::ast::PropertySymbol &propNode) {
998 LogicalResult visit(
const slang::ast::ClockingBlockSymbol &) {
1004 LogicalResult visit(
const slang::ast::LetDeclSymbol &) {
return success(); }
1007 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
1008 if (!
context.declareFunction(subroutine))
1014 LogicalResult visit(
const slang::ast::PrimitiveInstanceSymbol &prim) {
1015 return context.convertPrimitiveInstance(prim);
1019 LogicalResult visit(
const slang::ast::InstanceArraySymbol &arrNode) {
1021 for (
const auto *element : arrNode.elements)
1022 if (failed(element->visit(*
this)))
1028 template <
typename T>
1029 LogicalResult visit(T &&node) {
1030 mlir::emitError(loc,
"unsupported module member: ")
1031 << slang::ast::toString(node.kind);
1036struct ModulePredeclaration {
1040 ModulePredeclaration(Context &context)
1041 : context(context), builder(context.builder) {}
1043 LogicalResult declareVariable(
const slang::ast::VariableSymbol &varNode,
1044 Location loc, StringRef blockNamePrefix) {
1045 auto loweredType = context.convertType(*varNode.getDeclaredType());
1049 auto varOp = moore::VariableOp::create(
1051 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
1052 builder.getStringAttr(Twine(blockNamePrefix) + varNode.name), Value{});
1053 context.valueSymbols.insert(&varNode, varOp);
1055 const auto &canonTy = varNode.getType().getCanonicalType();
1056 if (
const auto *vi = canonTy.as_if<slang::ast::VirtualInterfaceType>())
1057 if (failed(context.registerVirtualInterfaceMembers(varNode, *vi, loc)))
1063 LogicalResult declareNet(
const slang::ast::NetSymbol &netNode, Location loc,
1064 StringRef blockNamePrefix) {
1065 auto loweredType = context.convertType(*netNode.getDeclaredType());
1070 if (netkind == moore::NetKind::Interconnect ||
1071 netkind == moore::NetKind::UserDefined ||
1072 netkind == moore::NetKind::Unknown)
1073 return mlir::emitError(loc,
"unsupported net kind `")
1074 << netNode.netType.name <<
"`";
1076 auto netOp = moore::NetOp::create(
1078 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
1079 builder.getStringAttr(Twine(blockNamePrefix) + netNode.name), netkind,
1081 context.valueSymbols.insert(&netNode, netOp);
1086 getGenerateBlockPrefix(
const slang::ast::GenerateBlockSymbol &genNode,
1087 StringRef blockNamePrefix) {
1088 SmallString<64> prefix = blockNamePrefix;
1089 if (!genNode.name.empty() ||
1090 genNode.getParentScope()->asSymbol().kind !=
1091 slang::ast::SymbolKind::GenerateBlockArray) {
1092 prefix += genNode.getExternalName();
1099 predeclareStorageGenerateBlock(
const slang::ast::GenerateBlockSymbol &genNode,
1100 StringRef blockNamePrefix) {
1101 if (genNode.isUninstantiated)
1103 return predeclareStorageScope(
1104 genNode, getGenerateBlockPrefix(genNode, blockNamePrefix));
1107 LogicalResult predeclareInterfaceGenerateBlock(
1108 const slang::ast::GenerateBlockSymbol &genNode,
1109 StringRef blockNamePrefix) {
1110 if (genNode.isUninstantiated)
1112 return predeclareInterfaceScope(
1113 genNode, getGenerateBlockPrefix(genNode, blockNamePrefix));
1116 LogicalResult predeclareModuleInstanceGenerateBlock(
1117 const slang::ast::GenerateBlockSymbol &genNode,
1118 StringRef blockNamePrefix) {
1119 if (genNode.isUninstantiated)
1121 return predeclareModuleInstanceScope(
1122 genNode, getGenerateBlockPrefix(genNode, blockNamePrefix));
1125 LogicalResult predeclareGenerateBlockArray(
1126 const slang::ast::GenerateBlockArraySymbol &genArrNode,
1127 StringRef blockNamePrefix,
1128 llvm::function_ref<LogicalResult(
const slang::ast::GenerateBlockSymbol &,
1131 SmallString<64> prefix = blockNamePrefix;
1132 prefix += genArrNode.getExternalName();
1134 auto prefixBaseLen = prefix.size();
1136 for (
const auto *entry : genArrNode.entries) {
1137 prefix.resize(prefixBaseLen);
1138 if (entry->arrayIndex)
1139 prefix += entry->arrayIndex->toString();
1141 Twine(entry->constructIndex).toVector(prefix);
1144 if (failed(predeclareBlock(*entry, prefix)))
1150 LogicalResult predeclareStorageMember(
const slang::ast::Symbol &member,
1151 StringRef blockNamePrefix) {
1152 auto loc = context.convertLocation(member.location);
1153 if (
const auto *varNode = member.as_if<slang::ast::VariableSymbol>())
1154 return declareVariable(*varNode, loc, blockNamePrefix);
1156 if (
const auto *netNode = member.as_if<slang::ast::NetSymbol>())
1157 return declareNet(*netNode, loc, blockNamePrefix);
1159 if (
const auto *genNode = member.as_if<slang::ast::GenerateBlockSymbol>())
1160 return predeclareStorageGenerateBlock(*genNode, blockNamePrefix);
1162 if (
const auto *genArrNode =
1163 member.as_if<slang::ast::GenerateBlockArraySymbol>())
1164 return predeclareGenerateBlockArray(
1165 *genArrNode, blockNamePrefix,
1166 [&](
const slang::ast::GenerateBlockSymbol &gen, StringRef prefix) {
1167 return predeclareStorageGenerateBlock(gen, prefix);
1173 LogicalResult predeclareInterfaceMember(
const slang::ast::Symbol &member,
1174 StringRef blockNamePrefix) {
1175 auto loc = context.convertLocation(member.location);
1176 if (
const auto *instNode = member.as_if<slang::ast::InstanceSymbol>()) {
1177 if (instNode->body.getDefinition().definitionKind ==
1178 slang::ast::DefinitionKind::Interface)
1179 return ModuleVisitor(context, loc, blockNamePrefix)
1180 .expandInterfaceInstance(*instNode);
1184 if (
const auto *genNode = member.as_if<slang::ast::GenerateBlockSymbol>())
1185 return predeclareInterfaceGenerateBlock(*genNode, blockNamePrefix);
1187 if (
const auto *genArrNode =
1188 member.as_if<slang::ast::GenerateBlockArraySymbol>())
1189 return predeclareGenerateBlockArray(
1190 *genArrNode, blockNamePrefix,
1191 [&](
const slang::ast::GenerateBlockSymbol &gen, StringRef prefix) {
1192 return predeclareInterfaceGenerateBlock(gen, prefix);
1198 LogicalResult predeclareModuleInstanceMember(
const slang::ast::Symbol &member,
1199 StringRef blockNamePrefix) {
1200 auto loc = context.convertLocation(member.location);
1201 if (
const auto *instNode = member.as_if<slang::ast::InstanceSymbol>()) {
1202 if (instNode->body.getDefinition().definitionKind !=
1203 slang::ast::DefinitionKind::Interface) {
1205 ModuleVisitor(context, loc, blockNamePrefix).visit(*instNode)))
1207 context.predeclaredInstances.insert(instNode);
1212 if (
const auto *arrNode = member.as_if<slang::ast::InstanceArraySymbol>()) {
1213 for (
const auto *element : arrNode->elements)
1214 if (failed(predeclareModuleInstanceMember(*element, blockNamePrefix)))
1219 if (
const auto *genNode = member.as_if<slang::ast::GenerateBlockSymbol>())
1220 return predeclareModuleInstanceGenerateBlock(*genNode, blockNamePrefix);
1222 if (
const auto *genArrNode =
1223 member.as_if<slang::ast::GenerateBlockArraySymbol>())
1224 return predeclareGenerateBlockArray(
1225 *genArrNode, blockNamePrefix,
1226 [&](
const slang::ast::GenerateBlockSymbol &gen, StringRef prefix) {
1227 return predeclareModuleInstanceGenerateBlock(gen, prefix);
1233 LogicalResult predeclareStorageScope(
const slang::ast::Scope &scope,
1234 StringRef blockNamePrefix) {
1235 for (
auto &member : scope.members())
1236 if (failed(predeclareStorageMember(member, blockNamePrefix)))
1241 LogicalResult predeclareInterfaceScope(
const slang::ast::Scope &scope,
1242 StringRef blockNamePrefix) {
1243 for (
auto &member : scope.members())
1244 if (failed(predeclareInterfaceMember(member, blockNamePrefix)))
1249 LogicalResult predeclareModuleInstanceScope(
const slang::ast::Scope &scope,
1250 StringRef blockNamePrefix) {
1251 for (
auto &member : scope.members())
1252 if (failed(predeclareModuleInstanceMember(member, blockNamePrefix)))
1257 LogicalResult predeclareScope(
const slang::ast::Scope &scope,
1258 StringRef blockNamePrefix) {
1262 if (failed(predeclareStorageScope(scope, blockNamePrefix)))
1268 if (failed(predeclareInterfaceScope(scope, blockNamePrefix)))
1273 return predeclareModuleInstanceScope(scope, blockNamePrefix);
1290 timeScale = root.getTimeScale().value_or(slang::TimeScale());
1291 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
1295 SmallVector<AmbiguousHierCapture> ambiguousHierCaptures;
1297 for (
auto &ambiguous : ambiguousHierCaptures) {
1298 auto d = mlir::emitError(
convertLocation(ambiguous.function->location))
1299 <<
"hierarchical reference to `" << ambiguous.symbol->name
1300 <<
"` is ambiguous: this function reaches it through more than "
1301 "one instance of the same module, which is not yet supported";
1303 <<
"symbol declared here";
1305 if (!ambiguousHierCaptures.empty())
1310 for (
auto *inst : root.topInstances)
1320 for (
auto *unit : root.compilationUnits) {
1322 for (
const auto &member : unit->members()) {
1324 if (failed(member.visit(RootVisitor(*
this, loc))))
1332 SmallVector<const slang::ast::InstanceSymbol *> topInstances;
1333 for (
auto *inst : root.topInstances) {
1335 if (body->getDefinition().definitionKind !=
1336 slang::ast::DefinitionKind::Interface)
1343 auto *
module = moduleWorklist.front();
1351 SmallVector<const slang::ast::ClassType *, 16> classMethodWorklist;
1352 classMethodWorklist.reserve(
classes.size());
1354 classMethodWorklist.push_back(kv.first);
1356 for (
auto *inst : classMethodWorklist) {
1375 auto &block = varOp.getInitRegion().emplaceBlock();
1376 OpBuilder::InsertionGuard guard(
builder);
1377 builder.setInsertionPointToEnd(&block);
1382 moore::YieldOp::create(
builder, varOp.getLoc(), value);
1391 using slang::ast::ArgumentDirection;
1392 using slang::ast::MultiPortSymbol;
1393 using slang::ast::ParameterSymbol;
1394 using slang::ast::PortSymbol;
1395 using slang::ast::TypeParameterSymbol;
1400 timeScale =
module->getTimeScale().value_or(slang::TimeScale());
1401 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
1408 slot = std::make_unique<ModuleLowering>();
1409 auto &lowering = *slot;
1412 OpBuilder::InsertionGuard g(
builder);
1417 auto kind =
module->getDefinition().definitionKind;
1418 if (kind != slang::ast::DefinitionKind::Module &&
1419 kind != slang::ast::DefinitionKind::Program) {
1420 mlir::emitError(loc) <<
"unsupported definition: "
1421 <<
module->getDefinition().getKindString();
1426 auto block = std::make_unique<Block>();
1427 SmallVector<hw::ModulePort> modulePorts;
1430 unsigned int outputIdx = 0, inputIdx = 0;
1431 for (
auto *symbol : module->getPortList()) {
1432 auto handlePort = [&](
const PortSymbol &port) {
1437 auto portName =
builder.getStringAttr(port.name);
1439 std::optional<unsigned> portOutputIdx;
1440 std::optional<unsigned> portInputIdx;
1441 if (port.direction == ArgumentDirection::Out) {
1443 portOutputIdx = outputIdx++;
1447 if (port.direction != ArgumentDirection::In)
1448 type = moore::RefType::get(cast<moore::UnpackedType>(type));
1450 arg = block->addArgument(type, portLoc);
1451 portInputIdx = inputIdx++;
1453 lowering.ports.push_back(
1454 {port, portLoc, arg, portOutputIdx, portInputIdx});
1461 auto handleIfacePort = [&](
const slang::ast::InterfacePortSymbol
1464 auto [connSym, modportSym] = ifacePort.getConnection();
1465 const auto *ifaceInst =
1466 connSym ? connSym->as_if<slang::ast::InstanceSymbol>() :
nullptr;
1467 auto portPrefix = (Twine(ifacePort.name) +
"_").str();
1471 for (
const auto &member : modportSym->members()) {
1472 const auto *mpp = member.as_if<slang::ast::ModportPortSymbol>();
1479 builder.getStringAttr(Twine(portPrefix) + StringRef(mpp->name));
1482 std::optional<unsigned> ifaceOutputIdx;
1483 std::optional<unsigned> ifaceInputIdx;
1484 if (mpp->direction == ArgumentDirection::Out) {
1486 modulePorts.push_back({name, type, dir});
1487 ifaceOutputIdx = outputIdx++;
1490 if (mpp->direction != ArgumentDirection::In)
1491 type = moore::RefType::get(cast<moore::UnpackedType>(type));
1492 modulePorts.push_back({name, type, dir});
1493 arg = block->addArgument(type, portLoc);
1494 ifaceInputIdx = inputIdx++;
1496 lowering.ifacePorts.push_back(
1497 {name, dir, type, portLoc, arg, &ifacePort, mpp->internalSymbol,
1498 ifaceInst, mpp, ifaceOutputIdx, ifaceInputIdx});
1503 const auto *instSym = connSym->as_if<slang::ast::InstanceSymbol>();
1505 mlir::emitError(portLoc)
1506 <<
"unsupported interface port connection for `" << ifacePort.name
1510 for (
const auto &member : instSym->body.members()) {
1511 const slang::ast::Type *slangType =
nullptr;
1512 const slang::ast::Symbol *bodySym =
nullptr;
1513 if (
const auto *var = member.as_if<slang::ast::VariableSymbol>()) {
1514 slangType = &var->getType();
1516 }
else if (
const auto *net = member.as_if<slang::ast::NetSymbol>()) {
1517 slangType = &net->getType();
1525 auto name =
builder.getStringAttr(Twine(portPrefix) +
1526 StringRef(bodySym->name));
1527 auto refType = moore::RefType::get(cast<moore::UnpackedType>(type));
1529 auto arg = block->addArgument(refType, portLoc);
1530 lowering.ifacePorts.push_back(
1532 bodySym, instSym,
nullptr, std::nullopt, inputIdx++});
1538 if (
const auto *port = symbol->as_if<PortSymbol>()) {
1539 if (failed(handlePort(*port)))
1541 }
else if (
const auto *multiPort = symbol->as_if<MultiPortSymbol>()) {
1542 for (
auto *port : multiPort->ports)
1543 if (failed(handlePort(*port)))
1545 }
else if (
const auto *ifacePort =
1546 symbol->as_if<slang::ast::InterfacePortSymbol>()) {
1547 if (failed(handleIfacePort(*ifacePort)))
1551 <<
"unsupported module port `" << symbol->name <<
"` ("
1552 << slang::ast::toString(symbol->kind) <<
")";
1558 lowering.numExplicitOutputs = outputIdx;
1559 lowering.numExplicitInputs = inputIdx;
1562 for (
auto &hierPath :
hierPaths[module]) {
1563 assert(!hierPath.valueSyms.empty() &&
"hierPath must have valueSyms");
1564 auto hierType =
convertType(hierPath.valueSyms.front().first->getType());
1568 if (
auto hierName = hierPath.hierName) {
1570 hierType = moore::RefType::get(cast<moore::UnpackedType>(hierType));
1571 if (hierPath.direction == ArgumentDirection::Out) {
1572 hierPath.idx = outputIdx++;
1575 hierPath.idx = inputIdx++;
1579 block->addArgument(hierType, hierLoc);
1583 auto moduleType = hw::ModuleType::get(
getContext(), modulePorts);
1592 builder.setInsertionPoint(it->second);
1596 moore::SVModuleOp::create(
builder, loc, module->name, moduleType);
1597 SmallVector<sv::SVAttributeAttr> svAttrs;
1598 for (
const auto *attr :
compilation.getAttributes(module->getDefinition())) {
1599 StringAttr exprAttr;
1600 if (
const auto *syntax = attr->getSyntax()) {
1601 const auto &spec = syntax->as<slang::syntax::AttributeSpecSyntax>();
1603 if (spec.value->expr->kind ==
1604 slang::syntax::SyntaxKind::StringLiteralExpression) {
1605 exprAttr =
builder.getStringAttr(
1606 spec.value->expr->as<slang::syntax::LiteralExpressionSyntax>()
1607 .literal.rawText());
1609 exprAttr =
builder.getStringAttr(attr->getValue().toString());
1613 svAttrs.push_back(sv::SVAttributeAttr::get(
1617 if (!svAttrs.empty())
1620 moduleOp.getBodyRegion().push_back(block.release());
1621 lowering.op = moduleOp;
1631 for (
const auto &port : lowering.ports)
1632 lowering.portsBySyntaxNode.insert({port.ast.getSyntax(), &port.ast});
1639 auto &lowering = *
modules[module];
1642 llvm::scope_exit currentDefinitionGuard(
1646 OpBuilder::InsertionGuard g(
builder);
1647 builder.setInsertionPointToEnd(lowering.op.getBody());
1656 timeScale =
module->getTimeScale().value_or(slang::TimeScale());
1657 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
1662 for (
auto &hierPath :
hierPaths[module])
1663 if (hierPath.direction == slang::ast::ArgumentDirection::In &&
1665 auto arg = lowering.op.getBody()->getArgument(*hierPath.idx);
1666 for (
auto &alias : hierPath.valueSyms)
1674 DenseMap<const slang::ast::InstanceSymbol *, InterfaceLowering *>
1677 auto getIfacePortLowering =
1683 if (
auto it = ifacePortLowerings.find(ifaceInst);
1684 it != ifacePortLowerings.end())
1687 auto lowering = std::make_unique<InterfaceLowering>();
1691 ifacePortLowerings.try_emplace(ifaceInst, ptr);
1695 for (
auto &fp : lowering.ifacePorts) {
1698 auto *valueSym = fp.bodySym->as_if<slang::ast::ValueSymbol>();
1707 portValue = moore::VariableOp::create(
1709 moore::RefType::get(cast<moore::UnpackedType>(fp.type)), fp.name,
1718 if (fp.modportPortSym)
1719 if (
auto *mppSym = fp.modportPortSym->as_if<slang::ast::ValueSymbol>())
1720 if (mppSym != valueSym)
1723 if (!fp.ifaceInstance)
1726 auto *ifaceLowering = getIfacePortLowering(fp.ifaceInstance);
1729 ifaceLowering->expandedMembers[fp.bodySym] = val;
1731 ->expandedMembersByName[
builder.getStringAttr(fp.bodySym->name)] =
1737 llvm::scope_exit predeclaredInstancesGuard(
1747 if (failed(ModulePredeclaration(*this).predeclareScope(*module,
"")))
1751 for (
auto &member : module->members()) {
1753 if (failed(member.visit(ModuleVisitor(*
this, loc))))
1765 SmallVector<Value> outputs(lowering.numExplicitOutputs);
1766 for (
auto &port : lowering.ports) {
1768 if (
auto *expr = port.ast.getInternalExpr()) {
1770 }
else if (port.ast.internalSymbol) {
1771 if (
const auto *sym =
1772 port.ast.internalSymbol->as_if<slang::ast::ValueSymbol>())
1776 return mlir::emitError(port.loc,
"unsupported port: `")
1778 <<
"` does not map to an internal symbol or expression";
1781 if (port.ast.direction == slang::ast::ArgumentDirection::Out) {
1782 if (isa<moore::RefType>(value.getType()))
1783 value = moore::ReadOp::create(
builder, value.getLoc(), value);
1784 outputs[*port.outputIdx] = value;
1790 Value portArg = port.arg;
1791 if (port.ast.direction != slang::ast::ArgumentDirection::In)
1792 portArg = moore::ReadOp::create(
builder, port.loc, port.arg);
1793 moore::ContinuousAssignOp::create(
builder, port.loc, value, portArg);
1798 for (
auto &fp : lowering.ifacePorts) {
1802 fp.bodySym ? fp.bodySym->as_if<slang::ast::ValueSymbol>() :
nullptr;
1808 outputs[*fp.outputIdx] =
1809 moore::ReadOp::create(
builder, fp.loc, ref).getResult();
1814 for (
auto &hierPath :
hierPaths[module]) {
1815 assert(!hierPath.valueSyms.empty() &&
"hierPath must have valueSyms");
1816 if (hierPath.direction != slang::ast::ArgumentDirection::Out)
1820 for (
auto &alias : hierPath.valueSyms)
1826 auto name = hierPath.hierName.getValue();
1827 if (
auto dot = name.find(
"."); dot != llvm::StringRef::npos) {
1828 auto innerName =
builder.getStringAttr(name.drop_front(dot + 1));
1829 for (
auto &member : module->members())
1830 if (
auto *inst = member.as_if<slang::ast::InstanceSymbol>())
1831 if (llvm::StringRef(inst->name.data(), inst->name.size()) ==
1832 name.take_front(dot)) {
1836 }
else if (
auto *sym =
1837 module->find(std::string_view(name.data(), name.size()))) {
1839 if (
auto *valueSym = sym->as_if<slang::ast::ValueSymbol>())
1844 return mlir::emitError(lowering.op.getLoc())
1845 <<
"unable to resolve hierarchical output `"
1846 << hierPath.hierName.getValue() <<
"` in module `" <<
module->name
1848 outputs.push_back(hierValue);
1851 moore::OutputOp::create(
builder, lowering.op.getLoc(), outputs);
1861 timeScale = package.getTimeScale().value_or(slang::TimeScale());
1862 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
1866 OpBuilder::InsertionGuard g(
builder);
1869 for (
auto &member : package.members()) {
1871 if (failed(member.visit(PackageVisitor(*
this, loc))))
1882 auto &lowering =
functions[&subroutine];
1884 if (!lowering->op.getOperation())
1886 return lowering.get();
1889 if (!subroutine.thisVar) {
1891 SmallString<64> name;
1893 name += subroutine.name;
1895 SmallVector<Type, 1> noThis = {};
1902 const slang::ast::Type &thisTy = subroutine.thisVar->getType();
1903 moore::ClassDeclOp ownerDecl;
1905 if (
auto *classTy = thisTy.as_if<slang::ast::ClassType>()) {
1906 auto &ownerLowering =
classes[classTy];
1907 ownerDecl = ownerLowering->op;
1909 mlir::emitError(loc) <<
"expected 'this' to be a class type, got "
1910 << thisTy.toString();
1915 SmallString<64> qualName;
1916 qualName += ownerDecl.getSymName();
1918 qualName += subroutine.name;
1921 SmallVector<Type, 1> extraParams;
1923 auto classSym = mlir::FlatSymbolRefAttr::get(ownerDecl.getSymNameAttr());
1924 auto handleTy = moore::ClassHandleType::get(
getContext(), classSym);
1925 extraParams.push_back(handleTy);
1935 Context &
context,
const slang::ast::SubroutineSymbol &subroutine,
1936 ArrayRef<Type> prefixParams, ArrayRef<Type> suffixParams = {}) {
1937 using slang::ast::ArgumentDirection;
1939 SmallVector<Type> inputTypes;
1940 inputTypes.append(prefixParams.begin(), prefixParams.end());
1941 SmallVector<Type, 1> outputTypes;
1943 for (
const auto *arg : subroutine.getArguments()) {
1944 auto type =
context.convertType(arg->getType());
1947 if (arg->direction == ArgumentDirection::In) {
1948 inputTypes.push_back(type);
1950 inputTypes.push_back(
1951 moore::RefType::get(cast<moore::UnpackedType>(type)));
1955 inputTypes.append(suffixParams.begin(), suffixParams.end());
1957 const auto &returnType = subroutine.getReturnType();
1958 if (!returnType.isVoid()) {
1959 auto type =
context.convertType(returnType);
1962 outputTypes.push_back(type);
1965 return FunctionType::get(
context.getContext(), inputTypes, outputTypes);
1968static FailureOr<SmallVector<moore::DPIArgInfo>>
1970 const slang::ast::SubroutineSymbol &subroutine) {
1971 using slang::ast::ArgumentDirection;
1973 SmallVector<moore::DPIArgInfo> args;
1974 args.reserve(subroutine.getArguments().size() +
1975 (!subroutine.getReturnType().isVoid() ? 1 : 0));
1977 for (
const auto *arg : subroutine.getArguments()) {
1978 auto type =
context.convertType(arg->getType());
1981 moore::DPIArgDirection dir;
1982 switch (arg->direction) {
1983 case ArgumentDirection::In:
1984 dir = moore::DPIArgDirection::In;
1986 case ArgumentDirection::Out:
1987 dir = moore::DPIArgDirection::Out;
1989 case ArgumentDirection::InOut:
1990 dir = moore::DPIArgDirection::InOut;
1992 case ArgumentDirection::Ref:
1993 llvm_unreachable(
"'ref' is not legal for DPI functions");
1996 {StringAttr::get(
context.getContext(), arg->name), type, dir});
1999 if (!subroutine.getReturnType().isVoid()) {
2000 auto type =
context.convertType(subroutine.getReturnType());
2003 args.push_back({StringAttr::get(
context.getContext(),
"return"), type,
2004 moore::DPIArgDirection::Return});
2014 mlir::StringRef qualifiedName,
2015 llvm::SmallVectorImpl<Type> &extraParams) {
2019 OpBuilder::InsertionGuard g(
builder);
2025 builder.setInsertionPoint(it->second);
2030 SmallVector<Type> captureTypes;
2033 for (
auto *sym : capturesIt->second) {
2037 captureTypes.push_back(
2038 moore::RefType::get(cast<moore::UnpackedType>(type)));
2047 std::unique_ptr<FunctionLowering> lowering;
2048 Operation *insertedOp =
nullptr;
2053 auto setVisibilityAndExportAttr = [&](Operation *op) {
2056 builder.getStringAttr(dpiExportIt->second));
2057 SymbolTable::setSymbolVisibility(op, SymbolTable::Visibility::Public);
2060 SymbolTable::setSymbolVisibility(op, SymbolTable::Visibility::Private);
2062 if (!subroutine.thisVar &&
2063 subroutine.flags.has(slang::ast::MethodFlags::DPIImport)) {
2069 auto dpiOp = moore::DPIFuncOp::create(
2072 StringAttr::get(
getContext(), subroutine.name));
2073 setVisibilityAndExportAttr(dpiOp);
2074 lowering = std::make_unique<FunctionLowering>(dpiOp);
2076 }
else if (subroutine.subroutineKind == slang::ast::SubroutineKind::Task) {
2078 auto op = moore::CoroutineOp::create(
builder, loc, qualifiedName, funcTy);
2079 setVisibilityAndExportAttr(op);
2080 lowering = std::make_unique<FunctionLowering>(op);
2085 mlir::func::FuncOp::create(
builder, loc, qualifiedName, funcTy);
2086 setVisibilityAndExportAttr(funcOp);
2087 lowering = std::make_unique<FunctionLowering>(funcOp);
2088 insertedOp = funcOp;
2094 lowering->capturedSymbols.assign(capturesIt->second.begin(),
2095 capturesIt->second.end());
2100 functions[&subroutine] = std::move(lowering);
2114 auto *lowering =
functions.at(&subroutine).get();
2119 timeScale = subroutine.getTimeScale().value_or(slang::TimeScale());
2120 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
2125 if (subroutine.flags.has(slang::ast::MethodFlags::DPIImport))
2128 const bool isMethod = (subroutine.thisVar !=
nullptr);
2133 if (
const auto *classTy =
2134 subroutine.thisVar->getType().as_if<slang::ast::ClassType>()) {
2135 for (
auto &member : classTy->members()) {
2136 const auto *prop = member.as_if<slang::ast::ClassPropertySymbol>();
2139 const auto &propCanon = prop->getType().getCanonicalType();
2140 if (
const auto *vi =
2141 propCanon.as_if<slang::ast::VirtualInterfaceType>()) {
2151 SmallVector<moore::VariableOp> argVariables;
2152 auto &block = lowering->op.getFunctionBody().emplaceBlock();
2159 cast<FunctionType>(lowering->op.getFunctionType()).getInput(0);
2160 auto thisArg = block.addArgument(thisType, thisLoc);
2168 auto inputs = cast<FunctionType>(lowering->op.getFunctionType()).getInputs();
2169 auto astArgs = subroutine.getArguments();
2170 unsigned prefixCount = isMethod ? 1 : 0;
2171 auto valInputs = llvm::ArrayRef<Type>(inputs)
2172 .drop_front(prefixCount)
2173 .take_front(astArgs.size());
2175 for (
auto [astArg, type] : llvm::zip(astArgs, valInputs)) {
2177 auto blockArg = block.addArgument(type, loc);
2179 if (isa<moore::RefType>(type)) {
2182 OpBuilder::InsertionGuard g(
builder);
2183 builder.setInsertionPointToEnd(&block);
2185 auto shadowArg = moore::VariableOp::create(
2186 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
2187 StringAttr{}, blockArg);
2189 argVariables.push_back(shadowArg);
2192 const auto &argCanon = astArg->getType().getCanonicalType();
2193 if (
const auto *vi = argCanon.as_if<slang::ast::VirtualInterfaceType>())
2199 OpBuilder::InsertionGuard g(
builder);
2200 builder.setInsertionPointToEnd(&block);
2203 if (subroutine.returnValVar) {
2204 auto type =
convertType(*subroutine.returnValVar->getDeclaredType());
2207 returnVar = moore::VariableOp::create(
2208 builder, lowering->op->getLoc(),
2209 moore::RefType::get(cast<moore::UnpackedType>(type)), StringAttr{},
2211 valueSymbols.insert(subroutine.returnValVar, returnVar);
2219 for (
auto *sym : lowering->capturedSymbols) {
2223 auto refType = moore::RefType::get(cast<moore::UnpackedType>(type));
2225 auto blockArg = block.addArgument(refType, loc);
2231 llvm::scope_exit restoreThis([&] {
currentThisRef = savedThis; });
2235 llvm::scope_exit restoreFunctionLowering(
2244 if (isa<moore::CoroutineOp>(lowering->op.getOperation())) {
2245 moore::ReturnOp::create(
builder, lowering->op->getLoc());
2246 }
else if (returnVar && !subroutine.getReturnType().isVoid()) {
2248 moore::ReadOp::create(
builder, returnVar.getLoc(), returnVar);
2249 mlir::func::ReturnOp::create(
builder, lowering->op->getLoc(), read);
2251 mlir::func::ReturnOp::create(
builder, lowering->op->getLoc(),
2255 if (returnVar && returnVar.use_empty())
2256 returnVar.getDefiningOp()->erase();
2258 for (
auto var : argVariables) {
2259 if (llvm::all_of(var->getUsers(),
2260 [](
auto *user) { return isa<moore::ReadOp>(user); })) {
2261 for (
auto *user : llvm::make_early_inc_range(var->getUsers())) {
2262 user->getResult(0).replaceAllUsesWith(var.getInitial());
2274 const slang::ast::TimingControl *delay,
2277 const slang::ast::Expression *delayExpr;
2278 if (
const auto *delay3 = delay->as_if<slang::ast::Delay3Control>()) {
2279 if (delay3->expr2 || delay3->expr3)
2280 return mlir::emitError(loc) <<
"only primitives that specify a "
2281 "single delay are currently supported.";
2282 delayExpr = &delay3->expr1;
2283 }
else if (
const auto *delayControl =
2284 delay->as_if<slang::ast::DelayControl>()) {
2285 delayExpr = &delayControl->expr;
2287 llvm_unreachable(
"unexpected delay control type in primitive instance");
2290 *delayExpr, moore::TimeType::get(
getContext()));
2293 moore::DelayedContinuousAssignOp::create(
builder, loc, outputVal,
2294 assignment, delayVal);
2296 moore::ContinuousAssignOp::create(
builder, loc, outputVal, assignment);
2304 const slang::ast::PrimitiveInstanceSymbol &prim) {
2305 if (prim.getDriveStrength().first.has_value() ||
2306 prim.getDriveStrength().second.has_value())
2308 <<
"primitive instances with explicit drive strengths are not "
2311 switch (prim.primitiveType.primitiveKind) {
2312 case slang::ast::PrimitiveSymbol::PrimitiveKind::NInput:
2315 case slang::ast::PrimitiveSymbol::PrimitiveKind::NOutput:
2318 case slang::ast::PrimitiveSymbol::PrimitiveKind::Fixed:
2321 case slang::ast::PrimitiveSymbol::PrimitiveKind::UserDefined:
2326 <<
"unsupported instance of primitive `" << prim.primitiveType.name
2332 const slang::ast::PrimitiveInstanceSymbol &prim) {
2334 auto primName = prim.primitiveType.name;
2336 auto portConns = prim.getPortConnections();
2337 assert(portConns.size() >= 2 &&
2338 "n-input primitives should have at least 2 ports");
2342 portConns[0]->as<slang::ast::AssignmentExpression>().left();
2348 SmallVector<Value> inputVals;
2349 inputVals.reserve(portConns.size() - 1);
2350 for (
const auto *inputConn : portConns.subspan(1, portConns.size() - 1)) {
2354 inputVals.push_back(inputVal);
2357 Value nextInput = inputVals.front();
2359 llvm::StringSwitch<std::function<Value()>>(prim.primitiveType.name)
2361 for (Value inputVal : llvm::drop_begin(inputVals))
2363 moore::AndOp::create(
builder, loc, nextInput, inputVal);
2367 for (Value inputVal : llvm::drop_begin(inputVals))
2369 moore::OrOp::create(
builder, loc, nextInput, inputVal);
2373 for (Value inputVal : llvm::drop_begin(inputVals))
2375 moore::XorOp::create(
builder, loc, nextInput, inputVal);
2378 .Case(
"nand", ([&] {
2379 for (Value inputVal : llvm::drop_begin(inputVals))
2381 moore::AndOp::create(
builder, loc, nextInput, inputVal);
2382 return moore::NotOp::create(
builder, loc, nextInput);
2385 for (Value inputVal : llvm::drop_begin(inputVals))
2387 moore::OrOp::create(
builder, loc, nextInput, inputVal);
2388 return moore::NotOp::create(
builder, loc, nextInput);
2390 .Case(
"xnor", ([&] {
2391 for (Value inputVal : llvm::drop_begin(inputVals))
2393 moore::XorOp::create(
builder, loc, nextInput, inputVal);
2394 return moore::NotOp::create(
builder, loc, nextInput);
2397 mlir::emitError(loc)
2398 <<
"unsupported primitive `" << primName <<
"`";
2405 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
2414 const slang::ast::PrimitiveInstanceSymbol &prim) {
2416 auto primName = prim.primitiveType.name;
2418 auto portConns = prim.getPortConnections();
2419 assert(portConns.size() >= 2 &&
2420 "n-output primitives should have at least 2 ports");
2423 SmallVector<Value> outputVals;
2424 outputVals.reserve(portConns.size() - 1);
2425 for (
const auto *outputConn : portConns.subspan(0, portConns.size() - 1)) {
2426 auto &output = outputConn->as<slang::ast::AssignmentExpression>().left();
2430 outputVals.push_back(outputVal);
2438 llvm::StringSwitch<std::function<Value()>>(prim.primitiveType.name)
2440 ([&] {
return moore::NotOp::create(
builder, loc, inputVal); }))
2442 return moore::BoolCastOp::create(
builder, loc, inputVal);
2445 mlir::emitError(loc)
2446 <<
"unsupported primitive `" << primName <<
"`";
2453 for (
auto outputVal : outputVals) {
2454 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
2467 const slang::ast::PrimitiveInstanceSymbol &prim) {
2468 auto primName = prim.primitiveType.name;
2473 if (primName ==
"pullup" || primName ==
"pulldown")
2476 if (primName ==
"bufif0" || primName ==
"bufif1" || primName ==
"notif0" ||
2477 primName ==
"notif1")
2480 if (primName ==
"nmos" || primName ==
"pmos" || primName ==
"rnmos" ||
2481 primName ==
"rpmos") {
2485 if (primName ==
"cmos" || primName ==
"rcmos")
2489 mlir::emitError(loc) <<
"unsupported primitive `" << primName <<
"`";
2494 const slang::ast::PrimitiveInstanceSymbol &prim) {
2495 assert((prim.primitiveType.name ==
"pullup" ||
2496 prim.primitiveType.name ==
"pulldown") &&
2497 "expected pullup or pulldown primitive");
2499 assert(!prim.getDelay() &&
2500 "SystemVerilog does not allow pull gate primitives with delays");
2502 auto primName = prim.primitiveType.name;
2504 auto portConns = prim.getPortConnections();
2506 assert(portConns.size() == 1 &&
2507 "pullup/pulldown primitives should have exactly one port");
2510 portConns.front()->as<slang::ast::AssignmentExpression>().left());
2512 auto dstType = cast<moore::RefType>(portVal.getType()).getNestedType();
2513 auto dstTypeWidth = dstType.getBitSize();
2516 "expected fixed-width type for pullup/pulldown primitive");
2517 auto constVal = primName ==
"pullup" ? -1 : 0;
2518 auto c = moore::ConstantOp::create(
2520 moore::IntType::getInt(this->
getContext(), dstTypeWidth.value()),
2526 moore::ContinuousAssignOp::create(
builder, loc, portVal, converted);
2537 auto dstIntType = cast<moore::IntType>(dstType);
2539 moore::ConstantOp::create(builder, loc, dstIntType,
FVInt::getAllX(1));
2541 moore::ConstantOp::create(builder, loc, dstIntType,
FVInt::getAllZ(1));
2543 auto isZ = moore::CaseEqOp::create(builder, loc, data, zVal);
2544 auto condOp = moore::ConditionalOp::create(builder, loc, dstType, isZ);
2545 auto &trueBlk = condOp.getTrueRegion().emplaceBlock();
2546 auto &falseBlk = condOp.getFalseRegion().emplaceBlock();
2548 OpBuilder::InsertionGuard g(builder);
2549 builder.setInsertionPointToStart(&trueBlk);
2550 moore::YieldOp::create(builder, loc, xVal);
2551 builder.setInsertionPointToStart(&falseBlk);
2552 moore::YieldOp::create(builder, loc, data);
2554 return condOp.getResult();
2558 const slang::ast::PrimitiveInstanceSymbol &prim) {
2560 auto primName = prim.primitiveType.name;
2562 auto portConns = prim.getPortConnections();
2563 assert(portConns.size() == 3 &&
2564 "Expected exactly 3 ports in three-state gate primitives");
2567 portConns[0]->as<slang::ast::AssignmentExpression>().left();
2574 if (!inVal || !enVal)
2577 auto enType = cast<moore::IntType>(enVal.getType());
2578 [[maybe_unused]]
auto enWidth = enType.getBitSize();
2580 assert(enWidth && *enWidth == 1 &&
2581 "enable signal of a three-state gate primitive must be 1 bit");
2583 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
2584 auto dstWidth = dstType.getBitSize();
2585 if (!dstWidth || *dstWidth != 1)
2586 return mlir::emitError(loc)
2587 <<
"output of a three-state gate primitive must be 1 bit";
2589 if (primName ==
"notif0" || primName ==
"notif1")
2590 inVal = moore::NotOp::create(
builder, loc, inVal);
2597 int inactiveLevel = (primName ==
"bufif1" || primName ==
"notif1") ? 0 : 1;
2598 Value inactiveConst =
2599 moore::ConstantOp::create(
builder, loc, enType, inactiveLevel,
false);
2601 auto dstIntType = cast<moore::IntType>(dstType);
2608 moore::CaseEqOp::create(
builder, loc, enVal, inactiveConst);
2610 moore::ConditionalOp::create(
builder, loc, dstType, condInactive);
2611 auto &inactiveTrue = inactiveOp.getTrueRegion().emplaceBlock();
2612 auto &inactiveFalse = inactiveOp.getFalseRegion().emplaceBlock();
2615 OpBuilder::InsertionGuard g(
builder);
2616 builder.setInsertionPointToStart(&inactiveTrue);
2617 moore::YieldOp::create(
builder, loc, zVal);
2618 builder.setInsertionPointToStart(&inactiveFalse);
2619 moore::YieldOp::create(
builder, loc,
2623 Value result = inactiveOp.getResult();
2629 const slang::ast::PrimitiveInstanceSymbol &prim) {
2631 prim.primitiveType.name ==
"nmos" || prim.primitiveType.name ==
"pmos" ||
2632 prim.primitiveType.name ==
"rnmos" || prim.primitiveType.name ==
"rpmos");
2635 auto primName = prim.primitiveType.name;
2637 auto portConns = prim.getPortConnections();
2639 assert(portConns.size() == 3 &&
"mos primitive should have exactly 3 ports");
2642 portConns[0]->as<slang::ast::AssignmentExpression>().left();
2653 assert(cast<moore::IntType>(inputVal.getType()).getBitSize() == 1 &&
2654 "MOS switch input must be 1 bit");
2662 auto controlType = cast<moore::IntType>(control.getType());
2663 assert(controlType.getBitSize() == 1 &&
"MOS switch control must be 1 bit");
2665 int offLevel = (primName ==
"nmos" || primName ==
"rnmos") ? 0 : 1;
2666 auto offValue = moore::ConstantOp::create(
2667 builder, loc, controlType,
FVInt(1,
static_cast<uint64_t
>(offLevel)));
2669 controlIsOff = moore::CaseEqOp::create(
builder, loc, control, offValue);
2670 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
2672 auto dstIntType = dyn_cast<moore::IntType>(dstType);
2673 if (!dstIntType || dstIntType.getBitSize() != 1)
2674 return mlir::emitError(loc) <<
"MOS switch output must be 1 bit";
2677 if (!convertedInput)
2684 moore::ConditionalOp::create(
builder, loc, dstType, controlIsOff);
2686 auto &trueBlock = condOp.getTrueRegion().emplaceBlock();
2687 auto &falseBlock = condOp.getFalseRegion().emplaceBlock();
2689 builder.setInsertionPointToStart(&trueBlock);
2690 moore::YieldOp::create(
builder, loc, zVal);
2692 builder.setInsertionPointToStart(&falseBlock);
2693 moore::YieldOp::create(
builder, loc, convertedInput);
2695 builder.setInsertionPointAfter(condOp);
2698 prim.getDelay(), loc);
2702 const slang::ast::PrimitiveInstanceSymbol &prim) {
2703 assert(prim.primitiveType.name ==
"cmos" ||
2704 prim.primitiveType.name ==
"rcmos");
2707 auto portConns = prim.getPortConnections();
2708 assert(portConns.size() == 4 &&
"cmos primitive should have exactly 4 ports");
2711 portConns[0]->as<slang::ast::AssignmentExpression>().left();
2720 assert(cast<moore::IntType>(dataVal.getType()).getBitSize() == 1 &&
2721 "CMOS switch input must be 1 bit");
2726 assert(cast<moore::IntType>(ncontrolVal.getType()).getBitSize() == 1 &&
2727 "CMOS switch ncontrol must be 1 bit");
2732 assert(cast<moore::IntType>(pcontrolVal.getType()).getBitSize() == 1 &&
2733 "CMOS switch pcontrol must be 1 bit");
2735 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
2736 auto dstIntType = dyn_cast<moore::IntType>(dstType);
2737 if (!dstIntType || dstIntType.getBitSize() != 1)
2738 return mlir::emitError(loc) <<
"CMOS switch output must be 1 bit";
2744 auto logicType = moore::IntType::getLogic(
getContext(), 1);
2746 auto makeConst = [&](
FVInt val) -> Value {
2747 Value c = moore::ConstantOp::create(
builder, loc, logicType, val);
2755 auto makeLevelConstant = [&](Value value,
int level) -> Value {
2756 auto type = cast<moore::IntType>(value.getType());
2757 return moore::ConstantOp::create(
builder, loc, type,
2758 FVInt(1,
static_cast<uint64_t
>(level)));
2761 auto muxZOrData = [&](Value cond) -> Value {
2762 auto condOp = moore::ConditionalOp::create(
builder, loc, dstType, cond);
2763 auto &trueBlk = condOp.getTrueRegion().emplaceBlock();
2764 auto &falseBlk = condOp.getFalseRegion().emplaceBlock();
2765 builder.setInsertionPointToStart(&trueBlk);
2766 moore::YieldOp::create(
builder, loc, zVal);
2767 builder.setInsertionPointToStart(&falseBlk);
2768 moore::YieldOp::create(
builder, loc, convertedData);
2769 builder.setInsertionPointAfter(condOp);
2770 return condOp.getResult();
2774 auto nOff = makeLevelConstant(ncontrolVal, 0);
2775 auto nIsOff = moore::CaseEqOp::create(
builder, loc, ncontrolVal, nOff);
2776 Value nResult = muxZOrData(nIsOff);
2779 auto pOff = makeLevelConstant(pcontrolVal, 1);
2780 auto pIsOff = moore::CaseEqOp::create(
builder, loc, pcontrolVal, pOff);
2781 Value pResult = muxZOrData(pIsOff);
2783 auto agree = moore::CaseEqOp::create(
builder, loc, nResult, pResult);
2784 auto nIsZ = moore::CaseEqOp::create(
builder, loc, nResult, zVal);
2785 auto pIsZ = moore::CaseEqOp::create(
builder, loc, pResult, zVal);
2787 auto outerCond = moore::ConditionalOp::create(
builder, loc, dstType, agree);
2788 auto &outerTrue = outerCond.getTrueRegion().emplaceBlock();
2789 auto &outerFalse = outerCond.getFalseRegion().emplaceBlock();
2792 OpBuilder::InsertionGuard guard(
builder);
2793 builder.setInsertionPointToStart(&outerTrue);
2794 moore::YieldOp::create(
builder, loc, nResult);
2796 builder.setInsertionPointToStart(&outerFalse);
2797 auto middleCond = moore::ConditionalOp::create(
builder, loc, dstType, nIsZ);
2798 auto &middleTrue = middleCond.getTrueRegion().emplaceBlock();
2799 auto &middleFalse = middleCond.getFalseRegion().emplaceBlock();
2800 moore::YieldOp::create(
builder, loc, middleCond.getResult());
2802 builder.setInsertionPointToStart(&middleTrue);
2803 moore::YieldOp::create(
builder, loc, pResult);
2805 builder.setInsertionPointToStart(&middleFalse);
2806 auto innerCond = moore::ConditionalOp::create(
builder, loc, dstType, pIsZ);
2807 auto &innerTrue = innerCond.getTrueRegion().emplaceBlock();
2808 auto &innerFalse = innerCond.getFalseRegion().emplaceBlock();
2810 builder.setInsertionPointToStart(&innerTrue);
2811 moore::YieldOp::create(
builder, loc, nResult);
2812 builder.setInsertionPointToStart(&innerFalse);
2813 moore::YieldOp::create(
builder, loc, xVal);
2815 builder.setInsertionPointAfter(innerCond);
2816 moore::YieldOp::create(
builder, loc, innerCond.getResult());
2820 prim.getDelay(), loc);
2829 levels.reserve(inputs.size());
2830 for (
char c : inputs)
2831 levels.push_back(llvm::toLower(c));
2838 ArrayRef<char> levels,
size_t numInputs) {
2843 if (entry.isEdgeSensitive || entry.output !=
'1' ||
2844 levels.size() != numInputs)
2846 return llvm::any_of(levels, [](
char c) {
2847 return c !=
'0' && c !=
'1' && c !=
'?' && c !=
'b';
2854 ArrayRef<char> levels, ValueRange inputVals,
2855 SmallVectorImpl<Value> ¬Vars) {
2857 for (
size_t i = 0, e = inputVals.size(); i < e; ++i) {
2862 }
else if (c ==
'0') {
2864 notVars[i] = moore::NotOp::create(builder, loc, inputVals[i]);
2870 term = term ? moore::AndOp::create(builder, loc, term,
lit) :
lit;
2876 const slang::ast::PrimitiveSymbol &primitive) {
2878 if (
auto moduleOp =
symbolTable.lookup<moore::SVModuleOp>(primitive.name))
2884 if (primitive.isSequential)
2885 return mlir::emitError(loc)
2886 <<
"sequential user-defined primitives are not supported yet";
2887 assert(primitive.ports.size() >= 2 &&
2888 "user-defined primitives must have at least 2 ports");
2892 OpBuilder::InsertionGuard guard(
builder);
2895 auto l1Ty = moore::IntType::getLogic(
getContext(), 1);
2896 SmallVector<hw::ModulePort> modulePorts;
2897 modulePorts.reserve(primitive.ports.size());
2898 modulePorts.push_back({
builder.getStringAttr(primitive.ports[0]->name), l1Ty,
2900 for (
const auto *port : primitive.ports.subspan(1))
2901 modulePorts.push_back(
2910 builder.setInsertionPoint(upperIt->second);
2912 moore::SVModuleOp::create(
builder, loc, primitive.name,
2913 hw::ModuleType::get(
getContext(), modulePorts));
2914 auto *block =
builder.createBlock(&moduleOp.getBodyRegion());
2915 for (
const auto *port : primitive.ports.subspan(1))
2920 auto inputVals = block->getArguments();
2921 size_t numInputs = inputVals.size();
2924 SmallVector<Value> notVars(numInputs, Value{});
2929 SmallVector<char> levels;
2930 for (
const auto &entry : primitive.table) {
2937 sum = moore::ConstantOp::create(
builder, loc, l1Ty, 1);
2940 sum = sum ? moore::OrOp::create(
builder, loc, sum, term) : term;
2943 sum = moore::ConstantOp::create(
builder, loc, l1Ty, 0);
2945 moore::OutputOp::create(
builder, loc, sum);
2950 const slang::ast::PrimitiveInstanceSymbol &prim) {
2952 auto portConns = prim.getPortConnections();
2953 assert(portConns.size() >= 2 &&
2954 "user-defined primitives should have at least 2 ports");
2956 size_t numInputs = portConns.size() - 1;
2960 portConns[0]->as<slang::ast::AssignmentExpression>().left();
2966 auto l1Ty = moore::IntType::getLogic(
getContext(), 1);
2967 SmallVector<Value> inputVals;
2968 inputVals.reserve(numInputs);
2969 for (
const auto *inputConn : portConns.subspan(1, numInputs)) {
2976 inputVals.push_back(converted);
2981 FailureOr<moore::SVModuleOp> moduleOpOrFailure =
2983 if (failed(moduleOpOrFailure))
2985 moore::SVModuleOp moduleOp = *moduleOpOrFailure;
2986 hw::ModuleType moduleType = moduleOp.getModuleType();
2987 auto inputNames =
builder.getArrayAttr(moduleType.getInputNames());
2988 auto outputNames =
builder.getArrayAttr(moduleType.getOutputNames());
2989 auto inst = moore::InstanceOp::create(
2990 builder, loc, moduleType.getOutputTypes(),
2991 builder.getStringAttr(prim.name),
2992 FlatSymbolRefAttr::get(moduleOp.getSymNameAttr()), inputVals, inputNames,
2996 auto dstType = cast<moore::RefType>(outputVal.getType()).getNestedType();
3009mlir::StringAttr fullyQualifiedClassName(
Context &ctx,
3010 const slang::ast::Type &ty) {
3011 SmallString<64> name;
3012 SmallVector<llvm::StringRef, 8> parts;
3014 const slang::ast::Scope *scope = ty.getParentScope();
3016 const auto &sym = scope->asSymbol();
3018 case slang::ast::SymbolKind::Root:
3021 case slang::ast::SymbolKind::InstanceBody:
3022 case slang::ast::SymbolKind::Instance:
3023 case slang::ast::SymbolKind::Package:
3024 case slang::ast::SymbolKind::ClassType:
3025 if (!sym.name.empty())
3026 parts.push_back(sym.name);
3031 scope = sym.getParentScope();
3034 for (
auto p : llvm::reverse(parts)) {
3039 return mlir::StringAttr::get(ctx.
getContext(), name);
3044std::pair<mlir::SymbolRefAttr, mlir::ArrayAttr>
3046 const slang::ast::ClassType &cls) {
3050 mlir::SymbolRefAttr base;
3051 if (
const auto *b = cls.getBaseClass())
3052 base = mlir::SymbolRefAttr::get(fullyQualifiedClassName(
context, *b));
3055 SmallVector<mlir::Attribute> impls;
3056 if (
auto ifaces = cls.getDeclaredInterfaces(); !ifaces.empty()) {
3057 impls.reserve(ifaces.size());
3058 for (
const auto *iface : ifaces)
3059 impls.push_back(mlir::FlatSymbolRefAttr::get(
3060 fullyQualifiedClassName(
context, *iface)));
3063 mlir::ArrayAttr implArr =
3064 impls.empty() ? mlir::ArrayAttr() : mlir::ArrayAttr::
get(ctx, impls);
3066 return {base, implArr};
3071struct ClassDeclVisitorBase {
3074 ClassLowering &classLowering;
3076 ClassDeclVisitorBase(Context &ctx, ClassLowering &lowering)
3077 :
context(ctx), builder(ctx.builder), classLowering(lowering) {}
3081 return context.convertLocation(sloc);
3087struct ClassPropertyVisitor : ClassDeclVisitorBase {
3088 using ClassDeclVisitorBase::ClassDeclVisitorBase;
3091 LogicalResult
run(
const slang::ast::ClassType &classAST) {
3092 if (!classLowering.
op.getBody().empty())
3095 OpBuilder::InsertionGuard ig(builder);
3097 Block *body = &classLowering.
op.getBody().emplaceBlock();
3098 builder.setInsertionPointToEnd(body);
3101 for (
const auto &mem : classAST.members()) {
3102 if (
const auto *prop = mem.as_if<slang::ast::ClassPropertySymbol>()) {
3103 if (failed(prop->visit(*
this)))
3112 LogicalResult visit(
const slang::ast::ClassPropertySymbol &prop) {
3114 auto ty =
context.convertType(prop.getType());
3118 if (prop.lifetime == slang::ast::VariableLifetime::Automatic) {
3119 moore::ClassPropertyDeclOp::create(builder, loc, prop.name,
3128 if (!
context.globalVariables.lookup(&prop))
3129 return context.convertGlobalVariable(prop);
3134 LogicalResult visit(
const slang::ast::ClassType &cls) {
3135 return context.buildClassProperties(cls);
3139 template <
typename T>
3140 LogicalResult visit(T &&) {
3147struct ClassMethodVisitor : ClassDeclVisitorBase {
3148 using ClassDeclVisitorBase::ClassDeclVisitorBase;
3151 LogicalResult
run(
const slang::ast::ClassType &classAST) {
3155 if (classLowering.
op.getBody().empty())
3158 OpBuilder::InsertionGuard ig(builder);
3159 builder.setInsertionPointToEnd(&classLowering.
op.getBody().front());
3162 for (
const auto &mem : classAST.members()) {
3163 if (failed(mem.visit(*
this)))
3172 LogicalResult visit(
const slang::ast::ClassPropertySymbol &) {
3178 LogicalResult visit(
const slang::ast::ParameterSymbol &) {
return success(); }
3182 LogicalResult visit(
const slang::ast::TypeParameterSymbol &) {
3188 LogicalResult visit(
const slang::ast::TypeAliasType &) {
return success(); }
3191 LogicalResult visit(
const slang::ast::GenericClassDefSymbol &) {
3196 LogicalResult visit(
const slang::ast::TransparentMemberSymbol &) {
3201 LogicalResult visit(
const slang::ast::EmptyMemberSymbol &) {
3206 LogicalResult visit(
const slang::ast::SubroutineSymbol &fn) {
3207 if (fn.flags & slang::ast::MethodFlags::BuiltIn) {
3208 static bool remarkEmitted =
false;
3212 mlir::emitRemark(classLowering.
op.getLoc())
3213 <<
"Class builtin functions (needed for randomization, constraints, "
3214 "and covergroups) are not yet supported and will be dropped "
3216 remarkEmitted =
true;
3220 const mlir::UnitAttr isVirtual =
3221 (fn.flags & slang::ast::MethodFlags::Virtual)
3222 ? UnitAttr::get(
context.getContext())
3229 if (fn.flags & slang::ast::MethodFlags::Pure) {
3231 SmallVector<Type, 1> extraParams;
3233 mlir::FlatSymbolRefAttr::get(classLowering.
op.getSymNameAttr());
3235 moore::ClassHandleType::get(
context.getContext(), classSym);
3236 extraParams.push_back(handleTy);
3240 mlir::emitError(loc) <<
"Invalid function signature for " << fn.name;
3244 moore::ClassMethodDeclOp::create(builder, loc, fn.name,
3245 {}, funcTy,
nullptr);
3249 auto *lowering =
context.declareFunction(fn);
3258 FunctionType fnTy = cast<FunctionType>(lowering->op.getFunctionType());
3260 moore::ClassMethodDeclOp::create(
3261 builder, loc, fn.name, {}, fnTy,
3262 SymbolRefAttr::get(lowering->op.getNameAttr()));
3279 LogicalResult visit(
const slang::ast::MethodPrototypeSymbol &fn) {
3280 const auto *externImpl = fn.getSubroutine();
3284 <<
"Didn't find an implementation matching the forward declaration "
3289 return visit(*externImpl);
3293 LogicalResult visit(
const slang::ast::ClassType &cls) {
3294 if (failed(
context.buildClassProperties(cls)))
3296 return context.materializeClassMethods(cls);
3300 template <
typename T>
3301 LogicalResult visit(T &&node) {
3302 Location loc = UnknownLoc::get(
context.getContext());
3303 if constexpr (
requires { node.location; })
3305 mlir::emitError(loc) <<
"unsupported construct in ClassType members: "
3306 << slang::ast::toString(node.kind);
3314 auto &lowering =
classes[&cls];
3316 return lowering.get();
3317 lowering = std::make_unique<ClassLowering>();
3322 OpBuilder::InsertionGuard g(
builder);
3328 builder.setInsertionPoint(it->second);
3330 auto symName = fullyQualifiedClassName(*
this, cls);
3332 auto [base, impls] = buildBaseAndImplementsAttrs(*
this, cls);
3333 auto classDeclOp = moore::ClassDeclOp::create(
3334 builder, loc, symName, {}, base, impls);
3336 SymbolTable::setSymbolVisibility(classDeclOp,
3337 SymbolTable::Visibility::Public);
3339 lowering->op = classDeclOp;
3342 return lowering.get();
3349 timeScale = classdecl.getTimeScale().value_or(slang::TimeScale());
3350 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
3357 if (classdecl.getBaseClass()) {
3358 if (
const auto *baseClassDecl =
3359 classdecl.getBaseClass()->as_if<slang::ast::ClassType>()) {
3370 return ClassPropertyVisitor(*
this, *lowering).run(classdecl);
3377 timeScale = classdecl.getTimeScale().value_or(slang::TimeScale());
3378 llvm::scope_exit timeScaleGuard([&] {
timeScale = prevTimeScale; });
3381 auto *lowering =
classes[&classdecl].get();
3388 if (classdecl.getBaseClass()) {
3389 if (
const auto *baseClassDecl =
3390 classdecl.getBaseClass()->as_if<slang::ast::ClassType>()) {
3396 return ClassMethodVisitor(*
this, *lowering).run(classdecl);
3406 OpBuilder::InsertionGuard g(
builder);
3412 builder.setInsertionPoint(it->second);
3416 SmallString<64> symName;
3420 if (
const auto *classVar = var.as_if<slang::ast::ClassPropertySymbol>()) {
3421 if (
const auto *parentScope = classVar->getParentScope()) {
3422 if (
const auto *parentClass =
3423 parentScope->asSymbol().as_if<slang::ast::ClassType>())
3424 symName = fullyQualifiedClassName(*
this, *parentClass);
3426 mlir::emitError(loc)
3427 <<
"Could not access parent class of class property "
3432 mlir::emitError(loc) <<
"Could not get parent scope of class property "
3437 symName += var.name;
3440 symName += var.name;
3449 auto varOp = moore::GlobalVariableOp::create(
builder, loc, symName,
3451 cast<moore::UnpackedType>(type));
3462 if (var.getInitializer())
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static Location convertLocation(MLIRContext *context, const slang::SourceManager &sourceManager, slang::SourceLocation loc)
Convert a slang SourceLocation to an MLIR Location.
false return failure()
Parse an optional keyword or string and set instance into 'result'.
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 Value buildProductiveTerm(OpBuilder &builder, Location loc, ArrayRef< char > levels, ValueRange inputVals, SmallVectorImpl< Value > ¬Vars)
Build the productive AND-term for this row.
static moore::ProcedureKind convertProcedureKind(slang::ast::ProceduralBlockKind kind)
static bool skipUnsupportedRow(const slang::ast::PrimitiveSymbol::TableEntry &entry, ArrayRef< char > levels, size_t numInputs)
static void parseInputRow(StringRef inputs, SmallVectorImpl< char > &levels)
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.
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, List[str] cmdline_args=sys.argv)
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...
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.
LogicalResult convertUserDefinedPrimitiveInstance(const slang::ast::PrimitiveInstanceSymbol &prim)
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.
DenseMap< std::pair< const slang::ast::InstanceSymbol *, mlir::StringAttr >, Value > hierValueSymbols
Persistent map for hierarchical value lookups.
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)
FailureOr< moore::SVModuleOp > convertUserDefinedPrimitiveDef(const slang::ast::PrimitiveSymbol &primitive)
ValueSymbols::ScopeTy ValueSymbolScope
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.
LogicalResult convertCompilation()
Convert hierarchy and structure AST nodes to MLIR ops.
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.