11#include "mlir/Dialect/Func/IR/FuncOps.h"
12#include "mlir/IR/Builders.h"
13#include "mlir/IR/Diagnostics.h"
14#include "slang/ast/Compilation.h"
15#include "slang/ast/SemanticFacts.h"
16#include "slang/ast/Statement.h"
17#include "slang/ast/SystemSubroutine.h"
18#include "llvm/ADT/ScopeExit.h"
22using namespace ImportVerilog;
34 bool isTerminated()
const {
return !builder.getInsertionBlock(); }
35 void setTerminated() { builder.clearInsertionPoint(); }
37 Block &createBlock() {
38 assert(builder.getInsertionBlock());
39 auto block = std::make_unique<Block>();
40 block->insertAfter(builder.getInsertionBlock());
41 return *block.release();
44 LogicalResult recursiveForeach(
const slang::ast::ForeachLoopStatement &stmt,
47 const auto &loopDim = stmt.loopDims[level];
48 if (!loopDim.range.has_value())
49 return mlir::emitError(loc) <<
"dynamic loop variable is unsupported";
50 auto &exitBlock = createBlock();
51 auto &stepBlock = createBlock();
52 auto &bodyBlock = createBlock();
53 auto &checkBlock = createBlock();
56 context.loopStack.push_back({&stepBlock, &exitBlock});
57 llvm::scope_exit done([&] {
context.loopStack.pop_back(); });
59 const auto &iter = loopDim.loopVar;
60 auto type =
context.convertType(*iter->getDeclaredType());
64 Value initial = moore::ConstantOp::create(
65 builder, loc, cast<moore::IntType>(type), loopDim.range->lower());
68 Value varOp = moore::VariableOp::create(
69 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
70 builder.getStringAttr(iter->name), initial);
71 context.valueSymbols.insertIntoScope(
context.valueSymbols.getCurScope(),
74 cf::BranchOp::create(builder, loc, &checkBlock);
75 builder.setInsertionPointToEnd(&checkBlock);
78 auto upperBound = moore::ConstantOp::create(
79 builder, loc, cast<moore::IntType>(type), loopDim.range->upper());
81 auto var = moore::ReadOp::create(builder, loc, varOp);
82 Value cond = moore::SleOp::create(builder, loc, var, upperBound);
85 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
86 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
87 ty && ty.getDomain() == Domain::FourValued) {
88 cond = moore::LogicToIntOp::create(builder, loc, cond);
90 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
91 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
93 builder.setInsertionPointToEnd(&bodyBlock);
98 for (uint32_t nextLevel = level + 1; nextLevel < stmt.loopDims.size();
100 if (stmt.loopDims[nextLevel].loopVar) {
101 if (failed(recursiveForeach(stmt, nextLevel)))
109 if (failed(
context.convertStatement(stmt.body)))
113 cf::BranchOp::create(builder, loc, &stepBlock);
115 builder.setInsertionPointToEnd(&stepBlock);
118 var = moore::ReadOp::create(builder, loc, varOp);
120 moore::ConstantOp::create(builder, loc, cast<moore::IntType>(type), 1);
121 auto postValue = moore::AddOp::create(builder, loc, var, one).getResult();
122 moore::BlockingAssignOp::create(builder, loc, varOp, postValue);
123 cf::BranchOp::create(builder, loc, &checkBlock);
125 if (exitBlock.hasNoPredecessors()) {
129 builder.setInsertionPointToEnd(&exitBlock);
135 LogicalResult visit(
const slang::ast::EmptyStatement &) {
return success(); }
144 LogicalResult visit(
const slang::ast::StatementList &stmts) {
145 for (
auto *stmt : stmts.list) {
146 if (isTerminated()) {
147 auto loc =
context.convertLocation(stmt->sourceRange);
148 mlir::emitWarning(loc,
"unreachable code");
151 if (failed(
context.convertStatement(*stmt)))
161 LogicalResult visit(
const slang::ast::BlockStatement &stmt) {
162 moore::JoinKind kind;
163 switch (stmt.blockKind) {
164 case slang::ast::StatementBlockKind::Sequential:
166 return context.convertStatement(stmt.body);
167 case slang::ast::StatementBlockKind::JoinAll:
168 kind = moore::JoinKind::Join;
170 case slang::ast::StatementBlockKind::JoinAny:
171 kind = moore::JoinKind::JoinAny;
173 case slang::ast::StatementBlockKind::JoinNone:
174 kind = moore::JoinKind::JoinNone;
181 auto *threadList = stmt.body.as_if<slang::ast::StatementList>();
182 unsigned int threadCount = threadList ? threadList->list.size() : 1;
184 auto forkOp = moore::ForkJoinOp::create(builder, loc, kind, threadCount);
185 OpBuilder::InsertionGuard guard(builder);
190 auto &tBlock = forkOp->getRegion(0).emplaceBlock();
191 builder.setInsertionPointToStart(&tBlock);
192 if (failed(
context.convertStatement(stmt.body)))
194 moore::CompleteOp::create(builder, loc);
199 for (
auto *thread : threadList->list) {
200 auto &tBlock = forkOp->getRegion(i).emplaceBlock();
201 builder.setInsertionPointToStart(&tBlock);
204 if (failed(
context.convertStatement(*thread)))
206 moore::CompleteOp::create(builder, loc);
213 LogicalResult visit(
const slang::ast::ExpressionStatement &stmt) {
215 if (
const auto *call = stmt.expr.as_if<slang::ast::CallExpression>()) {
216 if (
const auto *info =
217 std::get_if<slang::ast::CallExpression::SystemCallInfo>(
218 &call->subroutine)) {
219 auto handled = visitSystemCall(stmt, *call, *info);
233 if (!call->getSubroutineName().compare(
"$sformat") ||
234 !call->getSubroutineName().compare(
"$swrite")) {
237 auto *lhsExpr = call->arguments().front();
240 context.convertFormatString(call->arguments().subspan(1), loc,
241 moore::IntFormat::Decimal,
false);
242 if (failed(fmtValue))
245 auto strValue = moore::FormatStringToStringOp::create(builder, loc,
251 if (
auto assignExpr =
252 lhsExpr->as_if<slang::ast::AssignmentExpression>()) {
253 auto lhs =
context.convertLvalueExpression(assignExpr->left());
257 auto convertedValue =
context.materializeConversion(
258 cast<moore::RefType>(lhs.getType()).getNestedType(), strValue,
260 moore::BlockingAssignOp::create(builder, loc, lhs, convertedValue);
268 auto value =
context.convertRvalueExpression(stmt.expr);
274 if (
auto *defOp = value.getDefiningOp())
275 if (isOpTriviallyDead(defOp))
282 LogicalResult visit(
const slang::ast::VariableDeclStatement &stmt) {
283 const auto &var = stmt.symbol;
284 auto type =
context.convertType(*var.getDeclaredType());
289 if (
const auto *init = var.getInitializer()) {
290 initial =
context.convertRvalueExpression(*init, type);
296 auto varOp = moore::VariableOp::create(
297 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
298 builder.getStringAttr(var.name), initial);
299 context.valueSymbols.insertIntoScope(
context.valueSymbols.getCurScope(),
301 const auto &canonTy = var.getType().getCanonicalType();
302 if (
const auto *vi = canonTy.as_if<slang::ast::VirtualInterfaceType>())
303 if (failed(
context.registerVirtualInterfaceMembers(var, *vi, loc)))
309 LogicalResult visit(
const slang::ast::ConditionalStatement &stmt) {
313 for (
const auto &condition : stmt.conditions) {
314 if (condition.pattern)
315 return mlir::emitError(loc,
316 "match patterns in if conditions not supported");
317 auto cond =
context.convertRvalueExpression(*condition.expr);
320 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
322 allConds = moore::AndOp::create(builder, loc, allConds, cond);
326 assert(allConds &&
"slang guarantees at least one condition");
327 if (
auto ty = dyn_cast<moore::IntType>(allConds.getType());
328 ty && ty.getDomain() == Domain::FourValued) {
329 allConds = moore::LogicToIntOp::create(builder, loc, allConds);
331 allConds = moore::ToBuiltinIntOp::create(builder, loc, allConds);
334 Block &exitBlock = createBlock();
335 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
336 Block &trueBlock = createBlock();
337 cf::CondBranchOp::create(builder, loc, allConds, &trueBlock,
338 falseBlock ? falseBlock : &exitBlock);
341 builder.setInsertionPointToEnd(&trueBlock);
342 if (failed(
context.convertStatement(stmt.ifTrue)))
345 cf::BranchOp::create(builder, loc, &exitBlock);
349 builder.setInsertionPointToEnd(falseBlock);
350 if (failed(
context.convertStatement(*stmt.ifFalse)))
353 cf::BranchOp::create(builder, loc, &exitBlock);
358 if (exitBlock.hasNoPredecessors()) {
362 builder.setInsertionPointToEnd(&exitBlock);
368 LogicalResult visit(
const slang::ast::CaseStatement &caseStmt) {
369 using slang::ast::AttributeSymbol;
370 using slang::ast::CaseStatementCondition;
371 auto caseExpr =
context.convertRvalueExpression(caseStmt.expr);
378 auto &exitBlock = createBlock();
379 Block *lastMatchBlock =
nullptr;
380 SmallVector<moore::FVIntegerAttr> itemConsts;
382 for (
const auto &item : caseStmt.items) {
385 auto &matchBlock = createBlock();
386 lastMatchBlock = &matchBlock;
391 for (
const auto *expr : item.expressions) {
395 if (caseStmt.condition == CaseStatementCondition::Inside) {
397 cond =
context.convertInsideCheck(
398 context.convertToSimpleBitVector(caseExpr), itemLoc, *expr);
402 auto value =
context.convertRvalueExpression(*expr);
405 itemLoc = value.getLoc();
408 auto maybeConst = value;
410 isa_and_nonnull<moore::ConversionOp, moore::IntToLogicOp,
411 moore::LogicToIntOp>(maybeConst.getDefiningOp()))
412 maybeConst = maybeConst.getDefiningOp()->getOperand(0);
413 if (
auto defOp = maybeConst.getDefiningOp<moore::ConstantOp>())
414 itemConsts.push_back(defOp.getValueAttr());
417 switch (caseStmt.condition) {
418 case CaseStatementCondition::Normal:
419 cond = moore::CaseEqOp::create(builder, itemLoc, caseExpr, value);
421 case CaseStatementCondition::WildcardXOrZ:
422 cond = moore::CaseXZEqOp::create(builder, itemLoc, caseExpr, value);
424 case CaseStatementCondition::WildcardJustZ:
425 cond = moore::CaseZEqOp::create(builder, itemLoc, caseExpr, value);
427 case CaseStatementCondition::Inside:
428 llvm_unreachable(
"Inside condition has been handled already");
433 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
434 ty && ty.getDomain() == Domain::FourValued) {
435 cond = moore::LogicToIntOp::create(builder, loc, cond);
437 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
441 auto &nextBlock = createBlock();
442 mlir::cf::CondBranchOp::create(builder, itemLoc, cond, &matchBlock,
444 builder.setInsertionPointToEnd(&nextBlock);
450 matchBlock.moveBefore(builder.getInsertionBlock());
453 OpBuilder::InsertionGuard guard(builder);
454 builder.setInsertionPointToEnd(&matchBlock);
455 if (failed(
context.convertStatement(*item.stmt)))
457 if (!isTerminated()) {
458 auto loc =
context.convertLocation(item.stmt->sourceRange);
459 mlir::cf::BranchOp::create(builder, loc, &exitBlock);
463 const auto caseStmtAttrs =
context.compilation.getAttributes(caseStmt);
464 const bool hasFullCaseAttr =
465 llvm::find_if(caseStmtAttrs, [](
const AttributeSymbol *attr) {
466 return attr->name ==
"full_case";
467 }) != caseStmtAttrs.end();
478 auto twoStateExhaustive =
false;
479 if (
auto intType = dyn_cast<moore::IntType>(caseExpr.getType());
480 intType && intType.getWidth() < 32 &&
481 itemConsts.size() == (1 << intType.getWidth())) {
483 llvm::sort(itemConsts, [](
auto a,
auto b) {
484 return a.getValue().getRawValue().ult(
b.getValue().getRawValue());
492 for (
auto value : itemConsts) {
493 if (value.getValue() != nextValue)
497 twoStateExhaustive = nextValue.isZero();
508 if ((twoStateExhaustive || (hasFullCaseAttr && !caseStmt.defaultCase)) &&
510 caseStmt.condition == CaseStatementCondition::Normal) {
511 mlir::cf::BranchOp::create(builder, loc, lastMatchBlock);
514 if (caseStmt.defaultCase)
515 if (failed(
context.convertStatement(*caseStmt.defaultCase)))
518 mlir::cf::BranchOp::create(builder, loc, &exitBlock);
523 if (exitBlock.hasNoPredecessors()) {
527 builder.setInsertionPointToEnd(&exitBlock);
533 LogicalResult visit(
const slang::ast::ForLoopStatement &stmt) {
535 for (
auto *initExpr : stmt.initializers)
536 if (!
context.convertRvalueExpression(*initExpr))
540 auto &exitBlock = createBlock();
541 auto &stepBlock = createBlock();
542 auto &bodyBlock = createBlock();
543 auto &checkBlock = createBlock();
544 cf::BranchOp::create(builder, loc, &checkBlock);
547 context.loopStack.push_back({&stepBlock, &exitBlock});
548 llvm::scope_exit done([&] {
context.loopStack.pop_back(); });
551 builder.setInsertionPointToEnd(&checkBlock);
552 auto cond =
context.convertRvalueExpression(*stmt.stopExpr);
555 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
556 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
557 ty && ty.getDomain() == Domain::FourValued) {
558 cond = moore::LogicToIntOp::create(builder, loc, cond);
560 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
561 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
564 builder.setInsertionPointToEnd(&bodyBlock);
565 if (failed(
context.convertStatement(stmt.body)))
568 cf::BranchOp::create(builder, loc, &stepBlock);
571 builder.setInsertionPointToEnd(&stepBlock);
572 for (
auto *stepExpr : stmt.steps)
573 if (!
context.convertRvalueExpression(*stepExpr))
576 cf::BranchOp::create(builder, loc, &checkBlock);
580 if (exitBlock.hasNoPredecessors()) {
584 builder.setInsertionPointToEnd(&exitBlock);
589 LogicalResult visit(
const slang::ast::ForeachLoopStatement &stmt) {
590 for (uint32_t level = 0; level < stmt.loopDims.size(); level++) {
591 if (stmt.loopDims[level].loopVar)
592 return recursiveForeach(stmt, level);
598 LogicalResult visit(
const slang::ast::RepeatLoopStatement &stmt) {
599 auto intType = moore::IntType::getInt(
context.getContext(), 32);
600 auto count =
context.convertRvalueExpression(stmt.count, intType);
605 auto &exitBlock = createBlock();
606 auto &stepBlock = createBlock();
607 auto &bodyBlock = createBlock();
608 auto &checkBlock = createBlock();
609 auto currentCount = checkBlock.addArgument(count.getType(), count.getLoc());
610 cf::BranchOp::create(builder, loc, &checkBlock, count);
613 context.loopStack.push_back({&stepBlock, &exitBlock});
614 llvm::scope_exit done([&] {
context.loopStack.pop_back(); });
617 builder.setInsertionPointToEnd(&checkBlock);
618 auto cond = builder.createOrFold<moore::BoolCastOp>(loc, currentCount);
619 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
620 ty && ty.getDomain() == Domain::FourValued) {
621 cond = moore::LogicToIntOp::create(builder, loc, cond);
623 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
624 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
627 builder.setInsertionPointToEnd(&bodyBlock);
628 if (failed(
context.convertStatement(stmt.body)))
631 cf::BranchOp::create(builder, loc, &stepBlock);
634 builder.setInsertionPointToEnd(&stepBlock);
635 auto one = moore::ConstantOp::create(
636 builder, count.getLoc(), cast<moore::IntType>(count.getType()), 1);
638 moore::SubOp::create(builder, count.getLoc(), currentCount, one);
639 cf::BranchOp::create(builder, loc, &checkBlock, nextCount);
643 if (exitBlock.hasNoPredecessors()) {
647 builder.setInsertionPointToEnd(&exitBlock);
653 LogicalResult createWhileLoop(
const slang::ast::Expression &condExpr,
654 const slang::ast::Statement &bodyStmt,
657 auto &exitBlock = createBlock();
658 auto &bodyBlock = createBlock();
659 auto &checkBlock = createBlock();
660 cf::BranchOp::create(builder, loc, atLeastOnce ? &bodyBlock : &checkBlock);
662 bodyBlock.moveBefore(&checkBlock);
665 context.loopStack.push_back({&checkBlock, &exitBlock});
666 llvm::scope_exit done([&] {
context.loopStack.pop_back(); });
669 builder.setInsertionPointToEnd(&checkBlock);
670 auto cond =
context.convertRvalueExpression(condExpr);
673 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
674 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
675 ty && ty.getDomain() == Domain::FourValued) {
676 cond = moore::LogicToIntOp::create(builder, loc, cond);
678 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
679 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
682 builder.setInsertionPointToEnd(&bodyBlock);
683 if (failed(
context.convertStatement(bodyStmt)))
686 cf::BranchOp::create(builder, loc, &checkBlock);
690 if (exitBlock.hasNoPredecessors()) {
694 builder.setInsertionPointToEnd(&exitBlock);
699 LogicalResult visit(
const slang::ast::WhileLoopStatement &stmt) {
700 return createWhileLoop(stmt.cond, stmt.body,
false);
703 LogicalResult visit(
const slang::ast::DoWhileLoopStatement &stmt) {
704 return createWhileLoop(stmt.cond, stmt.body,
true);
708 LogicalResult visit(
const slang::ast::ForeverLoopStatement &stmt) {
710 auto &exitBlock = createBlock();
711 auto &bodyBlock = createBlock();
712 cf::BranchOp::create(builder, loc, &bodyBlock);
715 context.loopStack.push_back({&bodyBlock, &exitBlock});
716 llvm::scope_exit done([&] {
context.loopStack.pop_back(); });
719 builder.setInsertionPointToEnd(&bodyBlock);
720 if (failed(
context.convertStatement(stmt.body)))
723 cf::BranchOp::create(builder, loc, &bodyBlock);
727 if (exitBlock.hasNoPredecessors()) {
731 builder.setInsertionPointToEnd(&exitBlock);
737 LogicalResult visit(
const slang::ast::TimedStatement &stmt) {
738 return context.convertTimingControl(stmt.timing, stmt.stmt);
742 LogicalResult visit(
const slang::ast::ReturnStatement &stmt) {
743 Operation *parentOp = builder.getInsertionBlock()
744 ? builder.getInsertionBlock()->getParentOp()
747 return mlir::emitError(loc) <<
"return statement is not within an op";
749 if (isa<moore::CoroutineOp, moore::ProcedureOp>(parentOp)) {
751 return mlir::emitError(loc)
752 <<
"unsupported `return <expr>` in a procedure or task";
753 moore::ReturnOp::create(builder, loc);
758 if (!isa<mlir::func::FuncOp>(parentOp))
759 return mlir::emitError(loc) <<
"unsupported return statement context";
762 auto expr =
context.convertRvalueExpression(*stmt.expr);
765 mlir::func::ReturnOp::create(builder, loc, expr);
767 mlir::func::ReturnOp::create(builder, loc);
774 LogicalResult visit(
const slang::ast::ContinueStatement &stmt) {
776 return mlir::emitError(loc,
777 "cannot `continue` without a surrounding loop");
778 cf::BranchOp::create(builder, loc,
context.loopStack.back().continueBlock);
784 LogicalResult visit(
const slang::ast::BreakStatement &stmt) {
786 return mlir::emitError(loc,
"cannot `break` without a surrounding loop");
787 cf::BranchOp::create(builder, loc,
context.loopStack.back().breakBlock);
793 LogicalResult visit(
const slang::ast::ImmediateAssertionStatement &stmt) {
794 auto cond =
context.convertRvalueExpression(stmt.cond);
795 cond =
context.convertToBool(cond);
800 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
801 auto defer = moore::DeferAssert::Immediate;
803 defer = moore::DeferAssert::Final;
804 else if (stmt.isDeferred)
805 defer = moore::DeferAssert::Observed;
807 switch (stmt.assertionKind) {
808 case slang::ast::AssertionKind::Assert:
809 moore::AssertOp::create(builder, loc, defer, cond, StringAttr{});
811 case slang::ast::AssertionKind::Assume:
812 moore::AssumeOp::create(builder, loc, defer, cond, StringAttr{});
814 case slang::ast::AssertionKind::CoverProperty:
815 moore::CoverOp::create(builder, loc, defer, cond, StringAttr{});
820 mlir::emitError(loc) <<
"unsupported immediate assertion kind: "
821 << slang::ast::toString(stmt.assertionKind);
826 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
827 ty && ty.getDomain() == Domain::FourValued) {
828 cond = moore::LogicToIntOp::create(builder, loc, cond);
830 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
833 Block &exitBlock = createBlock();
834 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
835 Block &trueBlock = createBlock();
836 cf::CondBranchOp::create(builder, loc, cond, &trueBlock,
837 falseBlock ? falseBlock : &exitBlock);
840 builder.setInsertionPointToEnd(&trueBlock);
841 if (stmt.ifTrue && failed(
context.convertStatement(*stmt.ifTrue)))
844 cf::BranchOp::create(builder, loc, &exitBlock);
848 builder.setInsertionPointToEnd(falseBlock);
849 if (failed(
context.convertStatement(*stmt.ifFalse)))
852 cf::BranchOp::create(builder, loc, &exitBlock);
857 if (exitBlock.hasNoPredecessors()) {
861 builder.setInsertionPointToEnd(&exitBlock);
867 LogicalResult visit(
const slang::ast::ConcurrentAssertionStatement &stmt) {
868 auto loc =
context.convertLocation(stmt.sourceRange);
879 const slang::ast::AssertionExpr *propertySpec;
880 const slang::ast::ClockingAssertionExpr *clocking =
881 stmt.propertySpec.as_if<slang::ast::ClockingAssertionExpr>();
883 propertySpec = &(clocking->expr);
885 propertySpec = &(stmt.propertySpec);
887 if (
auto *disableIff =
888 propertySpec->as_if<slang::ast::DisableIffAssertionExpr>()) {
891 auto disableCond =
context.convertRvalueExpression(disableIff->condition);
892 auto enableCond = moore::NotOp::create(builder, loc, disableCond);
894 enable =
context.convertToI1(enableCond);
898 auto clockingExpr = slang::ast::ClockingAssertionExpr(
899 clocking->clocking, disableIff->expr);
900 property =
context.convertAssertionExpression(clockingExpr, loc);
902 property =
context.convertAssertionExpression(disableIff->expr, loc);
905 property =
context.convertAssertionExpression(stmt.propertySpec, loc);
912 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
913 switch (stmt.assertionKind) {
914 case slang::ast::AssertionKind::Assert:
915 verif::AssertOp::create(builder, loc, property, enable, StringAttr{});
917 case slang::ast::AssertionKind::Assume:
918 verif::AssumeOp::create(builder, loc, property, enable, StringAttr{});
923 mlir::emitError(loc) <<
"unsupported concurrent assertion kind: "
924 << slang::ast::toString(stmt.assertionKind);
929 <<
"concurrent assertion statements with action blocks "
930 "are not supported yet";
944 getDisplayMessage(std::span<const slang::ast::Expression *const> args) {
945 if (args.size() == 0)
954 if (args[0]->as_if<slang::ast::StringLiteral>()) {
955 return context.convertFormatString(args, loc);
958 if (args.size() == 1) {
959 return context.convertRvalueExpression(
960 *args[0], builder.getType<moore::FormatStringType>());
963 return emitError(loc) <<
"Failed to convert Display Message!";
970 visitSystemCall(
const slang::ast::ExpressionStatement &stmt,
971 const slang::ast::CallExpression &expr,
972 const slang::ast::CallExpression::SystemCallInfo &info) {
973 using ksn = slang::parsing::KnownSystemName;
974 const auto &subroutine = *
info.subroutine;
975 auto nameId = subroutine.knownNameId;
976 auto args = expr.arguments();
980 if (nameId == ksn::Stop) {
981 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
982 moore::StopBIOp::create(builder, loc);
986 if (nameId == ksn::Finish) {
987 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
988 moore::FinishBIOp::create(builder, loc, 0);
989 moore::UnreachableOp::create(builder, loc);
994 if (nameId == ksn::Exit) {
1003 using moore::IntFormat;
1004 bool isDisplay =
false;
1005 bool appendNewline =
false;
1006 IntFormat defaultFormat = IntFormat::Decimal;
1010 appendNewline =
true;
1014 appendNewline =
true;
1015 defaultFormat = IntFormat::Binary;
1019 appendNewline =
true;
1020 defaultFormat = IntFormat::Octal;
1024 appendNewline =
true;
1025 defaultFormat = IntFormat::HexLower;
1032 defaultFormat = IntFormat::Binary;
1036 defaultFormat = IntFormat::Octal;
1040 defaultFormat = IntFormat::HexLower;
1048 context.convertFormatString(args, loc, defaultFormat, appendNewline);
1049 if (failed(message))
1051 if (*message == Value{})
1053 moore::DisplayBIOp::create(builder, loc, *message);
1058 using moore::Severity;
1059 std::optional<Severity> severity;
1060 if (nameId == ksn::Info)
1061 severity = Severity::Info;
1062 else if (nameId == ksn::Warning)
1063 severity = Severity::Warning;
1064 else if (nameId == ksn::Error)
1065 severity = Severity::Error;
1066 else if (nameId == ksn::Fatal)
1067 severity = Severity::Fatal;
1071 const slang::ast::Expression *verbosityExpr =
nullptr;
1072 if (severity == Severity::Fatal && args.size() >= 1) {
1073 verbosityExpr = args[0];
1074 args = args.subspan(1);
1077 FailureOr<Value> maybeMessage = getDisplayMessage(args);
1078 if (failed(maybeMessage))
1080 auto message = maybeMessage.value();
1082 if (message == Value{})
1083 message = moore::FormatLiteralOp::create(builder, loc,
"");
1084 moore::SeverityBIOp::create(builder, loc, *severity, message);
1087 if (severity == Severity::Fatal) {
1088 createFinishMessage(verbosityExpr);
1089 moore::FinishBIOp::create(builder, loc, 1);
1090 moore::UnreachableOp::create(builder, loc);
1098 if (args.size() >= 1 && args[0]->type->isQueue()) {
1099 auto queue =
context.convertLvalueExpression(*args[0]);
1103 if (nameId == ksn::Delete) {
1104 if (args.size() == 1) {
1105 moore::QueueClearOp::create(builder, loc, queue);
1108 if (args.size() == 2) {
1109 auto index =
context.convertRvalueExpression(*args[1]);
1110 moore::QueueDeleteOp::create(builder, loc, queue, index);
1113 }
else if (nameId == ksn::Insert && args.size() == 3) {
1114 auto index =
context.convertRvalueExpression(*args[1]);
1115 auto item =
context.convertRvalueExpression(*args[2]);
1117 moore::QueueInsertOp::create(builder, loc, queue, index, item);
1119 }
else if (nameId == ksn::PushBack && args.size() == 2) {
1120 auto item =
context.convertRvalueExpression(*args[1]);
1121 moore::QueuePushBackOp::create(builder, loc, queue, item);
1123 }
else if (nameId == ksn::PushFront && args.size() == 2) {
1124 auto item =
context.convertRvalueExpression(*args[1]);
1125 moore::QueuePushFrontOp::create(builder, loc, queue, item);
1133 if (args.size() >= 1 && args[0]->type->isAssociativeArray()) {
1134 auto assocArray =
context.convertLvalueExpression(*args[0]);
1139 if (nameId == ksn::Delete) {
1140 if (args.size() == 1) {
1141 moore::AssocArrayClearOp::create(builder, loc, assocArray);
1144 if (args.size() == 2) {
1145 auto index =
context.convertRvalueExpression(*args[1]);
1146 moore::AssocArrayDeleteOp::create(builder, loc, assocArray, index);
1153 if (nameId == ksn::MonitorOn || nameId == ksn::MonitorOff) {
1154 context.ensureMonitorGlobals();
1155 bool enable = (nameId == ksn::MonitorOn);
1156 auto enabledRef = moore::GetGlobalVariableOp::create(
1158 auto value = moore::ConstantOp::create(
context.builder, loc,
1160 moore::BlockingAssignOp::create(
context.builder, loc, enabledRef, value);
1165 if (nameId == ksn::Monitor || nameId == ksn::MonitorB ||
1166 nameId == ksn::MonitorO || nameId == ksn::MonitorH) {
1167 context.ensureMonitorGlobals();
1170 unsigned myId =
context.nextMonitorId++;
1173 auto i32Type = moore::IntType::getInt(
context.getContext(), 32);
1175 moore::ConstantOp::create(
context.builder, loc, i32Type, myId);
1176 auto activeRef = moore::GetGlobalVariableOp::create(
1178 moore::BlockingAssignOp::create(
context.builder, loc, activeRef, idConst);
1181 context.pendingMonitors.push_back({myId, loc, &expr});
1192 void createFinishMessage(
const slang::ast::Expression *verbosityExpr) {
1193 unsigned verbosity = 1;
1194 if (verbosityExpr) {
1196 context.evaluateConstant(*verbosityExpr).integer().as<
unsigned>();
1197 assert(value &&
"Slang guarantees constant verbosity parameter");
1202 moore::FinishMessageBIOp::create(builder, loc, verbosity > 1);
1206 LogicalResult visit(
const slang::ast::EventTriggerStatement &stmt) {
1208 mlir::emitError(loc) <<
"unsupported delayed event trigger";
1214 auto target =
context.convertLvalueExpression(stmt.target);
1220 Value inverted = moore::ReadOp::create(builder, loc, target);
1221 inverted = moore::NotOp::create(builder, loc, inverted);
1223 if (stmt.isNonBlocking)
1224 moore::NonBlockingAssignOp::create(builder, loc, target, inverted);
1226 moore::BlockingAssignOp::create(builder, loc, target, inverted);
1231 LogicalResult visit(
const slang::ast::WaitStatement &stmt) {
1232 auto waitOp = moore::WaitLevelOp::create(builder, loc);
1234 OpBuilder::InsertionGuard guard(builder);
1235 builder.setInsertionPointToStart(&waitOp.getBody().emplaceBlock());
1236 auto cond =
context.convertRvalueExpression(stmt.cond);
1239 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
1240 moore::DetectLevelOp::create(builder, loc, cond);
1243 if (failed(
context.convertStatement(stmt.stmt)))
1249 LogicalResult visit(
const slang::ast::WaitForkStatement &stmt) {
1250 moore::WaitForkOp::create(builder, loc);
1255 template <
typename T>
1256 LogicalResult visit(T &&stmt) {
1257 mlir::emitError(loc,
"unsupported statement: ")
1258 << slang::ast::toString(stmt.kind);
1259 return mlir::failure();
1262 LogicalResult visitInvalid(
const slang::ast::Statement &stmt) {
1263 mlir::emitError(loc,
"invalid statement: ")
1264 << slang::ast::toString(stmt.kind);
1265 return mlir::failure();
1270LogicalResult Context::convertStatement(
const slang::ast::Statement &stmt) {
1287 OpBuilder::InsertionGuard guard(
builder);
1291 auto i32Type = moore::IntType::getInt(
getContext(), 32);
1292 auto i1Type = moore::IntType::getInt(
getContext(), 1);
1297 builder, loc,
"__monitor_active_id", i32Type);
1299 OpBuilder::InsertionGuard initGuard(
builder);
1300 builder.setInsertionPointToStart(
1302 auto zero = moore::ConstantOp::create(
builder, loc, i32Type, 0);
1303 moore::YieldOp::create(
builder, loc, zero);
1309 builder, loc,
"__monitor_enabled", i1Type);
1311 OpBuilder::InsertionGuard initGuard(
builder);
1312 builder.setInsertionPointToStart(
1316 moore::YieldOp::create(
builder, loc, trueVal);
1322 using ksn = slang::parsing::KnownSystemName;
1324 auto &call = *pending.call;
1325 auto loc = pending.loc;
1329 std::get<slang::ast::CallExpression::SystemCallInfo>(call.subroutine);
1330 auto nameId = info.subroutine->knownNameId;
1333 auto defaultFormat = moore::IntFormat::Decimal;
1336 defaultFormat = moore::IntFormat::Binary;
1339 defaultFormat = moore::IntFormat::Octal;
1342 defaultFormat = moore::IntFormat::HexLower;
1351 auto alwaysProc = moore::ProcedureOp::create(
1352 builder, loc, moore::ProcedureKind::AlwaysComb);
1353 OpBuilder::InsertionGuard guard(
builder);
1354 builder.setInsertionPointToStart(&alwaysProc.getBody().emplaceBlock());
1359 if (failed(message))
1363 auto i32Type = moore::IntType::getInt(
getContext(), 32);
1364 auto myId = moore::ConstantOp::create(
builder, loc, i32Type, pending.id);
1367 isActive = moore::ReadOp::create(
builder, loc, isActive);
1368 isActive = moore::EqOp::create(
builder, loc, isActive, myId);
1372 enabled = moore::ReadOp::create(
builder, loc, enabled);
1373 enabled = moore::AndOp::create(
builder, loc, isActive, enabled);
1374 enabled = moore::ToBuiltinIntOp::create(
builder, loc, enabled);
1378 auto &printBlock = alwaysProc.getBody().emplaceBlock();
1379 auto &skipBlock = alwaysProc.getBody().emplaceBlock();
1380 cf::CondBranchOp::create(
builder, loc, enabled, &printBlock, &skipBlock);
1384 builder.setInsertionPointToStart(&printBlock);
1386 moore::DisplayBIOp::create(
builder, loc, *message);
1387 moore::ReturnOp::create(
builder, loc);
1390 builder.setInsertionPointToStart(&skipBlock);
1391 moore::ReturnOp::create(
builder, loc);
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static FVInt getZero(unsigned numBits)
Construct an FVInt with all bits set to 0.
This helps visit TypeOp nodes.
@ TwoValued
Two-valued types such as bit or int.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
SmallVector< PendingMonitor > pendingMonitors
Pending $monitor calls that need to be converted at module level.
LogicalResult flushPendingMonitors()
Process any pending $monitor calls and generate the monitoring procedures at module level.
OpBuilder builder
The builder used to create IR operations.
void ensureMonitorGlobals()
Ensure that the global variables for $monitor state exist.
FailureOr< Value > convertFormatString(std::span< const slang::ast::Expression *const > arguments, Location loc, moore::IntFormat defaultFormat=moore::IntFormat::Decimal, bool appendNewline=false)
Convert a list of string literal arguments with formatting specifiers and arguments to be interpolate...
moore::GlobalVariableOp monitorActiveIdGlobal
Global variable ops for $monitor state management.
moore::GlobalVariableOp monitorEnabledGlobal
mlir::ModuleOp intoModuleOp
SymbolTable symbolTable
A symbol table of the MLIR module we are emitting into.
MLIRContext * getContext()
Return the MLIR context.
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.