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 "slang/ast/expressions/MiscExpressions.h"
19#include "slang/ast/symbols/CompilationUnitSymbols.h"
20#include "slang/ast/symbols/InstanceSymbols.h"
21#include "llvm/ADT/ScopeExit.h"
22#include "llvm/Support/raw_ostream.h"
26using namespace ImportVerilog;
32 Context &
context, std::span<const slang::ast::Expression *const> args) {
33 auto timeScale =
context.timeScale;
37 if (
auto *expr = args[0]->as_if<slang::ast::ArbitrarySymbolExpression>()) {
38 const auto *symbol = expr->symbol.get();
39 if (
auto *instance = symbol->as_if<slang::ast::InstanceSymbol>()) {
40 timeScale = instance->body.getTimeScale().value_or(timeScale);
41 target = instance->getHierarchicalPath();
42 }
else if (
auto *unit =
43 symbol->as_if<slang::ast::CompilationUnitSymbol>()) {
44 timeScale = unit->getTimeScale().value_or(timeScale);
46 }
else if (symbol->kind == slang::ast::SymbolKind::Root) {
53 llvm::raw_string_ostream os(out);
56 os <<
" of " << target;
57 os <<
" is " << timeScale.base.toString() <<
" / "
58 << timeScale.precision.toString() <<
"\n";
72 bool isTerminated()
const {
return !builder.getInsertionBlock(); }
73 void setTerminated() { builder.clearInsertionPoint(); }
75 Block &createBlock() {
76 assert(builder.getInsertionBlock());
77 auto block = std::make_unique<Block>();
78 block->insertAfter(builder.getInsertionBlock());
79 return *block.release();
82 LogicalResult recursiveForeach(
const slang::ast::ForeachLoopStatement &stmt,
85 const auto &loopDim = stmt.loopDims[level];
86 if (!loopDim.range.has_value())
87 return mlir::emitError(loc) <<
"dynamic loop variable is unsupported";
88 auto &exitBlock = createBlock();
89 auto &stepBlock = createBlock();
90 auto &bodyBlock = createBlock();
91 auto &checkBlock = createBlock();
94 context.loopStack.push_back({&stepBlock, &exitBlock});
95 llvm::scope_exit done([&] {
context.loopStack.pop_back(); });
97 const auto &iter = loopDim.loopVar;
98 auto type =
context.convertType(*iter->getDeclaredType());
102 Value initial = moore::ConstantOp::create(
103 builder, loc, cast<moore::IntType>(type), loopDim.range->lower());
106 Value varOp = moore::VariableOp::create(
107 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
108 builder.getStringAttr(iter->name), initial);
109 context.valueSymbols.insertIntoScope(
context.valueSymbols.getCurScope(),
112 cf::BranchOp::create(builder, loc, &checkBlock);
113 builder.setInsertionPointToEnd(&checkBlock);
116 auto upperBound = moore::ConstantOp::create(
117 builder, loc, cast<moore::IntType>(type), loopDim.range->upper());
119 auto var = moore::ReadOp::create(builder, loc, varOp);
120 Value cond = moore::SleOp::create(builder, loc, var, upperBound);
123 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
124 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
125 ty && ty.getDomain() == Domain::FourValued) {
126 cond = moore::LogicToIntOp::create(builder, loc, cond);
128 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
129 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
131 builder.setInsertionPointToEnd(&bodyBlock);
135 bool hasNext =
false;
136 for (uint32_t nextLevel = level + 1; nextLevel < stmt.loopDims.size();
138 if (stmt.loopDims[nextLevel].loopVar) {
139 if (failed(recursiveForeach(stmt, nextLevel)))
147 if (failed(
context.convertStatement(stmt.body)))
151 cf::BranchOp::create(builder, loc, &stepBlock);
153 builder.setInsertionPointToEnd(&stepBlock);
156 var = moore::ReadOp::create(builder, loc, varOp);
158 moore::ConstantOp::create(builder, loc, cast<moore::IntType>(type), 1);
159 auto postValue = moore::AddOp::create(builder, loc, var, one).getResult();
160 moore::BlockingAssignOp::create(builder, loc, varOp, postValue);
161 cf::BranchOp::create(builder, loc, &checkBlock);
163 if (exitBlock.hasNoPredecessors()) {
167 builder.setInsertionPointToEnd(&exitBlock);
173 LogicalResult visit(
const slang::ast::EmptyStatement &) {
return success(); }
182 LogicalResult visit(
const slang::ast::StatementList &stmts) {
183 for (
auto *stmt : stmts.list) {
184 if (isTerminated()) {
185 auto loc =
context.convertLocation(stmt->sourceRange);
186 mlir::emitWarning(loc,
"unreachable code");
189 if (failed(
context.convertStatement(*stmt)))
199 LogicalResult visit(
const slang::ast::BlockStatement &stmt) {
200 moore::JoinKind kind;
201 switch (stmt.blockKind) {
202 case slang::ast::StatementBlockKind::Sequential:
204 return context.convertStatement(stmt.body);
205 case slang::ast::StatementBlockKind::JoinAll:
206 kind = moore::JoinKind::Join;
208 case slang::ast::StatementBlockKind::JoinAny:
209 kind = moore::JoinKind::JoinAny;
211 case slang::ast::StatementBlockKind::JoinNone:
212 kind = moore::JoinKind::JoinNone;
219 auto *threadList = stmt.body.as_if<slang::ast::StatementList>();
220 unsigned int threadCount = threadList ? threadList->list.size() : 1;
222 auto forkOp = moore::ForkJoinOp::create(builder, loc, kind, threadCount);
223 OpBuilder::InsertionGuard guard(builder);
228 auto &tBlock = forkOp->getRegion(0).emplaceBlock();
229 builder.setInsertionPointToStart(&tBlock);
230 if (failed(
context.convertStatement(stmt.body)))
232 moore::CompleteOp::create(builder, loc);
237 for (
auto *thread : threadList->list) {
238 auto &tBlock = forkOp->getRegion(i).emplaceBlock();
239 builder.setInsertionPointToStart(&tBlock);
242 if (failed(
context.convertStatement(*thread)))
244 moore::CompleteOp::create(builder, loc);
251 LogicalResult visit(
const slang::ast::ExpressionStatement &stmt) {
253 if (
const auto *call = stmt.expr.as_if<slang::ast::CallExpression>()) {
254 if (
const auto *info =
255 std::get_if<slang::ast::CallExpression::SystemCallInfo>(
256 &call->subroutine)) {
257 auto handled = visitSystemCall(stmt, *call, *info);
265 auto value =
context.convertRvalueExpression(stmt.expr);
271 if (
auto *defOp = value.getDefiningOp())
272 if (isOpTriviallyDead(defOp))
279 LogicalResult visit(
const slang::ast::VariableDeclStatement &stmt) {
280 const auto &var = stmt.symbol;
281 auto type =
context.convertType(*var.getDeclaredType());
286 if (
const auto *init = var.getInitializer()) {
287 initial =
context.convertRvalueExpression(*init, type);
293 auto varOp = moore::VariableOp::create(
294 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
295 builder.getStringAttr(var.name), initial);
296 context.valueSymbols.insertIntoScope(
context.valueSymbols.getCurScope(),
298 const auto &canonTy = var.getType().getCanonicalType();
299 if (
const auto *vi = canonTy.as_if<slang::ast::VirtualInterfaceType>())
300 if (failed(
context.registerVirtualInterfaceMembers(var, *vi, loc)))
306 LogicalResult visit(
const slang::ast::ConditionalStatement &stmt) {
310 for (
const auto &condition : stmt.conditions) {
311 if (condition.pattern)
312 return mlir::emitError(loc,
313 "match patterns in if conditions not supported");
314 auto cond =
context.convertRvalueExpression(*condition.expr);
317 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
319 allConds = moore::AndOp::create(builder, loc, allConds, cond);
323 assert(allConds &&
"slang guarantees at least one condition");
324 if (
auto ty = dyn_cast<moore::IntType>(allConds.getType());
325 ty && ty.getDomain() == Domain::FourValued) {
326 allConds = moore::LogicToIntOp::create(builder, loc, allConds);
328 allConds = moore::ToBuiltinIntOp::create(builder, loc, allConds);
331 Block &exitBlock = createBlock();
332 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
333 Block &trueBlock = createBlock();
334 cf::CondBranchOp::create(builder, loc, allConds, &trueBlock,
335 falseBlock ? falseBlock : &exitBlock);
338 builder.setInsertionPointToEnd(&trueBlock);
339 if (failed(
context.convertStatement(stmt.ifTrue)))
342 cf::BranchOp::create(builder, loc, &exitBlock);
346 builder.setInsertionPointToEnd(falseBlock);
347 if (failed(
context.convertStatement(*stmt.ifFalse)))
350 cf::BranchOp::create(builder, loc, &exitBlock);
355 if (exitBlock.hasNoPredecessors()) {
359 builder.setInsertionPointToEnd(&exitBlock);
365 LogicalResult visit(
const slang::ast::CaseStatement &caseStmt) {
366 using slang::ast::AttributeSymbol;
367 using slang::ast::CaseStatementCondition;
369 caseStmt.expr.as_if<slang::ast::TypeReferenceExpression>()) {
370 if (caseStmt.condition != CaseStatementCondition::Normal)
371 return mlir::emitError(loc,
372 "unsupported type reference case condition");
374 const slang::ast::Statement *matchedStmt =
nullptr;
375 for (
const auto &item : caseStmt.items) {
376 for (
const auto *expr : item.expressions) {
377 auto *itemType = expr->as_if<slang::ast::TypeReferenceExpression>();
379 return mlir::emitError(
380 context.convertLocation(expr->sourceRange),
381 "unsupported non-type item in type reference case statement");
382 if (itemType->targetType.isMatching(caseType->targetType)) {
383 matchedStmt = item.stmt;
392 return context.convertStatement(*matchedStmt);
393 if (caseStmt.defaultCase)
394 return context.convertStatement(*caseStmt.defaultCase);
398 auto caseExpr =
context.convertRvalueExpression(caseStmt.expr);
405 auto &exitBlock = createBlock();
406 Block *lastMatchBlock =
nullptr;
407 SmallVector<moore::FVIntegerAttr> itemConsts;
409 for (
const auto &item : caseStmt.items) {
412 auto &matchBlock = createBlock();
413 lastMatchBlock = &matchBlock;
418 for (
const auto *expr : item.expressions) {
422 if (caseStmt.condition == CaseStatementCondition::Inside) {
424 cond =
context.convertInsideCheck(
425 context.convertToSimpleBitVector(caseExpr), itemLoc, *expr);
429 auto value =
context.convertRvalueExpression(*expr);
432 itemLoc = value.getLoc();
435 auto maybeConst = value;
437 isa_and_nonnull<moore::ConversionOp, moore::IntToLogicOp,
438 moore::LogicToIntOp>(maybeConst.getDefiningOp()))
439 maybeConst = maybeConst.getDefiningOp()->getOperand(0);
440 if (
auto defOp = maybeConst.getDefiningOp<moore::ConstantOp>())
441 itemConsts.push_back(defOp.getValueAttr());
444 switch (caseStmt.condition) {
445 case CaseStatementCondition::Normal:
446 cond = moore::CaseEqOp::create(builder, itemLoc, caseExpr, value);
448 case CaseStatementCondition::WildcardXOrZ:
449 cond = moore::CaseXZEqOp::create(builder, itemLoc, caseExpr, value);
451 case CaseStatementCondition::WildcardJustZ:
452 cond = moore::CaseZEqOp::create(builder, itemLoc, caseExpr, value);
454 case CaseStatementCondition::Inside:
455 llvm_unreachable(
"Inside condition has been handled already");
460 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
461 ty && ty.getDomain() == Domain::FourValued) {
462 cond = moore::LogicToIntOp::create(builder, loc, cond);
464 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
468 auto &nextBlock = createBlock();
469 mlir::cf::CondBranchOp::create(builder, itemLoc, cond, &matchBlock,
471 builder.setInsertionPointToEnd(&nextBlock);
477 matchBlock.moveBefore(builder.getInsertionBlock());
480 OpBuilder::InsertionGuard guard(builder);
481 builder.setInsertionPointToEnd(&matchBlock);
482 if (failed(
context.convertStatement(*item.stmt)))
484 if (!isTerminated()) {
485 auto loc =
context.convertLocation(item.stmt->sourceRange);
486 mlir::cf::BranchOp::create(builder, loc, &exitBlock);
490 const auto caseStmtAttrs =
context.compilation.getAttributes(caseStmt);
491 const bool hasFullCaseAttr =
492 llvm::find_if(caseStmtAttrs, [](
const AttributeSymbol *attr) {
493 return attr->name ==
"full_case";
494 }) != caseStmtAttrs.end();
505 auto twoStateExhaustive =
false;
506 if (
auto intType = dyn_cast<moore::IntType>(caseExpr.getType());
507 intType && intType.getWidth() < 32 &&
508 itemConsts.size() == (1 << intType.getWidth())) {
510 llvm::sort(itemConsts, [](
auto a,
auto b) {
511 return a.getValue().getRawValue().ult(b.getValue().getRawValue());
519 for (
auto value : itemConsts) {
520 if (value.getValue() != nextValue)
524 twoStateExhaustive = nextValue.isZero();
535 if ((twoStateExhaustive || (hasFullCaseAttr && !caseStmt.defaultCase)) &&
537 caseStmt.condition == CaseStatementCondition::Normal) {
538 mlir::cf::BranchOp::create(builder, loc, lastMatchBlock);
541 if (caseStmt.defaultCase)
542 if (failed(
context.convertStatement(*caseStmt.defaultCase)))
545 mlir::cf::BranchOp::create(builder, loc, &exitBlock);
550 if (exitBlock.hasNoPredecessors()) {
554 builder.setInsertionPointToEnd(&exitBlock);
560 LogicalResult visit(
const slang::ast::ForLoopStatement &stmt) {
562 for (
auto *initExpr : stmt.initializers)
563 if (!
context.convertRvalueExpression(*initExpr))
567 auto &exitBlock = createBlock();
568 auto &stepBlock = createBlock();
569 auto &bodyBlock = createBlock();
570 auto &checkBlock = createBlock();
571 cf::BranchOp::create(builder, loc, &checkBlock);
574 context.loopStack.push_back({&stepBlock, &exitBlock});
575 llvm::scope_exit done([&] {
context.loopStack.pop_back(); });
578 builder.setInsertionPointToEnd(&checkBlock);
579 auto cond =
context.convertRvalueExpression(*stmt.stopExpr);
582 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
583 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
584 ty && ty.getDomain() == Domain::FourValued) {
585 cond = moore::LogicToIntOp::create(builder, loc, cond);
587 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
588 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
591 builder.setInsertionPointToEnd(&bodyBlock);
592 if (failed(
context.convertStatement(stmt.body)))
595 cf::BranchOp::create(builder, loc, &stepBlock);
598 builder.setInsertionPointToEnd(&stepBlock);
599 for (
auto *stepExpr : stmt.steps)
600 if (!
context.convertRvalueExpression(*stepExpr))
603 cf::BranchOp::create(builder, loc, &checkBlock);
607 if (exitBlock.hasNoPredecessors()) {
611 builder.setInsertionPointToEnd(&exitBlock);
616 LogicalResult visit(
const slang::ast::ForeachLoopStatement &stmt) {
617 for (uint32_t level = 0; level < stmt.loopDims.size(); level++) {
618 if (stmt.loopDims[level].loopVar)
619 return recursiveForeach(stmt, level);
625 LogicalResult visit(
const slang::ast::RepeatLoopStatement &stmt) {
626 auto intType = moore::IntType::getInt(
context.getContext(), 32);
627 auto count =
context.convertRvalueExpression(stmt.count, intType);
632 auto &exitBlock = createBlock();
633 auto &stepBlock = createBlock();
634 auto &bodyBlock = createBlock();
635 auto &checkBlock = createBlock();
636 auto currentCount = checkBlock.addArgument(count.getType(), count.getLoc());
637 cf::BranchOp::create(builder, loc, &checkBlock, count);
640 context.loopStack.push_back({&stepBlock, &exitBlock});
641 llvm::scope_exit done([&] {
context.loopStack.pop_back(); });
644 builder.setInsertionPointToEnd(&checkBlock);
645 auto cond = builder.createOrFold<moore::BoolCastOp>(loc, currentCount);
646 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
647 ty && ty.getDomain() == Domain::FourValued) {
648 cond = moore::LogicToIntOp::create(builder, loc, cond);
650 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
651 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
654 builder.setInsertionPointToEnd(&bodyBlock);
655 if (failed(
context.convertStatement(stmt.body)))
658 cf::BranchOp::create(builder, loc, &stepBlock);
661 builder.setInsertionPointToEnd(&stepBlock);
662 auto one = moore::ConstantOp::create(
663 builder, count.getLoc(), cast<moore::IntType>(count.getType()), 1);
665 moore::SubOp::create(builder, count.getLoc(), currentCount, one);
666 cf::BranchOp::create(builder, loc, &checkBlock, nextCount);
670 if (exitBlock.hasNoPredecessors()) {
674 builder.setInsertionPointToEnd(&exitBlock);
680 LogicalResult createWhileLoop(
const slang::ast::Expression &condExpr,
681 const slang::ast::Statement &bodyStmt,
684 auto &exitBlock = createBlock();
685 auto &bodyBlock = createBlock();
686 auto &checkBlock = createBlock();
687 cf::BranchOp::create(builder, loc, atLeastOnce ? &bodyBlock : &checkBlock);
689 bodyBlock.moveBefore(&checkBlock);
692 context.loopStack.push_back({&checkBlock, &exitBlock});
693 llvm::scope_exit done([&] {
context.loopStack.pop_back(); });
696 builder.setInsertionPointToEnd(&checkBlock);
697 auto cond =
context.convertRvalueExpression(condExpr);
700 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
701 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
702 ty && ty.getDomain() == Domain::FourValued) {
703 cond = moore::LogicToIntOp::create(builder, loc, cond);
705 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
706 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
709 builder.setInsertionPointToEnd(&bodyBlock);
710 if (failed(
context.convertStatement(bodyStmt)))
713 cf::BranchOp::create(builder, loc, &checkBlock);
717 if (exitBlock.hasNoPredecessors()) {
721 builder.setInsertionPointToEnd(&exitBlock);
726 LogicalResult visit(
const slang::ast::WhileLoopStatement &stmt) {
727 return createWhileLoop(stmt.cond, stmt.body,
false);
730 LogicalResult visit(
const slang::ast::DoWhileLoopStatement &stmt) {
731 return createWhileLoop(stmt.cond, stmt.body,
true);
735 LogicalResult visit(
const slang::ast::ForeverLoopStatement &stmt) {
737 auto &exitBlock = createBlock();
738 auto &bodyBlock = createBlock();
739 cf::BranchOp::create(builder, loc, &bodyBlock);
742 context.loopStack.push_back({&bodyBlock, &exitBlock});
743 llvm::scope_exit done([&] {
context.loopStack.pop_back(); });
746 builder.setInsertionPointToEnd(&bodyBlock);
747 if (failed(
context.convertStatement(stmt.body)))
750 cf::BranchOp::create(builder, loc, &bodyBlock);
754 if (exitBlock.hasNoPredecessors()) {
758 builder.setInsertionPointToEnd(&exitBlock);
764 LogicalResult visit(
const slang::ast::TimedStatement &stmt) {
765 return context.convertTimingControl(stmt.timing, stmt.stmt);
769 LogicalResult visit(
const slang::ast::ReturnStatement &stmt) {
770 Operation *parentOp = builder.getInsertionBlock()
771 ? builder.getInsertionBlock()->getParentOp()
774 return mlir::emitError(loc) <<
"return statement is not within an op";
776 if (isa<moore::CoroutineOp, moore::ProcedureOp>(parentOp)) {
778 return mlir::emitError(loc)
779 <<
"unsupported `return <expr>` in a procedure or task";
780 moore::ReturnOp::create(builder, loc);
785 if (!isa<mlir::func::FuncOp>(parentOp))
786 return mlir::emitError(loc) <<
"unsupported return statement context";
789 auto expr =
context.convertRvalueExpression(*stmt.expr);
792 mlir::func::ReturnOp::create(builder, loc, expr);
794 mlir::func::ReturnOp::create(builder, loc);
801 LogicalResult visit(
const slang::ast::ContinueStatement &stmt) {
803 return mlir::emitError(loc,
804 "cannot `continue` without a surrounding loop");
805 cf::BranchOp::create(builder, loc,
context.loopStack.back().continueBlock);
811 LogicalResult visit(
const slang::ast::BreakStatement &stmt) {
813 return mlir::emitError(loc,
"cannot `break` without a surrounding loop");
814 cf::BranchOp::create(builder, loc,
context.loopStack.back().breakBlock);
820 LogicalResult visit(
const slang::ast::ImmediateAssertionStatement &stmt) {
821 auto cond =
context.convertRvalueExpression(stmt.cond);
822 cond =
context.convertToBool(cond);
827 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
828 auto defer = moore::DeferAssert::Immediate;
830 defer = moore::DeferAssert::Final;
831 else if (stmt.isDeferred)
832 defer = moore::DeferAssert::Observed;
834 switch (stmt.assertionKind) {
835 case slang::ast::AssertionKind::Assert:
836 moore::AssertOp::create(builder, loc, defer, cond, StringAttr{});
838 case slang::ast::AssertionKind::Assume:
839 moore::AssumeOp::create(builder, loc, defer, cond, StringAttr{});
841 case slang::ast::AssertionKind::CoverProperty:
842 moore::CoverOp::create(builder, loc, defer, cond, StringAttr{});
847 mlir::emitError(loc) <<
"unsupported immediate assertion kind: "
848 << slang::ast::toString(stmt.assertionKind);
853 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
854 ty && ty.getDomain() == Domain::FourValued) {
855 cond = moore::LogicToIntOp::create(builder, loc, cond);
857 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
860 Block &exitBlock = createBlock();
861 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
862 Block &trueBlock = createBlock();
863 cf::CondBranchOp::create(builder, loc, cond, &trueBlock,
864 falseBlock ? falseBlock : &exitBlock);
867 builder.setInsertionPointToEnd(&trueBlock);
868 if (stmt.ifTrue && failed(
context.convertStatement(*stmt.ifTrue)))
871 cf::BranchOp::create(builder, loc, &exitBlock);
875 builder.setInsertionPointToEnd(falseBlock);
876 if (failed(
context.convertStatement(*stmt.ifFalse)))
879 cf::BranchOp::create(builder, loc, &exitBlock);
884 if (exitBlock.hasNoPredecessors()) {
888 builder.setInsertionPointToEnd(&exitBlock);
894 LogicalResult visit(
const slang::ast::ConcurrentAssertionStatement &stmt) {
895 auto loc =
context.convertLocation(stmt.sourceRange);
906 const slang::ast::AssertionExpr *propertySpec;
907 const slang::ast::ClockingAssertionExpr *clocking =
908 stmt.propertySpec.as_if<slang::ast::ClockingAssertionExpr>();
910 propertySpec = &(clocking->expr);
912 propertySpec = &(stmt.propertySpec);
914 if (
auto *disableIff =
915 propertySpec->as_if<slang::ast::DisableIffAssertionExpr>()) {
918 auto disableCond =
context.convertRvalueExpression(disableIff->condition);
919 auto enableCond = moore::NotOp::create(builder, loc, disableCond);
921 enable =
context.convertToI1(enableCond);
925 auto clockingExpr = slang::ast::ClockingAssertionExpr(
926 clocking->clocking, disableIff->expr);
927 property =
context.convertAssertionExpression(clockingExpr, loc);
929 property =
context.convertAssertionExpression(disableIff->expr, loc);
932 property =
context.convertAssertionExpression(stmt.propertySpec, loc);
939 if (!stmt.ifTrue || stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
940 switch (stmt.assertionKind) {
941 case slang::ast::AssertionKind::Assert:
942 verif::AssertOp::create(builder, loc, property, enable, StringAttr{});
944 case slang::ast::AssertionKind::Assume:
945 verif::AssumeOp::create(builder, loc, property, enable, StringAttr{});
950 mlir::emitError(loc) <<
"unsupported concurrent assertion kind: "
951 << slang::ast::toString(stmt.assertionKind);
956 <<
"concurrent assertion statements with action blocks "
957 "are not supported yet";
971 getDisplayMessage(std::span<const slang::ast::Expression *const> args) {
972 if (args.size() == 0)
981 if (args[0]->as_if<slang::ast::StringLiteral>()) {
982 return context.convertFormatString(args, loc);
985 if (args.size() == 1) {
986 return context.convertRvalueExpression(
987 *args[0], builder.getType<moore::FormatStringType>());
990 return emitError(loc) <<
"Failed to convert Display Message!";
997 visitSystemCall(
const slang::ast::ExpressionStatement &stmt,
998 const slang::ast::CallExpression &expr,
999 const slang::ast::CallExpression::SystemCallInfo &info) {
1000 using ksn = slang::parsing::KnownSystemName;
1001 const auto &subroutine = *
info.subroutine;
1002 auto nameId = subroutine.knownNameId;
1003 auto args = expr.arguments();
1008 if (nameId == ksn::Cast) {
1014 if (nameId == ksn::Stop) {
1015 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
1016 moore::StopBIOp::create(builder, loc);
1020 if (nameId == ksn::Finish) {
1021 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
1022 moore::FinishBIOp::create(builder, loc, 0);
1023 moore::UnreachableOp::create(builder, loc);
1028 if (nameId == ksn::Exit) {
1037 if (nameId == ksn::PrintTimeScale) {
1038 auto message = moore::FormatLiteralOp::create(
1040 moore::DisplayBIOp::create(builder, loc, message);
1047 using moore::IntFormat;
1048 bool isDisplay =
false;
1049 bool isFDisplay =
false;
1050 bool isSWrite =
false;
1051 bool isSFormat =
false;
1052 bool appendNewline =
false;
1053 IntFormat defaultFormat = IntFormat::Decimal;
1057 appendNewline =
true;
1061 appendNewline =
true;
1062 defaultFormat = IntFormat::Binary;
1066 appendNewline =
true;
1067 defaultFormat = IntFormat::Octal;
1071 appendNewline =
true;
1072 defaultFormat = IntFormat::HexLower;
1079 defaultFormat = IntFormat::Binary;
1083 defaultFormat = IntFormat::Octal;
1087 defaultFormat = IntFormat::HexLower;
1091 appendNewline =
true;
1093 case ksn::FDisplayB:
1095 appendNewline =
true;
1096 defaultFormat = IntFormat::Binary;
1098 case ksn::FDisplayO:
1100 appendNewline =
true;
1101 defaultFormat = IntFormat::Octal;
1103 case ksn::FDisplayH:
1105 appendNewline =
true;
1106 defaultFormat = IntFormat::HexLower;
1113 defaultFormat = IntFormat::Binary;
1117 defaultFormat = IntFormat::Octal;
1121 defaultFormat = IntFormat::HexLower;
1131 defaultFormat = IntFormat::Binary;
1135 defaultFormat = IntFormat::Octal;
1139 defaultFormat = IntFormat::HexLower;
1147 context.convertFormatString(args, loc, defaultFormat, appendNewline);
1148 if (failed(message))
1150 if (*message == Value{})
1152 moore::DisplayBIOp::create(builder, loc, *message);
1157 assert(!args.empty() &&
"$fdisplay/$fwrite takes at least 1 argument");
1159 auto fd =
context.convertRvalueExpression(
1160 *args[0], moore::IntType::getInt(builder.getContext(), 32));
1163 args = args.subspan(1);
1166 context.convertFormatString(args, loc, defaultFormat, appendNewline);
1167 if (failed(message))
1169 if (*message == Value{})
1171 moore::FDisplayBIOp::create(builder, loc, fd, *message);
1182 if (isSWrite || isSFormat) {
1183 if (isSFormat && args.size() < 2)
1184 return emitError(loc) <<
"$sformat requires at least 2 arguments";
1185 if (isSWrite && args.size() < 1)
1186 return emitError(loc) <<
"$swrite requires at least 1 argument";
1189 context.convertFormatString(args.subspan(1), loc, defaultFormat,
1191 if (failed(fmtValue))
1193 if (*fmtValue == Value{})
1196 moore::FormatStringToStringOp::create(builder, loc, *fmtValue);
1197 auto *lhsExpr = args[0];
1198 if (
auto *assignExpr =
1199 lhsExpr->as_if<slang::ast::AssignmentExpression>()) {
1200 auto lhs =
context.convertLvalueExpression(assignExpr->left());
1203 auto convertedValue =
context.materializeConversion(
1204 cast<moore::RefType>(lhs.getType()).getNestedType(), strValue,
1206 moore::BlockingAssignOp::create(builder, loc, lhs, convertedValue);
1213 using moore::Severity;
1214 std::optional<Severity> severity;
1215 if (nameId == ksn::Info)
1216 severity = Severity::Info;
1217 else if (nameId == ksn::Warning)
1218 severity = Severity::Warning;
1219 else if (nameId == ksn::Error)
1220 severity = Severity::Error;
1221 else if (nameId == ksn::Fatal)
1222 severity = Severity::Fatal;
1226 const slang::ast::Expression *verbosityExpr =
nullptr;
1227 if (severity == Severity::Fatal && args.size() >= 1) {
1228 verbosityExpr = args[0];
1229 args = args.subspan(1);
1232 FailureOr<Value> maybeMessage = getDisplayMessage(args);
1233 if (failed(maybeMessage))
1235 auto message = maybeMessage.value();
1237 if (message == Value{})
1238 message = moore::FormatLiteralOp::create(builder, loc,
"");
1239 moore::SeverityBIOp::create(builder, loc, *severity, message);
1242 if (severity == Severity::Fatal) {
1243 createFinishMessage(verbosityExpr);
1244 moore::FinishBIOp::create(builder, loc, 1);
1245 moore::UnreachableOp::create(builder, loc);
1253 if (nameId == ksn::FClose) {
1254 assert(args.size() == 1 &&
"$fclose takes 1 argument");
1255 auto fd =
context.convertRvalueExpression(
1256 *args[0], moore::IntType::getInt(builder.getContext(), 32));
1259 moore::FCloseBIOp::create(builder, loc, fd);
1263 if (nameId == ksn::FFlush) {
1264 assert(args.size() <= 1 &&
"$fflush takes at most 1 argument");
1266 if (args.size() == 1) {
1267 fd =
context.convertRvalueExpression(
1268 *args[0], moore::IntType::getInt(builder.getContext(), 32));
1272 moore::FFlushBIOp::create(builder, loc, fd);
1277 if (args.size() >= 1 && args[0]->type->isString()) {
1278 auto str =
context.convertLvalueExpression(*args[0]);
1280 if (nameId == ksn::Putc) {
1282 assert(args.size() == 3 &&
"`putc` takes 3 arguments");
1283 auto index =
context.convertRvalueExpression(*args[1]);
1284 auto character =
context.convertRvalueExpression(*args[2]);
1285 moore::StringPutOp::create(builder, loc, str, index, character);
1289 if (nameId == ksn::IToA || nameId == ksn::HexToA ||
1290 nameId == ksn::OctToA || nameId == ksn::BinToA) {
1292 assert(args.size() == 2 &&
"`itoa/hex/oct/bin` takes 2 arguments");
1293 auto integerType = moore::IntType::getLogic(builder.getContext(), 32);
1294 auto input =
context.convertRvalueExpression(*args[1], integerType);
1298 moore::StringItoaOp::create(builder, loc, str, input);
1301 moore::StringHextoaOp::create(builder, loc, str, input);
1304 moore::StringOcttoaOp::create(builder, loc, str, input);
1307 moore::StringBintoaOp::create(builder, loc, str, input);
1310 llvm_unreachable(
"unexpected ASCII integer to string conversion");
1316 if (nameId == ksn::RealToA) {
1318 assert(args.size() == 2 &&
"`realtoa` takes 2 arguments");
1321 auto input =
context.convertRvalueExpression(*args[1], realType);
1322 moore::StringRealtoaOp::create(builder, loc, str, input);
1329 if (args.size() >= 1 && args[0]->type->isQueue()) {
1330 auto queue =
context.convertLvalueExpression(*args[0]);
1334 if (nameId == ksn::Delete) {
1335 if (args.size() == 1) {
1336 moore::QueueClearOp::create(builder, loc, queue);
1339 if (args.size() == 2) {
1340 auto index =
context.convertRvalueExpression(*args[1]);
1341 moore::QueueDeleteOp::create(builder, loc, queue, index);
1344 }
else if (nameId == ksn::Insert && args.size() == 3) {
1345 auto index =
context.convertRvalueExpression(*args[1]);
1346 auto item =
context.convertRvalueExpression(*args[2]);
1348 moore::QueueInsertOp::create(builder, loc, queue, index, item);
1350 }
else if (nameId == ksn::PushBack && args.size() == 2) {
1351 auto item =
context.convertRvalueExpression(*args[1]);
1352 moore::QueuePushBackOp::create(builder, loc, queue, item);
1354 }
else if (nameId == ksn::PushFront && args.size() == 2) {
1355 auto item =
context.convertRvalueExpression(*args[1]);
1356 moore::QueuePushFrontOp::create(builder, loc, queue, item);
1364 if (args.size() >= 1 && args[0]->type->isAssociativeArray()) {
1365 auto assocArray =
context.convertLvalueExpression(*args[0]);
1370 if (nameId == ksn::Delete) {
1371 if (args.size() == 1) {
1372 moore::AssocArrayClearOp::create(builder, loc, assocArray);
1375 if (args.size() == 2) {
1376 auto index =
context.convertRvalueExpression(*args[1]);
1377 moore::AssocArrayDeleteOp::create(builder, loc, assocArray, index);
1384 if (nameId == ksn::MonitorOn || nameId == ksn::MonitorOff) {
1385 context.ensureMonitorGlobals();
1386 bool enable = (nameId == ksn::MonitorOn);
1387 auto enabledRef = moore::GetGlobalVariableOp::create(
1389 auto value = moore::ConstantOp::create(
context.builder, loc,
1391 moore::BlockingAssignOp::create(
context.builder, loc, enabledRef, value);
1396 if (nameId == ksn::Monitor || nameId == ksn::MonitorB ||
1397 nameId == ksn::MonitorO || nameId == ksn::MonitorH) {
1398 context.ensureMonitorGlobals();
1401 unsigned myId =
context.nextMonitorId++;
1404 auto i32Type = moore::IntType::getInt(
context.getContext(), 32);
1406 moore::ConstantOp::create(
context.builder, loc, i32Type, myId);
1407 auto activeRef = moore::GetGlobalVariableOp::create(
1409 moore::BlockingAssignOp::create(
context.builder, loc, activeRef, idConst);
1412 context.pendingMonitors.push_back({myId, loc, &expr});
1423 void createFinishMessage(
const slang::ast::Expression *verbosityExpr) {
1424 unsigned verbosity = 1;
1425 if (verbosityExpr) {
1427 context.evaluateConstant(*verbosityExpr).integer().as<
unsigned>();
1428 assert(value &&
"Slang guarantees constant verbosity parameter");
1433 moore::FinishMessageBIOp::create(builder, loc, verbosity > 1);
1437 LogicalResult visit(
const slang::ast::EventTriggerStatement &stmt) {
1439 mlir::emitError(loc) <<
"unsupported delayed event trigger";
1445 auto target =
context.convertLvalueExpression(stmt.target);
1451 Value inverted = moore::ReadOp::create(builder, loc, target);
1452 inverted = moore::NotOp::create(builder, loc, inverted);
1454 if (stmt.isNonBlocking)
1455 moore::NonBlockingAssignOp::create(builder, loc, target, inverted);
1457 moore::BlockingAssignOp::create(builder, loc, target, inverted);
1462 LogicalResult visit(
const slang::ast::WaitStatement &stmt) {
1463 auto waitOp = moore::WaitLevelOp::create(builder, loc);
1465 OpBuilder::InsertionGuard guard(builder);
1466 builder.setInsertionPointToStart(&waitOp.getBody().emplaceBlock());
1467 auto cond =
context.convertRvalueExpression(stmt.cond);
1470 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
1471 moore::DetectLevelOp::create(builder, loc, cond);
1474 if (failed(
context.convertStatement(stmt.stmt)))
1480 LogicalResult visit(
const slang::ast::WaitForkStatement &stmt) {
1481 moore::WaitForkOp::create(builder, loc);
1486 template <
typename T>
1487 LogicalResult visit(T &&stmt) {
1488 mlir::emitError(loc,
"unsupported statement: ")
1489 << slang::ast::toString(stmt.kind);
1490 return mlir::failure();
1493 LogicalResult visitInvalid(
const slang::ast::Statement &stmt) {
1494 mlir::emitError(loc,
"invalid statement: ")
1495 << slang::ast::toString(stmt.kind);
1496 return mlir::failure();
1501LogicalResult Context::convertStatement(
const slang::ast::Statement &stmt) {
1518 OpBuilder::InsertionGuard guard(
builder);
1522 auto i32Type = moore::IntType::getInt(
getContext(), 32);
1523 auto i1Type = moore::IntType::getInt(
getContext(), 1);
1528 builder, loc,
"__monitor_active_id", i32Type);
1530 OpBuilder::InsertionGuard initGuard(
builder);
1531 builder.setInsertionPointToStart(
1533 auto zero = moore::ConstantOp::create(
builder, loc, i32Type, 0);
1534 moore::YieldOp::create(
builder, loc, zero);
1540 builder, loc,
"__monitor_enabled", i1Type);
1542 OpBuilder::InsertionGuard initGuard(
builder);
1543 builder.setInsertionPointToStart(
1547 moore::YieldOp::create(
builder, loc, trueVal);
1553 using ksn = slang::parsing::KnownSystemName;
1555 auto &call = *pending.call;
1556 auto loc = pending.loc;
1560 std::get<slang::ast::CallExpression::SystemCallInfo>(call.subroutine);
1561 auto nameId = info.subroutine->knownNameId;
1564 auto defaultFormat = moore::IntFormat::Decimal;
1567 defaultFormat = moore::IntFormat::Binary;
1570 defaultFormat = moore::IntFormat::Octal;
1573 defaultFormat = moore::IntFormat::HexLower;
1582 auto alwaysProc = moore::ProcedureOp::create(
1583 builder, loc, moore::ProcedureKind::AlwaysComb);
1584 OpBuilder::InsertionGuard guard(
builder);
1585 builder.setInsertionPointToStart(&alwaysProc.getBody().emplaceBlock());
1590 if (failed(message))
1594 auto i32Type = moore::IntType::getInt(
getContext(), 32);
1595 auto myId = moore::ConstantOp::create(
builder, loc, i32Type, pending.id);
1598 isActive = moore::ReadOp::create(
builder, loc, isActive);
1599 isActive = moore::EqOp::create(
builder, loc, isActive, myId);
1603 enabled = moore::ReadOp::create(
builder, loc, enabled);
1604 enabled = moore::AndOp::create(
builder, loc, isActive, enabled);
1605 enabled = moore::ToBuiltinIntOp::create(
builder, loc, enabled);
1609 auto &printBlock = alwaysProc.getBody().emplaceBlock();
1610 auto &skipBlock = alwaysProc.getBody().emplaceBlock();
1611 cf::CondBranchOp::create(
builder, loc, enabled, &printBlock, &skipBlock);
1615 builder.setInsertionPointToStart(&printBlock);
1617 moore::DisplayBIOp::create(
builder, loc, *message);
1618 moore::ReturnOp::create(
builder, loc);
1621 builder.setInsertionPointToStart(&skipBlock);
1622 moore::ReturnOp::create(
builder, loc);
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static std::string buildPrintTimeScaleMessage(Context &context, std::span< const slang::ast::Expression *const > args)
Build the message printed by the $printtimescale system task.
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.
@ f64
A 64-bit double-precision floation point number ("double")
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.