10#include "slang/ast/SystemSubroutine.h"
11#include "llvm/ADT/ScopeExit.h"
15using namespace ImportVerilog;
25 : context(context), loc(loc), builder(context.builder) {}
27 bool isTerminated()
const {
return !builder.getInsertionBlock(); }
28 void setTerminated() { builder.clearInsertionPoint(); }
30 Block &createBlock() {
31 assert(builder.getInsertionBlock());
32 auto block = std::make_unique<Block>();
33 block->insertAfter(builder.getInsertionBlock());
34 return *block.release();
37 LogicalResult recursiveForeach(
const slang::ast::ForeachLoopStatement &stmt,
40 const auto &loopDim = stmt.loopDims[level];
41 if (!loopDim.range.has_value()) {
42 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 = builder.create<moore::ConstantOp>(
59 loc, cast<moore::IntType>(type), loopDim.range->lower());
62 Value varOp = builder.create<moore::VariableOp>(
63 loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
64 builder.getStringAttr(iter->name), initial);
68 builder.create<cf::BranchOp>(loc, &checkBlock);
69 builder.setInsertionPointToEnd(&checkBlock);
72 auto upperBound = builder.create<moore::ConstantOp>(
73 loc, cast<moore::IntType>(type), loopDim.range->upper());
75 auto var = builder.create<moore::ReadOp>(loc, varOp);
76 Value cond = builder.create<moore::SleOp>(loc, var, upperBound);
79 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
80 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
81 builder.create<cf::CondBranchOp>(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 builder.create<cf::BranchOp>(loc, &stepBlock);
105 builder.setInsertionPointToEnd(&stepBlock);
108 var = builder.create<moore::ReadOp>(loc, varOp);
110 builder.create<moore::ConstantOp>(loc, cast<moore::IntType>(type), 1);
111 auto postValue = builder.create<moore::AddOp>(loc, var, one).getResult();
112 builder.create<moore::BlockingAssignOp>(loc, varOp, postValue);
113 builder.create<cf::BranchOp>(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 (
auto *defOp = value.getDefiningOp())
174 if (isOpTriviallyDead(defOp))
181 LogicalResult visit(
const slang::ast::VariableDeclStatement &stmt) {
182 const auto &var = stmt.symbol;
183 auto type = context.
convertType(*var.getDeclaredType());
188 if (
const auto *init = var.getInitializer()) {
195 auto varOp = builder.create<moore::VariableOp>(
196 loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
197 builder.getStringAttr(var.name), initial);
204 LogicalResult visit(
const slang::ast::ConditionalStatement &stmt) {
208 for (
const auto &condition : stmt.conditions) {
209 if (condition.pattern)
210 return mlir::emitError(loc,
211 "match patterns in if conditions not supported");
215 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
217 allConds = builder.create<moore::AndOp>(loc, allConds, cond);
221 assert(allConds &&
"slang guarantees at least one condition");
223 builder.create<moore::ConversionOp>(loc, builder.getI1Type(), allConds);
226 Block &exitBlock = createBlock();
227 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
228 Block &trueBlock = createBlock();
229 builder.create<cf::CondBranchOp>(loc, allConds, &trueBlock,
230 falseBlock ? falseBlock : &exitBlock);
233 builder.setInsertionPointToEnd(&trueBlock);
237 builder.create<cf::BranchOp>(loc, &exitBlock);
241 builder.setInsertionPointToEnd(falseBlock);
245 builder.create<cf::BranchOp>(loc, &exitBlock);
250 if (exitBlock.hasNoPredecessors()) {
254 builder.setInsertionPointToEnd(&exitBlock);
260 LogicalResult visit(
const slang::ast::CaseStatement &caseStmt) {
261 using slang::ast::CaseStatementCondition;
269 auto &exitBlock = createBlock();
271 for (
const auto &item : caseStmt.items) {
274 auto &matchBlock = createBlock();
279 for (
const auto *expr : item.expressions) {
283 auto itemLoc = value.getLoc();
287 switch (caseStmt.condition) {
288 case CaseStatementCondition::Normal:
289 cond = builder.create<moore::CaseEqOp>(itemLoc, caseExpr, value);
291 case CaseStatementCondition::WildcardXOrZ:
292 cond = builder.create<moore::CaseXZEqOp>(itemLoc, caseExpr, value);
294 case CaseStatementCondition::WildcardJustZ:
295 cond = builder.create<moore::CaseZEqOp>(itemLoc, caseExpr, value);
297 case CaseStatementCondition::Inside:
298 mlir::emitError(loc,
"unsupported set membership case statement");
301 cond = builder.create<moore::ConversionOp>(itemLoc, builder.getI1Type(),
306 auto &nextBlock = createBlock();
307 builder.create<mlir::cf::CondBranchOp>(itemLoc, cond, &matchBlock,
309 builder.setInsertionPointToEnd(&nextBlock);
315 matchBlock.moveBefore(builder.getInsertionBlock());
318 OpBuilder::InsertionGuard guard(builder);
319 builder.setInsertionPointToEnd(&matchBlock);
322 if (!isTerminated()) {
324 builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
329 if (caseStmt.defaultCase)
333 builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
337 if (exitBlock.hasNoPredecessors()) {
341 builder.setInsertionPointToEnd(&exitBlock);
347 LogicalResult visit(
const slang::ast::ForLoopStatement &stmt) {
349 for (
auto *initExpr : stmt.initializers)
350 if (!context.convertRvalueExpression(*initExpr))
354 auto &exitBlock = createBlock();
355 auto &stepBlock = createBlock();
356 auto &bodyBlock = createBlock();
357 auto &checkBlock = createBlock();
358 builder.create<cf::BranchOp>(loc, &checkBlock);
361 context.
loopStack.push_back({&stepBlock, &exitBlock});
362 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
365 builder.setInsertionPointToEnd(&checkBlock);
369 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
370 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
371 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
374 builder.setInsertionPointToEnd(&bodyBlock);
378 builder.create<cf::BranchOp>(loc, &stepBlock);
381 builder.setInsertionPointToEnd(&stepBlock);
382 for (
auto *stepExpr : stmt.steps)
383 if (!context.convertRvalueExpression(*stepExpr))
386 builder.create<cf::BranchOp>(loc, &checkBlock);
390 if (exitBlock.hasNoPredecessors()) {
394 builder.setInsertionPointToEnd(&exitBlock);
399 LogicalResult visit(
const slang::ast::ForeachLoopStatement &stmt) {
400 for (uint32_t level = 0; level < stmt.loopDims.size(); level++) {
401 if (stmt.loopDims[level].loopVar)
402 return recursiveForeach(stmt, level);
408 LogicalResult visit(
const slang::ast::RepeatLoopStatement &stmt) {
414 auto &exitBlock = createBlock();
415 auto &stepBlock = createBlock();
416 auto &bodyBlock = createBlock();
417 auto &checkBlock = createBlock();
418 auto currentCount = checkBlock.addArgument(count.getType(), count.getLoc());
419 builder.create<cf::BranchOp>(loc, &checkBlock, count);
422 context.
loopStack.push_back({&stepBlock, &exitBlock});
423 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
426 builder.setInsertionPointToEnd(&checkBlock);
427 auto cond = builder.createOrFold<moore::BoolCastOp>(loc, currentCount);
428 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
429 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
432 builder.setInsertionPointToEnd(&bodyBlock);
436 builder.create<cf::BranchOp>(loc, &stepBlock);
439 builder.setInsertionPointToEnd(&stepBlock);
440 auto one = builder.create<moore::ConstantOp>(
441 count.getLoc(), cast<moore::IntType>(count.getType()), 1);
443 builder.create<moore::SubOp>(count.getLoc(), currentCount, one);
444 builder.create<cf::BranchOp>(loc, &checkBlock, nextCount);
448 if (exitBlock.hasNoPredecessors()) {
452 builder.setInsertionPointToEnd(&exitBlock);
458 LogicalResult createWhileLoop(
const slang::ast::Expression &condExpr,
459 const slang::ast::Statement &bodyStmt,
462 auto &exitBlock = createBlock();
463 auto &bodyBlock = createBlock();
464 auto &checkBlock = createBlock();
465 builder.create<cf::BranchOp>(loc, atLeastOnce ? &bodyBlock : &checkBlock);
467 bodyBlock.moveBefore(&checkBlock);
470 context.
loopStack.push_back({&checkBlock, &exitBlock});
471 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
474 builder.setInsertionPointToEnd(&checkBlock);
478 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
479 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
480 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
483 builder.setInsertionPointToEnd(&bodyBlock);
487 builder.create<cf::BranchOp>(loc, &checkBlock);
491 if (exitBlock.hasNoPredecessors()) {
495 builder.setInsertionPointToEnd(&exitBlock);
500 LogicalResult visit(
const slang::ast::WhileLoopStatement &stmt) {
501 return createWhileLoop(stmt.cond, stmt.body,
false);
504 LogicalResult visit(
const slang::ast::DoWhileLoopStatement &stmt) {
505 return createWhileLoop(stmt.cond, stmt.body,
true);
509 LogicalResult visit(
const slang::ast::ForeverLoopStatement &stmt) {
511 auto &exitBlock = createBlock();
512 auto &bodyBlock = createBlock();
513 builder.create<cf::BranchOp>(loc, &bodyBlock);
516 context.
loopStack.push_back({&bodyBlock, &exitBlock});
517 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
520 builder.setInsertionPointToEnd(&bodyBlock);
524 builder.create<cf::BranchOp>(loc, &bodyBlock);
528 if (exitBlock.hasNoPredecessors()) {
532 builder.setInsertionPointToEnd(&exitBlock);
538 LogicalResult visit(
const slang::ast::TimedStatement &stmt) {
543 LogicalResult visit(
const slang::ast::ReturnStatement &stmt) {
548 builder.create<mlir::func::ReturnOp>(loc, expr);
550 builder.create<mlir::func::ReturnOp>(loc);
557 LogicalResult visit(
const slang::ast::ContinueStatement &stmt) {
559 return mlir::emitError(loc,
560 "cannot `continue` without a surrounding loop");
561 builder.create<cf::BranchOp>(loc, context.
loopStack.back().continueBlock);
567 LogicalResult visit(
const slang::ast::BreakStatement &stmt) {
569 return mlir::emitError(loc,
"cannot `break` without a surrounding loop");
570 builder.create<cf::BranchOp>(loc, context.
loopStack.back().breakBlock);
576 LogicalResult visit(
const slang::ast::ImmediateAssertionStatement &stmt) {
583 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
584 auto defer = moore::DeferAssert::Immediate;
586 defer = moore::DeferAssert::Final;
587 else if (stmt.isDeferred)
588 defer = moore::DeferAssert::Observed;
590 switch (stmt.assertionKind) {
591 case slang::ast::AssertionKind::Assert:
592 builder.create<moore::AssertOp>(loc, defer, cond, StringAttr{});
594 case slang::ast::AssertionKind::Assume:
595 builder.create<moore::AssumeOp>(loc, defer, cond, StringAttr{});
597 case slang::ast::AssertionKind::CoverProperty:
598 builder.create<moore::CoverOp>(loc, defer, cond, StringAttr{});
603 mlir::emitError(loc) <<
"unsupported immediate assertion kind: "
604 << slang::ast::toString(stmt.assertionKind);
609 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
612 Block &exitBlock = createBlock();
613 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
614 Block &trueBlock = createBlock();
615 builder.create<cf::CondBranchOp>(loc, cond, &trueBlock,
616 falseBlock ? falseBlock : &exitBlock);
619 builder.setInsertionPointToEnd(&trueBlock);
623 builder.create<cf::BranchOp>(loc, &exitBlock);
627 builder.setInsertionPointToEnd(falseBlock);
631 builder.create<cf::BranchOp>(loc, &exitBlock);
636 if (exitBlock.hasNoPredecessors()) {
640 builder.setInsertionPointToEnd(&exitBlock);
649 visitSystemCall(
const slang::ast::ExpressionStatement &stmt,
650 const slang::ast::CallExpression &expr,
651 const slang::ast::CallExpression::SystemCallInfo &info) {
652 const auto &subroutine = *info.subroutine;
653 auto args = expr.arguments();
657 if (subroutine.name ==
"$stop") {
658 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
659 builder.create<moore::StopBIOp>(loc);
663 if (subroutine.name ==
"$finish") {
664 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
665 builder.create<moore::FinishBIOp>(loc, 0);
666 builder.create<moore::UnreachableOp>(loc);
671 if (subroutine.name ==
"$exit") {
681 bool isDisplay =
false;
682 bool appendNewline =
false;
683 StringRef remainingName = subroutine.name;
684 if (remainingName.consume_front(
"$display")) {
686 appendNewline =
true;
687 }
else if (remainingName.consume_front(
"$write")) {
692 using moore::IntFormat;
693 IntFormat defaultFormat = IntFormat::Decimal;
694 if (isDisplay && !remainingName.empty()) {
695 if (remainingName ==
"b")
696 defaultFormat = IntFormat::Binary;
697 else if (remainingName ==
"o")
698 defaultFormat = IntFormat::Octal;
699 else if (remainingName ==
"h")
700 defaultFormat = IntFormat::HexLower;
710 if (*message == Value{})
712 builder.create<moore::DisplayBIOp>(loc, *message);
717 using moore::Severity;
718 std::optional<Severity> severity;
719 if (subroutine.name ==
"$info")
720 severity = Severity::Info;
721 else if (subroutine.name ==
"$warning")
722 severity = Severity::Warning;
723 else if (subroutine.name ==
"$error")
724 severity = Severity::Error;
725 else if (subroutine.name ==
"$fatal")
726 severity = Severity::Fatal;
730 const slang::ast::Expression *verbosityExpr =
nullptr;
731 if (severity == Severity::Fatal && args.size() >= 1) {
732 verbosityExpr = args[0];
733 args = args.subspan(1);
740 if (*message == Value{})
741 *message = builder.create<moore::FormatLiteralOp>(loc,
"");
743 builder.create<moore::SeverityBIOp>(loc, *severity, *message);
746 if (severity == Severity::Fatal) {
747 createFinishMessage(verbosityExpr);
748 builder.create<moore::FinishBIOp>(loc, 1);
749 builder.create<moore::UnreachableOp>(loc);
761 void createFinishMessage(
const slang::ast::Expression *verbosityExpr) {
762 unsigned verbosity = 1;
766 assert(value &&
"Slang guarantees constant verbosity parameter");
771 builder.create<moore::FinishMessageBIOp>(loc, verbosity > 1);
775 template <
typename T>
776 LogicalResult visit(T &&stmt) {
777 mlir::emitError(loc,
"unsupported statement: ")
778 << slang::ast::toString(stmt.kind);
779 return mlir::failure();
782 LogicalResult visitInvalid(
const slang::ast::Statement &stmt) {
783 mlir::emitError(loc,
"invalid statement: ")
784 << slang::ast::toString(stmt.kind);
785 return mlir::failure();
assert(baseType &&"element must be base type")
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.
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.
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.
FailureOr< Value > convertFormatString(slang::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...
ValueSymbols valueSymbols
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
LogicalResult convertStatement(const slang::ast::Statement &stmt)
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.