10#include "slang/ast/Compilation.h"
11#include "slang/ast/symbols/ClassSymbols.h"
12#include "llvm/ADT/ScopeExit.h"
15using namespace ImportVerilog;
22 SmallString<64> &prefix) {
23 if (symbol.kind != slang::ast::SymbolKind::Package)
26 if (!symbol.name.empty()) {
27 prefix += symbol.name;
44 BaseVisitor(
Context &context, Location loc)
45 : context(context), loc(loc), builder(context.builder) {}
48 LogicalResult visit(
const slang::ast::EmptyMemberSymbol &) {
54 LogicalResult visit(
const slang::ast::TransparentMemberSymbol &) {
59 LogicalResult visit(
const slang::ast::ClassType &classdecl) {
67 LogicalResult visit(
const slang::ast::GenericClassDefSymbol &) {
72 LogicalResult visit(
const slang::ast::TypeAliasType &) {
return success(); }
73 LogicalResult visit(
const slang::ast::ForwardingTypedefSymbol &) {
78 LogicalResult visit(
const slang::ast::ExplicitImportSymbol &) {
81 LogicalResult visit(
const slang::ast::WildcardImportSymbol &) {
86 LogicalResult visit(
const slang::ast::TypeParameterSymbol &) {
91 LogicalResult visit(
const slang::ast::ElabSystemTaskSymbol &) {
96 LogicalResult visit(
const slang::ast::ParameterSymbol ¶m) {
97 visitParameter(param);
101 LogicalResult visit(
const slang::ast::SpecparamSymbol ¶m) {
102 visitParameter(param);
106 template <
class Node>
107 void visitParameter(
const Node ¶m) {
117 if (builder.getInsertionBlock()->getParentOp() == context.
intoModuleOp)
118 context.
orderedRootOps.insert({param.location, value.getDefiningOp()});
122 SmallString<64> paramName;
124 paramName += param.name;
126 debug::VariableOp::create(builder, loc, builder.getStringAttr(paramName),
137struct RootVisitor :
public BaseVisitor {
138 using BaseVisitor::BaseVisitor;
139 using BaseVisitor::visit;
142 LogicalResult visit(
const slang::ast::PackageSymbol &package) {
147 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
152 template <
typename T>
153 LogicalResult visit(T &&node) {
154 mlir::emitError(loc,
"unsupported construct: ")
155 << slang::ast::toString(node.kind);
166struct PackageVisitor :
public BaseVisitor {
167 using BaseVisitor::BaseVisitor;
168 using BaseVisitor::visit;
171 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
176 template <
typename T>
177 LogicalResult visit(T &&node) {
178 mlir::emitError(loc,
"unsupported package member: ")
179 << slang::ast::toString(node.kind);
189static moore::ProcedureKind
192 case slang::ast::ProceduralBlockKind::Always:
193 return moore::ProcedureKind::Always;
194 case slang::ast::ProceduralBlockKind::AlwaysComb:
195 return moore::ProcedureKind::AlwaysComb;
196 case slang::ast::ProceduralBlockKind::AlwaysLatch:
197 return moore::ProcedureKind::AlwaysLatch;
198 case slang::ast::ProceduralBlockKind::AlwaysFF:
199 return moore::ProcedureKind::AlwaysFF;
200 case slang::ast::ProceduralBlockKind::Initial:
201 return moore::ProcedureKind::Initial;
202 case slang::ast::ProceduralBlockKind::Final:
203 return moore::ProcedureKind::Final;
205 llvm_unreachable(
"all procedure kinds handled");
210 case slang::ast::NetType::Supply0:
211 return moore::NetKind::Supply0;
212 case slang::ast::NetType::Supply1:
213 return moore::NetKind::Supply1;
214 case slang::ast::NetType::Tri:
215 return moore::NetKind::Tri;
216 case slang::ast::NetType::TriAnd:
217 return moore::NetKind::TriAnd;
218 case slang::ast::NetType::TriOr:
219 return moore::NetKind::TriOr;
220 case slang::ast::NetType::TriReg:
221 return moore::NetKind::TriReg;
222 case slang::ast::NetType::Tri0:
223 return moore::NetKind::Tri0;
224 case slang::ast::NetType::Tri1:
225 return moore::NetKind::Tri1;
226 case slang::ast::NetType::UWire:
227 return moore::NetKind::UWire;
228 case slang::ast::NetType::Wire:
229 return moore::NetKind::Wire;
230 case slang::ast::NetType::WAnd:
231 return moore::NetKind::WAnd;
232 case slang::ast::NetType::WOr:
233 return moore::NetKind::WOr;
234 case slang::ast::NetType::Interconnect:
235 return moore::NetKind::Interconnect;
236 case slang::ast::NetType::UserDefined:
237 return moore::NetKind::UserDefined;
238 case slang::ast::NetType::Unknown:
239 return moore::NetKind::Unknown;
241 llvm_unreachable(
"all net kinds handled");
245struct ModuleVisitor :
public BaseVisitor {
246 using BaseVisitor::visit;
250 StringRef blockNamePrefix;
252 ModuleVisitor(
Context &context, Location loc, StringRef blockNamePrefix =
"")
253 : BaseVisitor(context, loc), blockNamePrefix(blockNamePrefix) {}
256 LogicalResult visit(
const slang::ast::PortSymbol &) {
return success(); }
257 LogicalResult visit(
const slang::ast::MultiPortSymbol &) {
return success(); }
260 LogicalResult visit(
const slang::ast::GenvarSymbol &genvarNode) {
265 LogicalResult visit(
const slang::ast::DefParamSymbol &) {
return success(); }
269 LogicalResult visit(
const slang::ast::TypeParameterSymbol &) {
274 LogicalResult visit(
const slang::ast::InstanceSymbol &instNode) {
275 using slang::ast::ArgumentDirection;
276 using slang::ast::AssignmentExpression;
277 using slang::ast::MultiPortSymbol;
278 using slang::ast::PortSymbol;
283 auto module = moduleLowering->op;
284 auto moduleType =
module.getModuleType();
287 SymbolTable::setSymbolVisibility(module, SymbolTable::Visibility::Private);
294 portValues.reserve(moduleType.getNumPorts());
296 for (
const auto *con : instNode.getPortConnections()) {
297 const auto *expr = con->getExpression();
302 auto *port = con->port.as_if<PortSymbol>();
303 if (
auto *existingPort =
304 moduleLowering->portsBySyntaxNode.lookup(port->getSyntax()))
307 switch (port->direction) {
308 case ArgumentDirection::In: {
309 auto refType = moore::RefType::get(
310 cast<moore::UnpackedType>(context.
convertType(port->getType())));
312 if (
const auto *net =
313 port->internalSymbol->as_if<slang::ast::NetSymbol>()) {
314 auto netOp = moore::NetOp::create(
315 builder, loc, refType,
316 StringAttr::get(builder.getContext(), net->name),
318 auto readOp = moore::ReadOp::create(builder, loc, netOp);
319 portValues.insert({port, readOp});
320 }
else if (
const auto *var =
322 ->as_if<slang::ast::VariableSymbol>()) {
323 auto varOp = moore::VariableOp::create(
324 builder, loc, refType,
325 StringAttr::get(builder.getContext(), var->name),
nullptr);
326 auto readOp = moore::ReadOp::create(builder, loc, varOp);
327 portValues.insert({port, readOp});
329 return mlir::emitError(loc)
330 <<
"unsupported internal symbol for unconnected port `"
331 << port->name <<
"`";
338 case ArgumentDirection::Out:
343 return mlir::emitError(loc)
344 <<
"unsupported port `" << port->name <<
"` ("
345 << slang::ast::toString(port->kind) <<
")";
351 if (
const auto *assign = expr->as_if<AssignmentExpression>())
352 expr = &assign->left();
357 if (
auto *port = con->port.as_if<PortSymbol>()) {
359 auto value = (port->direction == ArgumentDirection::In)
364 if (
auto *existingPort =
365 moduleLowering->portsBySyntaxNode.lookup(con->port.getSyntax()))
367 portValues.insert({port, value});
374 if (
const auto *multiPort = con->port.as_if<MultiPortSymbol>()) {
380 for (
const auto *port :
llvm::reverse(multiPort->ports)) {
381 if (
auto *existingPort = moduleLowering->portsBySyntaxNode.lookup(
382 con->port.getSyntax()))
384 unsigned width = port->getType().getBitWidth();
385 auto sliceType = context.
convertType(port->getType());
388 Value slice = moore::ExtractRefOp::create(
390 moore::RefType::get(cast<moore::UnpackedType>(sliceType)), value,
393 if (port->direction == ArgumentDirection::In)
394 slice = moore::ReadOp::create(builder, loc, slice);
395 portValues.insert({port, slice});
401 mlir::emitError(loc) <<
"unsupported instance port `" << con->port.name
402 <<
"` (" << slang::ast::toString(con->port.kind)
408 SmallVector<Value> inputValues;
409 SmallVector<Value> outputValues;
410 inputValues.reserve(moduleType.getNumInputs());
411 outputValues.reserve(moduleType.getNumOutputs());
413 for (
auto &port : moduleLowering->ports) {
414 auto value = portValues.lookup(&port.ast);
415 if (port.ast.direction == ArgumentDirection::Out)
416 outputValues.push_back(value);
418 inputValues.push_back(value);
422 for (
auto [value, type] :
423 llvm::zip(inputValues, moduleType.getInputTypes()))
425 value = context.materializeConversion(type, value, false, value.
getLoc());
429 for (
const auto &hierPath : context.hierPaths[&instNode.body])
430 if (auto hierValue = context.valueSymbols.lookup(hierPath.valueSym);
431 hierPath.hierName && hierPath.direction == ArgumentDirection::In)
432 inputValues.push_back(hierValue);
435 auto inputNames = builder.getArrayAttr(moduleType.getInputNames());
436 auto outputNames = builder.getArrayAttr(moduleType.getOutputNames());
437 auto inst = moore::InstanceOp::create(
438 builder, loc, moduleType.getOutputTypes(),
439 builder.getStringAttr(Twine(blockNamePrefix) + instNode.name),
440 FlatSymbolRefAttr::get(module.getSymNameAttr()), inputValues,
441 inputNames, outputNames);
444 for (
const auto &hierPath : context.hierPaths[&instNode.body])
445 if (hierPath.idx && hierPath.direction == ArgumentDirection::
Out)
446 context.valueSymbols.insert(hierPath.valueSym,
447 inst->getResult(*hierPath.idx));
450 for (
auto [lvalue, output] :
llvm::zip(outputValues, inst.getOutputs())) {
453 Value rvalue = output;
454 auto dstType = cast<moore::RefType>(lvalue.getType()).getNestedType();
457 moore::ContinuousAssignOp::create(builder, loc, lvalue, rvalue);
464 LogicalResult visit(
const slang::ast::VariableSymbol &varNode) {
465 auto loweredType = context.
convertType(*varNode.getDeclaredType());
470 if (
const auto *init = varNode.getInitializer()) {
476 auto varOp = moore::VariableOp::create(
478 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
479 builder.getStringAttr(Twine(blockNamePrefix) + varNode.name), initial);
485 LogicalResult visit(
const slang::ast::NetSymbol &netNode) {
486 auto loweredType = context.
convertType(*netNode.getDeclaredType());
491 if (
const auto *init = netNode.getInitializer()) {
498 if (netkind == moore::NetKind::Interconnect ||
499 netkind == moore::NetKind::UserDefined ||
500 netkind == moore::NetKind::Unknown)
501 return mlir::emitError(loc,
"unsupported net kind `")
502 << netNode.netType.name <<
"`";
504 auto netOp = moore::NetOp::create(
506 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
507 builder.getStringAttr(Twine(blockNamePrefix) + netNode.name), netkind,
514 LogicalResult visit(
const slang::ast::ContinuousAssignSymbol &assignNode) {
516 assignNode.getAssignment().as<slang::ast::AssignmentExpression>();
522 expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
527 if (
auto *timingCtrl = assignNode.getDelay()) {
528 auto *ctrl = timingCtrl->as_if<slang::ast::DelayControl>();
529 assert(ctrl &&
"slang guarantees this to be a simple delay");
531 ctrl->expr, moore::TimeType::get(builder.getContext()));
534 moore::DelayedContinuousAssignOp::create(builder, loc, lhs, rhs, delay);
539 moore::ContinuousAssignOp::create(builder, loc, lhs, rhs);
544 LogicalResult convertProcedure(moore::ProcedureKind kind,
545 const slang::ast::Statement &body) {
546 auto procOp = moore::ProcedureOp::create(builder, loc, kind);
547 OpBuilder::InsertionGuard guard(builder);
548 builder.setInsertionPointToEnd(&procOp.getBody().emplaceBlock());
552 if (builder.getBlock())
553 moore::ReturnOp::create(builder, loc);
557 LogicalResult visit(
const slang::ast::ProceduralBlockSymbol &procNode) {
561 auto *stmt = procNode.getBody().as_if<slang::ast::TimedStatement>();
562 if (procNode.procedureKind == slang::ast::ProceduralBlockKind::Always &&
564 stmt->timing.kind == slang::ast::TimingControlKind::ImplicitEvent)
565 return convertProcedure(moore::ProcedureKind::AlwaysComb, stmt->stmt);
573 LogicalResult visit(
const slang::ast::GenerateBlockSymbol &genNode) {
575 if (genNode.isUninstantiated)
579 SmallString<64> prefix = blockNamePrefix;
580 if (!genNode.name.empty() ||
581 genNode.getParentScope()->asSymbol().kind !=
582 slang::ast::SymbolKind::GenerateBlockArray) {
583 prefix += genNode.getExternalName();
588 for (
auto &member : genNode.members())
589 if (failed(member.visit(ModuleVisitor(context, loc, prefix))))
595 LogicalResult visit(
const slang::ast::GenerateBlockArraySymbol &genArrNode) {
598 SmallString<64> prefix = blockNamePrefix;
599 prefix += genArrNode.getExternalName();
601 auto prefixBaseLen = prefix.size();
604 for (
const auto *entry : genArrNode.entries) {
606 prefix.resize(prefixBaseLen);
607 if (entry->arrayIndex)
608 prefix += entry->arrayIndex->toString();
610 Twine(entry->constructIndex).toVector(prefix);
614 if (failed(entry->asSymbol().visit(ModuleVisitor(context, loc, prefix))))
626 LogicalResult visit(
const slang::ast::StatementBlockSymbol &) {
632 LogicalResult visit(
const slang::ast::SequenceSymbol &seqNode) {
638 LogicalResult visit(
const slang::ast::PropertySymbol &propNode) {
643 LogicalResult visit(
const slang::ast::SubroutineSymbol &subroutine) {
648 template <
typename T>
649 LogicalResult visit(T &&node) {
650 mlir::emitError(loc,
"unsupported module member: ")
651 << slang::ast::toString(node.kind);
663LogicalResult Context::convertCompilation() {
669 timeScale = root.getTimeScale().value_or(slang::TimeScale());
670 auto timeScaleGuard =
671 llvm::make_scope_exit([&] {
timeScale = prevTimeScale; });
675 for (
auto *inst : root.topInstances)
682 for (
auto *unit : root.compilationUnits) {
683 for (
const auto &member : unit->members()) {
685 if (failed(member.visit(RootVisitor(*
this, loc))))
691 SmallVector<const slang::ast::InstanceSymbol *> topInstances;
692 for (
auto *inst : root.topInstances)
698 auto *
module = moduleWorklist.front();
713 using slang::ast::ArgumentDirection;
714 using slang::ast::MultiPortSymbol;
715 using slang::ast::ParameterSymbol;
716 using slang::ast::PortSymbol;
717 using slang::ast::TypeParameterSymbol;
722 timeScale =
module->getTimeScale().value_or(slang::TimeScale());
723 auto timeScaleGuard =
724 llvm::make_scope_exit([&] {
timeScale = prevTimeScale; });
726 auto parameters =
module->getParameters();
727 bool hasModuleSame =
false;
731 for (
auto const &existingModule :
modules) {
732 if (module->getDeclaringDefinition() ==
733 existingModule.getFirst()->getDeclaringDefinition()) {
734 auto moduleParameters = existingModule.getFirst()->getParameters();
735 hasModuleSame =
true;
736 for (
auto it1 = parameters.begin(), it2 = moduleParameters.begin();
737 it1 != parameters.end() && it2 != moduleParameters.end();
740 if (it1 == parameters.end() || it2 == moduleParameters.end()) {
741 hasModuleSame =
false;
744 const auto *para1 = (*it1)->symbol.as_if<ParameterSymbol>();
745 const auto *para2 = (*it2)->symbol.as_if<ParameterSymbol>();
747 if ((para1 ==
nullptr) ^ (para2 ==
nullptr)) {
748 hasModuleSame =
false;
752 if (para1 !=
nullptr) {
753 hasModuleSame = para1->getValue() == para2->getValue();
756 if (para1 ==
nullptr) {
758 (*it1)->symbol.as<TypeParameterSymbol>().getTypeAlias());
760 (*it2)->symbol.as<TypeParameterSymbol>().getTypeAlias());
761 hasModuleSame = para1Type == para2Type;
767 module = existingModule.first;
776 slot = std::make_unique<ModuleLowering>();
777 auto &lowering = *slot;
780 OpBuilder::InsertionGuard g(
builder);
785 if (module->getDefinition().definitionKind !=
786 slang::ast::DefinitionKind::Module) {
787 mlir::emitError(loc) <<
"unsupported definition: "
788 <<
module->getDefinition().getKindString();
793 auto block = std::make_unique<Block>();
794 SmallVector<hw::ModulePort> modulePorts;
797 unsigned int outputIdx = 0, inputIdx = 0;
798 for (
auto *symbol :
module->getPortList()) {
799 auto handlePort = [&](const PortSymbol &port) {
800 auto portLoc = convertLocation(port.location);
804 auto portName =
builder.getStringAttr(port.name);
806 if (port.direction == ArgumentDirection::Out) {
812 if (port.direction != ArgumentDirection::In)
813 type = moore::RefType::get(cast<moore::UnpackedType>(type));
815 arg = block->addArgument(type, portLoc);
818 lowering.ports.push_back({port, portLoc, arg});
822 if (
const auto *port = symbol->as_if<PortSymbol>()) {
823 if (failed(handlePort(*port)))
825 }
else if (
const auto *multiPort = symbol->as_if<MultiPortSymbol>()) {
826 for (
auto *port : multiPort->ports)
827 if (failed(handlePort(*port)))
831 <<
"unsupported module port `" << symbol->name <<
"` ("
832 << slang::ast::toString(symbol->kind) <<
")";
838 for (
auto &hierPath : hierPaths[module]) {
839 auto hierType =
convertType(hierPath.valueSym->getType());
843 if (
auto hierName = hierPath.hierName) {
845 hierType = moore::RefType::get(cast<moore::UnpackedType>(hierType));
846 if (hierPath.direction == ArgumentDirection::Out) {
847 hierPath.idx = outputIdx++;
850 hierPath.idx = inputIdx++;
853 block->addArgument(hierType, hierLoc);
857 auto moduleType = hw::ModuleType::get(getContext(), modulePorts);
861 auto it = orderedRootOps.upper_bound(module->location);
862 if (it == orderedRootOps.end())
863 builder.setInsertionPointToEnd(intoModuleOp.getBody());
865 builder.setInsertionPoint(it->second);
869 moore::SVModuleOp::create(builder, loc, module->name, moduleType);
870 orderedRootOps.insert(it, {
module->location, moduleOp});
871 moduleOp.getBodyRegion().push_back(block.release());
872 lowering.op = moduleOp;
876 symbolTable.insert(moduleOp);
879 moduleWorklist.push(module);
882 for (
const auto &port : lowering.ports)
883 lowering.portsBySyntaxNode.insert({port.ast.getSyntax(), &port.ast});
892 auto &lowering = *
modules[module];
893 OpBuilder::InsertionGuard g(
builder);
894 builder.setInsertionPointToEnd(lowering.op.getBody());
901 timeScale =
module->getTimeScale().value_or(slang::TimeScale());
902 auto timeScaleGuard =
903 llvm::make_scope_exit([&] {
timeScale = prevTimeScale; });
909 if (hierPath.direction == slang::ast::ArgumentDirection::In && hierPath.idx)
911 lowering.op.getBody()->getArgument(*hierPath.idx));
914 for (
auto &member :
module->members()) {
915 auto loc = convertLocation(member.location);
916 if (failed(member.visit(ModuleVisitor(*
this, loc))))
923 SmallVector<Value> outputs;
924 for (
auto &port : lowering.ports) {
926 if (
auto *expr = port.ast.getInternalExpr()) {
927 value = convertLvalueExpression(*expr);
928 }
else if (port.ast.internalSymbol) {
929 if (
const auto *sym =
930 port.ast.internalSymbol->as_if<slang::ast::ValueSymbol>())
931 value = valueSymbols.lookup(sym);
934 return mlir::emitError(port.loc,
"unsupported port: `")
936 <<
"` does not map to an internal symbol or expression";
939 if (port.ast.direction == slang::ast::ArgumentDirection::Out) {
940 if (isa<moore::RefType>(value.getType()))
941 value = moore::ReadOp::create(builder, value.getLoc(), value);
942 outputs.push_back(value);
948 Value portArg = port.arg;
949 if (port.ast.direction != slang::ast::ArgumentDirection::In)
950 portArg = moore::ReadOp::create(builder, port.loc, port.arg);
951 moore::ContinuousAssignOp::create(builder, port.loc, value, portArg);
956 for (
auto &hierPath : hierPaths[module])
957 if (auto hierValue = valueSymbols.lookup(hierPath.valueSym))
958 if (hierPath.direction == slang::ast::ArgumentDirection::
Out)
959 outputs.push_back(hierValue);
961 moore::OutputOp::create(builder, lowering.op.getLoc(), outputs);
971 timeScale = package.getTimeScale().value_or(slang::TimeScale());
972 auto timeScaleGuard =
973 llvm::make_scope_exit([&] {
timeScale = prevTimeScale; });
975 OpBuilder::InsertionGuard g(
builder);
978 for (
auto &member : package.members()) {
980 if (failed(member.visit(PackageVisitor(*
this, loc))))
996 return lowering.get();
999 if (!subroutine.thisVar) {
1001 SmallString<64> name;
1003 name += subroutine.name;
1005 SmallVector<Type, 1> noThis = {};
1012 const slang::ast::Type &thisTy = subroutine.thisVar->getType();
1013 moore::ClassDeclOp ownerDecl;
1015 if (
auto *classTy = thisTy.as_if<slang::ast::ClassType>()) {
1016 auto &ownerLowering =
classes[classTy];
1017 ownerDecl = ownerLowering->op;
1019 mlir::emitError(loc) <<
"expected 'this' to be a class type, got "
1020 << thisTy.toString();
1024 if (subroutine.flags.has(slang::ast::MethodFlags::Virtual)) {
1025 mlir::emitError(loc) <<
"Virtual methods are not yet supported!";
1030 SmallString<64> qualName;
1031 qualName += ownerDecl.getSymName();
1033 qualName += subroutine.name;
1036 SmallVector<Type, 1> extraParams;
1038 auto classSym = mlir::FlatSymbolRefAttr::get(ownerDecl.getSymNameAttr());
1039 auto handleTy = moore::ClassHandleType::get(
getContext(), classSym);
1040 extraParams.push_back(handleTy);
1051 mlir::StringRef qualifiedName,
1052 llvm::SmallVectorImpl<Type> &extraParams) {
1053 using slang::ast::ArgumentDirection;
1055 std::unique_ptr<FunctionLowering> lowering =
1056 std::make_unique<FunctionLowering>();
1061 OpBuilder::InsertionGuard g(
builder);
1066 builder.setInsertionPoint(it->second);
1069 SmallVector<Type> inputTypes;
1070 inputTypes.append(extraParams.begin(), extraParams.end());
1071 SmallVector<Type, 1> outputTypes;
1073 for (
const auto *arg : subroutine.getArguments()) {
1077 if (arg->direction == ArgumentDirection::In) {
1078 inputTypes.push_back(type);
1080 inputTypes.push_back(
1081 moore::RefType::get(cast<moore::UnpackedType>(type)));
1085 if (!subroutine.getReturnType().isVoid()) {
1086 auto type =
convertType(subroutine.getReturnType());
1089 outputTypes.push_back(type);
1092 auto funcType = FunctionType::get(
getContext(), inputTypes, outputTypes);
1096 mlir::func::FuncOp::create(
builder, loc, qualifiedName, funcType);
1097 SymbolTable::setSymbolVisibility(funcOp, SymbolTable::Visibility::Private);
1099 lowering->op = funcOp;
1104 functions[&subroutine] = std::move(lowering);
1113 ArrayRef<Value> captures) {
1114 if (captures.empty())
1117 mlir::ModuleOp
module = callee->getParentOfType<mlir::ModuleOp>();
1119 return callee.emitError(
"expected callee to be nested under ModuleOp");
1121 auto usesOpt = mlir::SymbolTable::getSymbolUses(callee, module);
1123 return callee.emitError(
"failed to compute symbol uses");
1126 SmallVector<mlir::func::CallOp, 8> callSites;
1127 callSites.reserve(std::distance(usesOpt->begin(), usesOpt->end()));
1128 for (
const mlir::SymbolTable::SymbolUse &use : *usesOpt) {
1129 if (
auto call = llvm::dyn_cast<mlir::func::CallOp>(use.getUser()))
1130 callSites.push_back(call);
1132 if (callSites.empty())
1135 Block &entry = callee.getBody().front();
1136 const unsigned numCaps = captures.size();
1137 const unsigned numEntryArgs = entry.getNumArguments();
1138 if (numEntryArgs < numCaps)
1139 return callee.emitError(
"entry block has fewer args than captures");
1140 const unsigned capArgStart = numEntryArgs - numCaps;
1143 auto fTy = callee.getFunctionType();
1145 for (
auto call : callSites) {
1146 SmallVector<Value> newOperands(call.getArgOperands().begin(),
1147 call.getArgOperands().end());
1149 const bool inSameFunc = callee->isProperAncestor(call);
1152 for (
unsigned i = 0; i < numCaps; ++i)
1153 newOperands.push_back(entry.getArgument(capArgStart + i));
1156 newOperands.append(captures.begin(), captures.end());
1160 auto flatRef = mlir::FlatSymbolRefAttr::get(callee);
1161 auto newCall = mlir::func::CallOp::create(
1162 b, call.getLoc(), fTy.getResults(), flatRef, newOperands);
1163 call->replaceAllUsesWith(newCall.getOperation());
1176 timeScale = subroutine.getTimeScale().value_or(slang::TimeScale());
1177 auto timeScaleGuard =
1178 llvm::make_scope_exit([&] {
timeScale = prevTimeScale; });
1187 if (lowering->capturesFinalized || lowering->isConverting)
1190 const bool isMethod = (subroutine.thisVar !=
nullptr);
1195 SmallVector<moore::VariableOp> argVariables;
1196 auto &block = lowering->op.getBody().emplaceBlock();
1202 auto thisType = lowering->op.getFunctionType().getInput(0);
1203 auto thisArg = block.addArgument(thisType, thisLoc);
1210 auto inputs = lowering->op.getFunctionType().getInputs();
1211 auto astArgs = subroutine.getArguments();
1212 auto valInputs = llvm::ArrayRef<Type>(inputs).drop_front(isMethod ? 1 : 0);
1214 for (
auto [astArg, type] : llvm::zip(astArgs, valInputs)) {
1216 auto blockArg = block.addArgument(type, loc);
1218 if (isa<moore::RefType>(type)) {
1221 OpBuilder::InsertionGuard g(
builder);
1222 builder.setInsertionPointToEnd(&block);
1224 auto shadowArg = moore::VariableOp::create(
1225 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
1226 StringAttr{}, blockArg);
1228 argVariables.push_back(shadowArg);
1233 OpBuilder::InsertionGuard g(
builder);
1234 builder.setInsertionPointToEnd(&block);
1237 if (subroutine.returnValVar) {
1238 auto type =
convertType(*subroutine.returnValVar->getDeclaredType());
1241 returnVar = moore::VariableOp::create(
1242 builder, lowering->op.getLoc(),
1243 moore::RefType::get(cast<moore::UnpackedType>(type)), StringAttr{},
1245 valueSymbols.insert(subroutine.returnValVar, returnVar);
1251 auto prevRCbGuard = llvm::make_scope_exit([&] {
1258 mlir::Value ref = rop.getInput();
1261 mlir::Type ty = ref.getType();
1262 if (!ty || !(isa<moore::RefType>(ty)))
1266 mlir::Region *defReg = ref.getParentRegion();
1267 if (defReg && lowering->op.getBody().isAncestor(defReg))
1271 if (lowering->captureIndex.count(ref))
1275 auto [it, inserted] =
1276 lowering->captureIndex.try_emplace(ref, lowering->captures.size());
1278 lowering->captures.push_back(ref);
1286 mlir::Value dstRef =
1287 llvm::TypeSwitch<mlir::Operation *, mlir::Value>(op)
1288 .Case<moore::BlockingAssignOp, moore::NonBlockingAssignOp,
1289 moore::DelayedNonBlockingAssignOp>(
1290 [](
auto op) {
return op.getDst(); })
1291 .Default([](
auto) -> mlir::Value {
return {}; });
1294 mlir::Type ty = dstRef.getType();
1295 if (!ty || !(isa<moore::RefType>(ty)))
1299 mlir::Region *defReg = dstRef.getParentRegion();
1300 if (defReg && lowering->op.getBody().isAncestor(defReg))
1304 if (lowering->captureIndex.count(dstRef))
1308 auto [it, inserted] =
1309 lowering->captureIndex.try_emplace(dstRef, lowering->captures.size());
1311 lowering->captures.push_back(dstRef);
1320 auto restoreThis = llvm::make_scope_exit([&] {
currentThisRef = savedThis; });
1322 lowering->isConverting =
true;
1323 auto convertingGuard =
1324 llvm::make_scope_exit([&] { lowering->isConverting =
false; });
1341 if (returnVar && !subroutine.getReturnType().isVoid()) {
1343 moore::ReadOp::create(
builder, returnVar.getLoc(), returnVar);
1344 mlir::func::ReturnOp::create(
builder, lowering->op.getLoc(), read);
1346 mlir::func::ReturnOp::create(
builder, lowering->op.getLoc(),
1350 if (returnVar && returnVar.use_empty())
1351 returnVar.getDefiningOp()->erase();
1353 for (
auto var : argVariables) {
1354 if (llvm::all_of(var->getUsers(),
1355 [](
auto *user) { return isa<moore::ReadOp>(user); })) {
1356 for (
auto *user : llvm::make_early_inc_range(var->getUsers())) {
1357 user->getResult(0).replaceAllUsesWith(var.getInitial());
1364 lowering->capturesFinalized =
true;
1376 SmallVector<Type> newInputs(lowering.
op.getFunctionType().getInputs().begin(),
1377 lowering.
op.getFunctionType().getInputs().end());
1379 for (Value cap : lowering.
captures) {
1381 Type capTy = cap.getType();
1382 if (!isa<moore::RefType>(capTy)) {
1383 return lowering.
op.emitError(
1384 "expected captured value to be a ref-like type");
1386 newInputs.push_back(capTy);
1390 auto newFuncTy = FunctionType::get(
1391 ctx, newInputs, lowering.
op.getFunctionType().getResults());
1392 lowering.
op.setFunctionType(newFuncTy);
1395 Block &entry = lowering.
op.getBody().front();
1396 SmallVector<Value> capArgs;
1397 capArgs.reserve(lowering.
captures.size());
1399 llvm::ArrayRef<Type>(newInputs).take_back(lowering.
captures.size())) {
1400 capArgs.push_back(entry.addArgument(t, lowering.
op.getLoc()));
1406 Value arg = capArgs[idx];
1407 cap.replaceUsesWithIf(arg, [&](OpOperand &use) {
1408 return lowering.
op->isProperAncestor(use.getOwner());
1419mlir::StringAttr fullyQualifiedClassName(
Context &ctx,
1420 const slang::ast::Type &ty) {
1421 SmallString<64> name;
1422 SmallVector<llvm::StringRef, 8> parts;
1424 const slang::ast::Scope *scope = ty.getParentScope();
1426 const auto &sym = scope->asSymbol();
1428 case slang::ast::SymbolKind::Root:
1431 case slang::ast::SymbolKind::InstanceBody:
1432 case slang::ast::SymbolKind::Instance:
1433 case slang::ast::SymbolKind::Package:
1434 case slang::ast::SymbolKind::ClassType:
1435 if (!sym.name.empty())
1436 parts.push_back(sym.name);
1441 scope = sym.getParentScope();
1444 for (
auto p :
llvm::reverse(parts)) {
1449 return mlir::StringAttr::get(ctx.
getContext(), name);
1454std::pair<mlir::SymbolRefAttr, mlir::ArrayAttr>
1455buildBaseAndImplementsAttrs(
Context &context,
1456 const slang::ast::ClassType &cls) {
1457 mlir::MLIRContext *ctx = context.
getContext();
1460 mlir::SymbolRefAttr base;
1461 if (
const auto *b = cls.getBaseClass())
1462 base = mlir::SymbolRefAttr::get(fullyQualifiedClassName(context, *b));
1465 SmallVector<mlir::Attribute> impls;
1466 if (
auto ifaces = cls.getDeclaredInterfaces(); !ifaces.empty()) {
1467 impls.reserve(ifaces.size());
1468 for (
const auto *iface : ifaces)
1469 impls.push_back(
mlir::FlatSymbolRefAttr::
get(
1470 fullyQualifiedClassName(context, *iface)));
1473 mlir::ArrayAttr implArr =
1474 impls.empty() ? mlir::ArrayAttr() :
mlir::ArrayAttr::
get(ctx, impls);
1476 return {base, implArr};
1481struct ClassDeclVisitor {
1487 : context(ctx), builder(ctx.builder), classLowering(lowering) {}
1489 LogicalResult
run(
const slang::ast::ClassType &classAST) {
1490 if (!classLowering.
op.getBody().empty())
1493 OpBuilder::InsertionGuard ig(builder);
1495 Block *body = &classLowering.
op.getBody().emplaceBlock();
1496 builder.setInsertionPointToEnd(body);
1498 for (
const auto &mem : classAST.members())
1499 if (failed(mem.visit(*this)))
1506 LogicalResult visit(
const slang::ast::ClassPropertySymbol &prop) {
1512 moore::ClassPropertyDeclOp::create(builder, loc, prop.name, ty);
1518 LogicalResult visit(
const slang::ast::ParameterSymbol &) {
return success(); }
1521 LogicalResult visit(
const slang::ast::SubroutineSymbol &fn) {
1522 if (fn.flags & slang::ast::MethodFlags::BuiltIn) {
1523 static bool remarkEmitted =
false;
1527 mlir::emitRemark(classLowering.
op.getLoc())
1528 <<
"Class builtin functions (needed for randomization, constraints, "
1529 "and covergroups) are not yet supported and will be dropped "
1531 remarkEmitted =
true;
1543 if (!lowering->capturesFinalized)
1547 FunctionType fnTy = lowering->op.getFunctionType();
1550 moore::ClassMethodDeclOp::create(builder, loc, fn.name, fnTy);
1567 LogicalResult visit(
const slang::ast::MethodPrototypeSymbol &fn) {
1568 const auto *externImpl = fn.getSubroutine();
1572 <<
"Didn't find an implementation matching the forward declaration "
1577 return visit(*externImpl);
1581 LogicalResult visit(
const slang::ast::GenericClassDefSymbol &) {
1586 LogicalResult visit(
const slang::ast::ClassType &cls) {
1591 LogicalResult visit(
const slang::ast::TransparentMemberSymbol &) {
1596 LogicalResult visit(
const slang::ast::EmptyMemberSymbol &) {
1601 template <
typename T>
1602 LogicalResult visit(T &&node) {
1603 Location loc = UnknownLoc::get(context.
getContext());
1604 if constexpr (
requires { node.location; })
1606 mlir::emitError(loc) <<
"unsupported construct in ClassType members: "
1607 << slang::ast::toString(node.kind);
1620 auto &lowering =
classes[&cls];
1624 return lowering.get();
1627 lowering = std::make_unique<ClassLowering>();
1632 OpBuilder::InsertionGuard g(
builder);
1637 builder.setInsertionPoint(it->second);
1639 auto symName = fullyQualifiedClassName(*
this, cls);
1640 auto [base, impls] = buildBaseAndImplementsAttrs(*
this, cls);
1643 moore::ClassDeclOp::create(
builder, loc, symName, base, impls);
1645 SymbolTable::setSymbolVisibility(classDeclOp,
1646 SymbolTable::Visibility::Public);
1648 lowering->op = classDeclOp;
1651 return lowering.get();
1659 timeScale = classdecl.getTimeScale().value_or(slang::TimeScale());
1660 auto timeScaleGuard =
1661 llvm::make_scope_exit([&] {
timeScale = prevTimeScale; });
1664 if (failed(ClassDeclVisitor(*
this, *lowering).run(classdecl)))
assert(baseType &&"element must be base type")
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 Location getLoc(DefSlot slot)
static moore::ProcedureKind convertProcedureKind(slang::ast::ProceduralBlockKind kind)
static void guessNamespacePrefix(const slang::ast::Symbol &symbol, SmallString< 64 > &prefix)
static LogicalResult rewriteCallSitesToPassCaptures(mlir::func::FuncOp callee, ArrayRef< Value > captures)
Special case handling for recursive functions with captures; this function fixes the in-body call of ...
static moore::NetKind convertNetKind(slang::ast::NetType::NetKind kind)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
bool lowerAlwaysAtStarAsComb
Interpret always @(*) as always_comb.
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.
LogicalResult convertFunction(const slang::ast::SubroutineSymbol &subroutine)
Convert a function.
Value materializeConversion(Type type, Value value, bool isSigned, Location loc)
Helper function to insert the necessary operations to cast a value from one type to another.
FunctionLowering * declareCallableImpl(const slang::ast::SubroutineSymbol &subroutine, mlir::StringRef qualifiedName, llvm::SmallVectorImpl< Type > &extraParams)
Helper function to extract the commonalities in lowering of functions and methods.
ModuleLowering * convertModuleHeader(const slang::ast::InstanceBodySymbol *module)
Convert a module and its ports to an empty module op in the IR.
Value convertLvalueExpression(const slang::ast::Expression &expr)
Value materializeConstant(const slang::ConstantValue &constant, const slang::ast::Type &type, Location loc)
Helper function to materialize a ConstantValue as an SSA value.
LogicalResult convertModuleBody(const slang::ast::InstanceBodySymbol *module)
Convert a module's body to the corresponding IR ops.
LogicalResult convertClassDeclaration(const slang::ast::ClassType &classdecl)
slang::ast::Compilation & compilation
OpBuilder builder
The builder used to create IR operations.
std::queue< const slang::ast::InstanceBodySymbol * > moduleWorklist
A list of modules for which the header has been created, but the body has not been converted yet.
DenseMap< const slang::ast::ClassType *, std::unique_ptr< ClassLowering > > classes
Classes that have already been converted.
std::function< void(moore::ReadOp)> rvalueReadCallback
A listener called for every variable or net being read.
std::map< slang::SourceLocation, Operation * > orderedRootOps
The top-level operations ordered by their Slang source location.
Type convertType(const slang::ast::Type &type, LocationAttr loc={})
Convert a slang type into an MLIR type.
DenseMap< const slang::ast::SubroutineSymbol *, std::unique_ptr< FunctionLowering > > functions
Functions that have already been converted.
slang::TimeScale timeScale
The time scale currently in effect.
LogicalResult finalizeFunctionBodyCaptures(FunctionLowering &lowering)
ClassLowering * declareClass(const slang::ast::ClassType &cls)
ValueSymbols valueSymbols
std::function< void(mlir::Operation *)> variableAssignCallback
A listener called for every variable or net being assigned.
ValueSymbols::ScopeTy ValueSymbolScope
const ImportVerilogOptions & options
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
LogicalResult traverseInstanceBody(const slang::ast::Symbol &symbol)
Value currentThisRef
Variable to track the value of the current function's implicit this reference.
mlir::ModuleOp intoModuleOp
SymbolTable symbolTable
A symbol table of the MLIR module we are emitting into.
DenseMap< const slang::ast::InstanceBodySymbol *, SmallVector< HierPathInfo > > hierPaths
Collect all hierarchical names used for the per module/instance.
FunctionLowering * declareFunction(const slang::ast::SubroutineSymbol &subroutine)
Convert a function and its arguments to a function declaration in the IR.
LogicalResult convertPackage(const slang::ast::PackageSymbol &package)
Convert a package and its contents.
MLIRContext * getContext()
Return the MLIR context.
LogicalResult convertStatement(const slang::ast::Statement &stmt)
DenseMap< const slang::ast::InstanceBodySymbol *, std::unique_ptr< ModuleLowering > > modules
How we have lowered modules to MLIR.
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.
Function lowering information.
llvm::DenseMap< Value, unsigned > captureIndex
llvm::SmallVector< Value, 4 > captures
Module lowering information.