10#include "slang/ast/Compilation.h"
11#include "slang/ast/SystemSubroutine.h"
12#include "llvm/ADT/ScopeExit.h"
16using namespace ImportVerilog;
26 : context(context), loc(loc), builder(context.builder) {}
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);
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)))
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()) {
138 mlir::emitWarning(loc,
"unreachable code");
148 LogicalResult visit(
const slang::ast::BlockStatement &stmt) {
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();
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>()) {
197 cast<moore::RefType>(lhs.getType()).getNestedType(), strValue,
199 moore::BlockingAssignOp::create(builder, loc, lhs, convertedValue);
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()) {
235 auto varOp = moore::VariableOp::create(
236 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
237 builder.getStringAttr(var.name), initial);
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");
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);
276 cf::BranchOp::create(builder, loc, &exitBlock);
280 builder.setInsertionPointToEnd(falseBlock);
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;
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) {
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);
372 if (!isTerminated()) {
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)
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);
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);
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) {
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);
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);
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);
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);
625 cf::BranchOp::create(builder, loc, &bodyBlock);
629 if (exitBlock.hasNoPredecessors()) {
633 builder.setInsertionPointToEnd(&exitBlock);
639 LogicalResult visit(
const slang::ast::TimedStatement &stmt) {
644 LogicalResult visit(
const slang::ast::ReturnStatement &stmt) {
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) {
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);
724 cf::BranchOp::create(builder, loc, &exitBlock);
728 builder.setInsertionPointToEnd(falseBlock);
732 cf::BranchOp::create(builder, loc, &exitBlock);
737 if (exitBlock.hasNoPredecessors()) {
741 builder.setInsertionPointToEnd(&exitBlock);
747 LogicalResult visit(
const slang::ast::ConcurrentAssertionStatement &stmt) {
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>()) {
800 if (args.size() == 1) {
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;
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;
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 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.
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.
Value convertLvalueExpression(const slang::ast::Expression &expr)
SmallVector< LoopFrame > loopStack
A stack of loop continuation and exit blocks.
slang::ConstantValue evaluateConstant(const slang::ast::Expression &expr)
Evaluate the constant value of an expression.
slang::ast::Compilation & compilation
LogicalResult convertTimingControl(const slang::ast::TimingControl &ctrl, const slang::ast::Statement &stmt)
OpBuilder builder
The builder used to create IR operations.
Type convertType(const slang::ast::Type &type, LocationAttr loc={})
Convert a slang type into an MLIR type.
Value convertToBool(Value value)
Helper function to convert a value to its "truthy" boolean value.
ValueSymbols valueSymbols
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...
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
Value convertAssertionExpression(const slang::ast::AssertionExpr &expr, Location loc)
LogicalResult convertStatement(const slang::ast::Statement &stmt)
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.