10#include "slang/ast/Compilation.h"
11#include "slang/ast/SystemSubroutine.h"
12#include "llvm/ADT/ScopeExit.h"
16using namespace ImportVerilog;
28 bool isTerminated()
const {
return !builder.getInsertionBlock(); }
29 void setTerminated() { builder.clearInsertionPoint(); }
31 Block &createBlock() {
32 assert(builder.getInsertionBlock());
33 auto block = std::make_unique<Block>();
34 block->insertAfter(builder.getInsertionBlock());
35 return *block.release();
38 LogicalResult recursiveForeach(
const slang::ast::ForeachLoopStatement &stmt,
41 const auto &loopDim = stmt.loopDims[level];
42 if (!loopDim.range.has_value())
43 return mlir::emitError(loc) <<
"dynamic loop variable is unsupported";
44 auto &exitBlock = createBlock();
45 auto &stepBlock = createBlock();
46 auto &bodyBlock = createBlock();
47 auto &checkBlock = createBlock();
50 context.loopStack.push_back({&stepBlock, &exitBlock});
51 auto done = llvm::make_scope_exit([&] {
context.loopStack.pop_back(); });
53 const auto &iter = loopDim.loopVar;
54 auto type =
context.convertType(*iter->getDeclaredType());
58 Value initial = moore::ConstantOp::create(
59 builder, loc, cast<moore::IntType>(type), loopDim.range->lower());
62 Value varOp = moore::VariableOp::create(
63 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
64 builder.getStringAttr(iter->name), initial);
65 context.valueSymbols.insertIntoScope(
context.valueSymbols.getCurScope(),
68 cf::BranchOp::create(builder, loc, &checkBlock);
69 builder.setInsertionPointToEnd(&checkBlock);
72 auto upperBound = moore::ConstantOp::create(
73 builder, loc, cast<moore::IntType>(type), loopDim.range->upper());
75 auto var = moore::ReadOp::create(builder, loc, varOp);
76 Value cond = moore::SleOp::create(builder, loc, var, upperBound);
79 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
80 cond = moore::ToBuiltinBoolOp::create(builder, loc, cond);
81 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
83 builder.setInsertionPointToEnd(&bodyBlock);
88 for (uint32_t nextLevel = level + 1; nextLevel < stmt.loopDims.size();
90 if (stmt.loopDims[nextLevel].loopVar) {
91 if (failed(recursiveForeach(stmt, nextLevel)))
99 if (failed(
context.convertStatement(stmt.body)))
103 cf::BranchOp::create(builder, loc, &stepBlock);
105 builder.setInsertionPointToEnd(&stepBlock);
108 var = moore::ReadOp::create(builder, loc, varOp);
110 moore::ConstantOp::create(builder, loc, cast<moore::IntType>(type), 1);
111 auto postValue = moore::AddOp::create(builder, loc, var, one).getResult();
112 moore::BlockingAssignOp::create(builder, loc, varOp, postValue);
113 cf::BranchOp::create(builder, loc, &checkBlock);
115 if (exitBlock.hasNoPredecessors()) {
119 builder.setInsertionPointToEnd(&exitBlock);
125 LogicalResult visit(
const slang::ast::EmptyStatement &) {
return success(); }
134 LogicalResult visit(
const slang::ast::StatementList &stmts) {
135 for (
auto *stmt : stmts.list) {
136 if (isTerminated()) {
137 auto loc =
context.convertLocation(stmt->sourceRange);
138 mlir::emitWarning(loc,
"unreachable code");
141 if (failed(
context.convertStatement(*stmt)))
148 LogicalResult visit(
const slang::ast::BlockStatement &stmt) {
149 return context.convertStatement(stmt.body);
153 LogicalResult visit(
const slang::ast::ExpressionStatement &stmt) {
155 if (
const auto *call = stmt.expr.as_if<slang::ast::CallExpression>()) {
156 if (
const auto *info =
157 std::get_if<slang::ast::CallExpression::SystemCallInfo>(
158 &call->subroutine)) {
159 auto handled = visitSystemCall(stmt, *call, *info);
173 if (!call->getSubroutineName().compare(
"$sformat")) {
176 auto *lhsExpr = call->arguments().front();
179 context.convertFormatString(call->arguments().subspan(1), loc,
180 moore::IntFormat::Decimal,
false);
181 if (failed(fmtValue))
184 auto strValue = moore::FormatStringToStringOp::create(builder, loc,
190 if (
auto assignExpr =
191 lhsExpr->as_if<slang::ast::AssignmentExpression>()) {
192 auto lhs =
context.convertLvalueExpression(assignExpr->left());
196 auto convertedValue =
context.materializeConversion(
197 cast<moore::RefType>(lhs.getType()).getNestedType(), strValue,
199 moore::BlockingAssignOp::create(builder, loc, lhs, convertedValue);
207 auto value =
context.convertRvalueExpression(stmt.expr);
213 if (
auto *defOp = value.getDefiningOp())
214 if (isOpTriviallyDead(defOp))
221 LogicalResult visit(
const slang::ast::VariableDeclStatement &stmt) {
222 const auto &var = stmt.symbol;
223 auto type =
context.convertType(*var.getDeclaredType());
228 if (
const auto *init = var.getInitializer()) {
229 initial =
context.convertRvalueExpression(*init, type);
235 auto varOp = moore::VariableOp::create(
236 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
237 builder.getStringAttr(var.name), initial);
238 context.valueSymbols.insertIntoScope(
context.valueSymbols.getCurScope(),
244 LogicalResult visit(
const slang::ast::ConditionalStatement &stmt) {
248 for (
const auto &condition : stmt.conditions) {
249 if (condition.pattern)
250 return mlir::emitError(loc,
251 "match patterns in if conditions not supported");
252 auto cond =
context.convertRvalueExpression(*condition.expr);
255 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
257 allConds = moore::AndOp::create(builder, loc, allConds, cond);
261 assert(allConds &&
"slang guarantees at least one condition");
262 allConds = moore::ToBuiltinBoolOp::create(builder, loc, allConds);
265 Block &exitBlock = createBlock();
266 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
267 Block &trueBlock = createBlock();
268 cf::CondBranchOp::create(builder, loc, allConds, &trueBlock,
269 falseBlock ? falseBlock : &exitBlock);
272 builder.setInsertionPointToEnd(&trueBlock);
273 if (failed(
context.convertStatement(stmt.ifTrue)))
276 cf::BranchOp::create(builder, loc, &exitBlock);
280 builder.setInsertionPointToEnd(falseBlock);
281 if (failed(
context.convertStatement(*stmt.ifFalse)))
284 cf::BranchOp::create(builder, loc, &exitBlock);
289 if (exitBlock.hasNoPredecessors()) {
293 builder.setInsertionPointToEnd(&exitBlock);
299 LogicalResult visit(
const slang::ast::CaseStatement &caseStmt) {
300 using slang::ast::AttributeSymbol;
301 using slang::ast::CaseStatementCondition;
302 auto caseExpr =
context.convertRvalueExpression(caseStmt.expr);
309 auto &exitBlock = createBlock();
310 Block *lastMatchBlock =
nullptr;
311 SmallVector<moore::FVIntegerAttr> itemConsts;
313 for (
const auto &item : caseStmt.items) {
316 auto &matchBlock = createBlock();
317 lastMatchBlock = &matchBlock;
322 for (
const auto *expr : item.expressions) {
323 auto value =
context.convertRvalueExpression(*expr);
326 auto itemLoc = value.getLoc();
329 auto maybeConst = value;
330 while (isa_and_nonnull<moore::ConversionOp, moore::IntToLogicOp,
331 moore::LogicToIntOp>(maybeConst.getDefiningOp()))
332 maybeConst = maybeConst.getDefiningOp()->getOperand(0);
333 if (
auto defOp = maybeConst.getDefiningOp<moore::ConstantOp>())
334 itemConsts.push_back(defOp.getValueAttr());
338 switch (caseStmt.condition) {
339 case CaseStatementCondition::Normal:
340 cond = moore::CaseEqOp::create(builder, itemLoc, caseExpr, value);
342 case CaseStatementCondition::WildcardXOrZ:
343 cond = moore::CaseXZEqOp::create(builder, itemLoc, caseExpr, value);
345 case CaseStatementCondition::WildcardJustZ:
346 cond = moore::CaseZEqOp::create(builder, itemLoc, caseExpr, value);
348 case CaseStatementCondition::Inside:
349 mlir::emitError(loc,
"unsupported set membership case statement");
352 cond = moore::ToBuiltinBoolOp::create(builder, itemLoc, cond);
356 auto &nextBlock = createBlock();
357 mlir::cf::CondBranchOp::create(builder, itemLoc, cond, &matchBlock,
359 builder.setInsertionPointToEnd(&nextBlock);
365 matchBlock.moveBefore(builder.getInsertionBlock());
368 OpBuilder::InsertionGuard guard(builder);
369 builder.setInsertionPointToEnd(&matchBlock);
370 if (failed(
context.convertStatement(*item.stmt)))
372 if (!isTerminated()) {
373 auto loc =
context.convertLocation(item.stmt->sourceRange);
374 mlir::cf::BranchOp::create(builder, loc, &exitBlock);
378 const auto caseStmtAttrs =
context.compilation.getAttributes(caseStmt);
379 const bool hasFullCaseAttr =
380 llvm::find_if(caseStmtAttrs, [](
const AttributeSymbol *attr) {
381 return attr->name ==
"full_case";
382 }) != caseStmtAttrs.end();
393 auto twoStateExhaustive =
false;
394 if (
auto intType = dyn_cast<moore::IntType>(caseExpr.getType());
395 intType && intType.getWidth() < 32 &&
396 itemConsts.size() == (1 << intType.getWidth())) {
398 llvm::sort(itemConsts, [](
auto a,
auto b) {
399 return a.getValue().getRawValue().ult(b.getValue().getRawValue());
407 for (
auto value : itemConsts) {
408 if (value.getValue() != nextValue)
412 twoStateExhaustive = nextValue.isZero();
423 if ((twoStateExhaustive || (hasFullCaseAttr && !caseStmt.defaultCase)) &&
425 caseStmt.condition == CaseStatementCondition::Normal) {
426 mlir::cf::BranchOp::create(builder, loc, lastMatchBlock);
429 if (caseStmt.defaultCase)
430 if (failed(
context.convertStatement(*caseStmt.defaultCase)))
433 mlir::cf::BranchOp::create(builder, loc, &exitBlock);
438 if (exitBlock.hasNoPredecessors()) {
442 builder.setInsertionPointToEnd(&exitBlock);
448 LogicalResult visit(
const slang::ast::ForLoopStatement &stmt) {
450 for (
auto *initExpr : stmt.initializers)
451 if (!
context.convertRvalueExpression(*initExpr))
455 auto &exitBlock = createBlock();
456 auto &stepBlock = createBlock();
457 auto &bodyBlock = createBlock();
458 auto &checkBlock = createBlock();
459 cf::BranchOp::create(builder, loc, &checkBlock);
462 context.loopStack.push_back({&stepBlock, &exitBlock});
463 auto done = llvm::make_scope_exit([&] {
context.loopStack.pop_back(); });
466 builder.setInsertionPointToEnd(&checkBlock);
467 auto cond =
context.convertRvalueExpression(*stmt.stopExpr);
470 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
471 cond = moore::ToBuiltinBoolOp::create(builder, loc, cond);
472 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
475 builder.setInsertionPointToEnd(&bodyBlock);
476 if (failed(
context.convertStatement(stmt.body)))
479 cf::BranchOp::create(builder, loc, &stepBlock);
482 builder.setInsertionPointToEnd(&stepBlock);
483 for (
auto *stepExpr : stmt.steps)
484 if (!
context.convertRvalueExpression(*stepExpr))
487 cf::BranchOp::create(builder, loc, &checkBlock);
491 if (exitBlock.hasNoPredecessors()) {
495 builder.setInsertionPointToEnd(&exitBlock);
500 LogicalResult visit(
const slang::ast::ForeachLoopStatement &stmt) {
501 for (uint32_t level = 0; level < stmt.loopDims.size(); level++) {
502 if (stmt.loopDims[level].loopVar)
503 return recursiveForeach(stmt, level);
509 LogicalResult visit(
const slang::ast::RepeatLoopStatement &stmt) {
510 auto count =
context.convertRvalueExpression(stmt.count);
515 auto &exitBlock = createBlock();
516 auto &stepBlock = createBlock();
517 auto &bodyBlock = createBlock();
518 auto &checkBlock = createBlock();
519 auto currentCount = checkBlock.addArgument(count.getType(), count.getLoc());
520 cf::BranchOp::create(builder, loc, &checkBlock, count);
523 context.loopStack.push_back({&stepBlock, &exitBlock});
524 auto done = llvm::make_scope_exit([&] {
context.loopStack.pop_back(); });
527 builder.setInsertionPointToEnd(&checkBlock);
528 auto cond = builder.createOrFold<moore::BoolCastOp>(loc, currentCount);
529 cond = moore::ToBuiltinBoolOp::create(builder, loc, cond);
530 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
533 builder.setInsertionPointToEnd(&bodyBlock);
534 if (failed(
context.convertStatement(stmt.body)))
537 cf::BranchOp::create(builder, loc, &stepBlock);
540 builder.setInsertionPointToEnd(&stepBlock);
541 auto one = moore::ConstantOp::create(
542 builder, count.getLoc(), cast<moore::IntType>(count.getType()), 1);
544 moore::SubOp::create(builder, count.getLoc(), currentCount, one);
545 cf::BranchOp::create(builder, loc, &checkBlock, nextCount);
549 if (exitBlock.hasNoPredecessors()) {
553 builder.setInsertionPointToEnd(&exitBlock);
559 LogicalResult createWhileLoop(
const slang::ast::Expression &condExpr,
560 const slang::ast::Statement &bodyStmt,
563 auto &exitBlock = createBlock();
564 auto &bodyBlock = createBlock();
565 auto &checkBlock = createBlock();
566 cf::BranchOp::create(builder, loc, atLeastOnce ? &bodyBlock : &checkBlock);
568 bodyBlock.moveBefore(&checkBlock);
571 context.loopStack.push_back({&checkBlock, &exitBlock});
572 auto done = llvm::make_scope_exit([&] {
context.loopStack.pop_back(); });
575 builder.setInsertionPointToEnd(&checkBlock);
576 auto cond =
context.convertRvalueExpression(condExpr);
579 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
580 cond = moore::ToBuiltinBoolOp::create(builder, loc, cond);
581 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
584 builder.setInsertionPointToEnd(&bodyBlock);
585 if (failed(
context.convertStatement(bodyStmt)))
588 cf::BranchOp::create(builder, loc, &checkBlock);
592 if (exitBlock.hasNoPredecessors()) {
596 builder.setInsertionPointToEnd(&exitBlock);
601 LogicalResult visit(
const slang::ast::WhileLoopStatement &stmt) {
602 return createWhileLoop(stmt.cond, stmt.body,
false);
605 LogicalResult visit(
const slang::ast::DoWhileLoopStatement &stmt) {
606 return createWhileLoop(stmt.cond, stmt.body,
true);
610 LogicalResult visit(
const slang::ast::ForeverLoopStatement &stmt) {
612 auto &exitBlock = createBlock();
613 auto &bodyBlock = createBlock();
614 cf::BranchOp::create(builder, loc, &bodyBlock);
617 context.loopStack.push_back({&bodyBlock, &exitBlock});
618 auto done = llvm::make_scope_exit([&] {
context.loopStack.pop_back(); });
621 builder.setInsertionPointToEnd(&bodyBlock);
622 if (failed(
context.convertStatement(stmt.body)))
625 cf::BranchOp::create(builder, loc, &bodyBlock);
629 if (exitBlock.hasNoPredecessors()) {
633 builder.setInsertionPointToEnd(&exitBlock);
639 LogicalResult visit(
const slang::ast::TimedStatement &stmt) {
640 return context.convertTimingControl(stmt.timing, stmt.stmt);
644 LogicalResult visit(
const slang::ast::ReturnStatement &stmt) {
646 auto expr =
context.convertRvalueExpression(*stmt.expr);
649 mlir::func::ReturnOp::create(builder, loc, expr);
651 mlir::func::ReturnOp::create(builder, loc);
658 LogicalResult visit(
const slang::ast::ContinueStatement &stmt) {
660 return mlir::emitError(loc,
661 "cannot `continue` without a surrounding loop");
662 cf::BranchOp::create(builder, loc,
context.loopStack.back().continueBlock);
668 LogicalResult visit(
const slang::ast::BreakStatement &stmt) {
670 return mlir::emitError(loc,
"cannot `break` without a surrounding loop");
671 cf::BranchOp::create(builder, loc,
context.loopStack.back().breakBlock);
677 LogicalResult visit(
const slang::ast::ImmediateAssertionStatement &stmt) {
678 auto cond =
context.convertRvalueExpression(stmt.cond);
679 cond =
context.convertToBool(cond);
684 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
685 auto defer = moore::DeferAssert::Immediate;
687 defer = moore::DeferAssert::Final;
688 else if (stmt.isDeferred)
689 defer = moore::DeferAssert::Observed;
691 switch (stmt.assertionKind) {
692 case slang::ast::AssertionKind::Assert:
693 moore::AssertOp::create(builder, loc, defer, cond, StringAttr{});
695 case slang::ast::AssertionKind::Assume:
696 moore::AssumeOp::create(builder, loc, defer, cond, StringAttr{});
698 case slang::ast::AssertionKind::CoverProperty:
699 moore::CoverOp::create(builder, loc, defer, cond, StringAttr{});
704 mlir::emitError(loc) <<
"unsupported immediate assertion kind: "
705 << slang::ast::toString(stmt.assertionKind);
710 cond = moore::ToBuiltinBoolOp::create(builder, loc, cond);
713 Block &exitBlock = createBlock();
714 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
715 Block &trueBlock = createBlock();
716 cf::CondBranchOp::create(builder, loc, cond, &trueBlock,
717 falseBlock ? falseBlock : &exitBlock);
720 builder.setInsertionPointToEnd(&trueBlock);
721 if (stmt.ifTrue && failed(
context.convertStatement(*stmt.ifTrue)))
724 cf::BranchOp::create(builder, loc, &exitBlock);
728 builder.setInsertionPointToEnd(falseBlock);
729 if (failed(
context.convertStatement(*stmt.ifFalse)))
732 cf::BranchOp::create(builder, loc, &exitBlock);
737 if (exitBlock.hasNoPredecessors()) {
741 builder.setInsertionPointToEnd(&exitBlock);
747 LogicalResult visit(
const slang::ast::ConcurrentAssertionStatement &stmt) {
748 auto loc =
context.convertLocation(stmt.sourceRange);
749 auto property =
context.convertAssertionExpression(stmt.propertySpec, loc);
754 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
755 switch (stmt.assertionKind) {
756 case slang::ast::AssertionKind::Assert:
757 verif::AssertOp::create(builder, loc, property, Value(), StringAttr{});
759 case slang::ast::AssertionKind::Assume:
760 verif::AssumeOp::create(builder, loc, property, Value(), StringAttr{});
765 mlir::emitError(loc) <<
"unsupported concurrent assertion kind: "
766 << slang::ast::toString(stmt.assertionKind);
771 <<
"concurrent assertion statements with action blocks "
772 "are not supported yet";
786 getDisplayMessage(std::span<const slang::ast::Expression *const> args) {
787 if (args.size() == 0)
796 if (args[0]->as_if<slang::ast::StringLiteral>()) {
797 return context.convertFormatString(args, loc);
800 if (args.size() == 1) {
801 return context.convertRvalueExpression(
802 *args[0], builder.getType<moore::FormatStringType>());
805 return emitError(loc) <<
"Failed to convert Display Message!";
812 visitSystemCall(
const slang::ast::ExpressionStatement &stmt,
813 const slang::ast::CallExpression &expr,
814 const slang::ast::CallExpression::SystemCallInfo &info) {
815 const auto &subroutine = *
info.subroutine;
816 auto args = expr.arguments();
820 if (subroutine.name ==
"$stop") {
821 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
822 moore::StopBIOp::create(builder, loc);
826 if (subroutine.name ==
"$finish") {
827 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
828 moore::FinishBIOp::create(builder, loc, 0);
829 moore::UnreachableOp::create(builder, loc);
834 if (subroutine.name ==
"$exit") {
844 bool isDisplay =
false;
845 bool appendNewline =
false;
846 StringRef remainingName = subroutine.name;
847 if (remainingName.consume_front(
"$display")) {
849 appendNewline =
true;
850 }
else if (remainingName.consume_front(
"$write")) {
855 using moore::IntFormat;
856 IntFormat defaultFormat = IntFormat::Decimal;
857 if (isDisplay && !remainingName.empty()) {
858 if (remainingName ==
"b")
859 defaultFormat = IntFormat::Binary;
860 else if (remainingName ==
"o")
861 defaultFormat = IntFormat::Octal;
862 else if (remainingName ==
"h")
863 defaultFormat = IntFormat::HexLower;
870 context.convertFormatString(args, loc, defaultFormat, appendNewline);
873 if (*message == Value{})
875 moore::DisplayBIOp::create(builder, loc, *message);
880 using moore::Severity;
881 std::optional<Severity> severity;
882 if (subroutine.name ==
"$info")
883 severity = Severity::Info;
884 else if (subroutine.name ==
"$warning")
885 severity = Severity::Warning;
886 else if (subroutine.name ==
"$error")
887 severity = Severity::Error;
888 else if (subroutine.name ==
"$fatal")
889 severity = Severity::Fatal;
893 const slang::ast::Expression *verbosityExpr =
nullptr;
894 if (severity == Severity::Fatal && args.size() >= 1) {
895 verbosityExpr = args[0];
896 args = args.subspan(1);
899 FailureOr<Value> maybeMessage = getDisplayMessage(args);
900 if (failed(maybeMessage))
902 auto message = maybeMessage.value();
904 if (message == Value{})
905 message = moore::FormatLiteralOp::create(builder, loc,
"");
906 moore::SeverityBIOp::create(builder, loc, *severity, message);
909 if (severity == Severity::Fatal) {
910 createFinishMessage(verbosityExpr);
911 moore::FinishBIOp::create(builder, loc, 1);
912 moore::UnreachableOp::create(builder, loc);
924 void createFinishMessage(
const slang::ast::Expression *verbosityExpr) {
925 unsigned verbosity = 1;
928 context.evaluateConstant(*verbosityExpr).integer().as<
unsigned>();
929 assert(value &&
"Slang guarantees constant verbosity parameter");
934 moore::FinishMessageBIOp::create(builder, loc, verbosity > 1);
938 template <
typename T>
939 LogicalResult visit(T &&stmt) {
940 mlir::emitError(loc,
"unsupported statement: ")
941 << slang::ast::toString(stmt.kind);
942 return mlir::failure();
945 LogicalResult visitInvalid(
const slang::ast::Statement &stmt) {
946 mlir::emitError(loc,
"invalid statement: ")
947 << slang::ast::toString(stmt.kind);
948 return mlir::failure();
953LogicalResult Context::convertStatement(
const slang::ast::Statement &stmt) {
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.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
OpBuilder builder
The builder used to create IR operations.
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.