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();
270 Block *lastMatchBlock =
nullptr;
271 SmallVector<moore::FVIntegerAttr> itemConsts;
273 for (
const auto &item : caseStmt.items) {
276 auto &matchBlock = createBlock();
277 lastMatchBlock = &matchBlock;
282 for (
const auto *expr : item.expressions) {
286 auto itemLoc = value.getLoc();
289 auto maybeConst = value;
290 if (
auto defOp = maybeConst.getDefiningOp<moore::ConversionOp>())
291 maybeConst = defOp.getInput();
292 if (
auto defOp = maybeConst.getDefiningOp<moore::ConstantOp>())
293 itemConsts.push_back(defOp.getValueAttr());
297 switch (caseStmt.condition) {
298 case CaseStatementCondition::Normal:
299 cond = builder.create<moore::CaseEqOp>(itemLoc, caseExpr, value);
301 case CaseStatementCondition::WildcardXOrZ:
302 cond = builder.create<moore::CaseXZEqOp>(itemLoc, caseExpr, value);
304 case CaseStatementCondition::WildcardJustZ:
305 cond = builder.create<moore::CaseZEqOp>(itemLoc, caseExpr, value);
307 case CaseStatementCondition::Inside:
308 mlir::emitError(loc,
"unsupported set membership case statement");
311 cond = builder.create<moore::ConversionOp>(itemLoc, builder.getI1Type(),
316 auto &nextBlock = createBlock();
317 builder.create<mlir::cf::CondBranchOp>(itemLoc, cond, &matchBlock,
319 builder.setInsertionPointToEnd(&nextBlock);
325 matchBlock.moveBefore(builder.getInsertionBlock());
328 OpBuilder::InsertionGuard guard(builder);
329 builder.setInsertionPointToEnd(&matchBlock);
332 if (!isTerminated()) {
334 builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
347 auto twoStateExhaustive =
false;
348 if (
auto intType = dyn_cast<moore::IntType>(caseExpr.getType());
349 intType && intType.getWidth() < 32 &&
350 itemConsts.size() == (1 << intType.getWidth())) {
352 llvm::sort(itemConsts, [](
auto a,
auto b) {
353 return a.getValue().getRawValue().ult(b.getValue().getRawValue());
361 for (
auto value : itemConsts) {
362 if (value.getValue() != nextValue)
366 twoStateExhaustive = nextValue.isZero();
372 if (twoStateExhaustive && lastMatchBlock &&
373 caseStmt.condition == CaseStatementCondition::Normal) {
374 builder.create<mlir::cf::BranchOp>(loc, lastMatchBlock);
377 if (caseStmt.defaultCase)
381 builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
386 if (exitBlock.hasNoPredecessors()) {
390 builder.setInsertionPointToEnd(&exitBlock);
396 LogicalResult visit(
const slang::ast::ForLoopStatement &stmt) {
398 for (
auto *initExpr : stmt.initializers)
399 if (!context.convertRvalueExpression(*initExpr))
403 auto &exitBlock = createBlock();
404 auto &stepBlock = createBlock();
405 auto &bodyBlock = createBlock();
406 auto &checkBlock = createBlock();
407 builder.create<cf::BranchOp>(loc, &checkBlock);
410 context.
loopStack.push_back({&stepBlock, &exitBlock});
411 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
414 builder.setInsertionPointToEnd(&checkBlock);
418 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
419 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
420 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
423 builder.setInsertionPointToEnd(&bodyBlock);
427 builder.create<cf::BranchOp>(loc, &stepBlock);
430 builder.setInsertionPointToEnd(&stepBlock);
431 for (
auto *stepExpr : stmt.steps)
432 if (!context.convertRvalueExpression(*stepExpr))
435 builder.create<cf::BranchOp>(loc, &checkBlock);
439 if (exitBlock.hasNoPredecessors()) {
443 builder.setInsertionPointToEnd(&exitBlock);
448 LogicalResult visit(
const slang::ast::ForeachLoopStatement &stmt) {
449 for (uint32_t level = 0; level < stmt.loopDims.size(); level++) {
450 if (stmt.loopDims[level].loopVar)
451 return recursiveForeach(stmt, level);
457 LogicalResult visit(
const slang::ast::RepeatLoopStatement &stmt) {
463 auto &exitBlock = createBlock();
464 auto &stepBlock = createBlock();
465 auto &bodyBlock = createBlock();
466 auto &checkBlock = createBlock();
467 auto currentCount = checkBlock.addArgument(count.getType(), count.getLoc());
468 builder.create<cf::BranchOp>(loc, &checkBlock, count);
471 context.
loopStack.push_back({&stepBlock, &exitBlock});
472 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
475 builder.setInsertionPointToEnd(&checkBlock);
476 auto cond = builder.createOrFold<moore::BoolCastOp>(loc, currentCount);
477 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
478 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
481 builder.setInsertionPointToEnd(&bodyBlock);
485 builder.create<cf::BranchOp>(loc, &stepBlock);
488 builder.setInsertionPointToEnd(&stepBlock);
489 auto one = builder.create<moore::ConstantOp>(
490 count.getLoc(), cast<moore::IntType>(count.getType()), 1);
492 builder.create<moore::SubOp>(count.getLoc(), currentCount, one);
493 builder.create<cf::BranchOp>(loc, &checkBlock, nextCount);
497 if (exitBlock.hasNoPredecessors()) {
501 builder.setInsertionPointToEnd(&exitBlock);
507 LogicalResult createWhileLoop(
const slang::ast::Expression &condExpr,
508 const slang::ast::Statement &bodyStmt,
511 auto &exitBlock = createBlock();
512 auto &bodyBlock = createBlock();
513 auto &checkBlock = createBlock();
514 builder.create<cf::BranchOp>(loc, atLeastOnce ? &bodyBlock : &checkBlock);
516 bodyBlock.moveBefore(&checkBlock);
519 context.
loopStack.push_back({&checkBlock, &exitBlock});
520 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
523 builder.setInsertionPointToEnd(&checkBlock);
527 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
528 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
529 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
532 builder.setInsertionPointToEnd(&bodyBlock);
536 builder.create<cf::BranchOp>(loc, &checkBlock);
540 if (exitBlock.hasNoPredecessors()) {
544 builder.setInsertionPointToEnd(&exitBlock);
549 LogicalResult visit(
const slang::ast::WhileLoopStatement &stmt) {
550 return createWhileLoop(stmt.cond, stmt.body,
false);
553 LogicalResult visit(
const slang::ast::DoWhileLoopStatement &stmt) {
554 return createWhileLoop(stmt.cond, stmt.body,
true);
558 LogicalResult visit(
const slang::ast::ForeverLoopStatement &stmt) {
560 auto &exitBlock = createBlock();
561 auto &bodyBlock = createBlock();
562 builder.create<cf::BranchOp>(loc, &bodyBlock);
565 context.
loopStack.push_back({&bodyBlock, &exitBlock});
566 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
569 builder.setInsertionPointToEnd(&bodyBlock);
573 builder.create<cf::BranchOp>(loc, &bodyBlock);
577 if (exitBlock.hasNoPredecessors()) {
581 builder.setInsertionPointToEnd(&exitBlock);
587 LogicalResult visit(
const slang::ast::TimedStatement &stmt) {
592 LogicalResult visit(
const slang::ast::ReturnStatement &stmt) {
597 builder.create<mlir::func::ReturnOp>(loc, expr);
599 builder.create<mlir::func::ReturnOp>(loc);
606 LogicalResult visit(
const slang::ast::ContinueStatement &stmt) {
608 return mlir::emitError(loc,
609 "cannot `continue` without a surrounding loop");
610 builder.create<cf::BranchOp>(loc, context.
loopStack.back().continueBlock);
616 LogicalResult visit(
const slang::ast::BreakStatement &stmt) {
618 return mlir::emitError(loc,
"cannot `break` without a surrounding loop");
619 builder.create<cf::BranchOp>(loc, context.
loopStack.back().breakBlock);
625 LogicalResult visit(
const slang::ast::ImmediateAssertionStatement &stmt) {
632 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
633 auto defer = moore::DeferAssert::Immediate;
635 defer = moore::DeferAssert::Final;
636 else if (stmt.isDeferred)
637 defer = moore::DeferAssert::Observed;
639 switch (stmt.assertionKind) {
640 case slang::ast::AssertionKind::Assert:
641 builder.create<moore::AssertOp>(loc, defer, cond, StringAttr{});
643 case slang::ast::AssertionKind::Assume:
644 builder.create<moore::AssumeOp>(loc, defer, cond, StringAttr{});
646 case slang::ast::AssertionKind::CoverProperty:
647 builder.create<moore::CoverOp>(loc, defer, cond, StringAttr{});
652 mlir::emitError(loc) <<
"unsupported immediate assertion kind: "
653 << slang::ast::toString(stmt.assertionKind);
658 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
661 Block &exitBlock = createBlock();
662 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
663 Block &trueBlock = createBlock();
664 builder.create<cf::CondBranchOp>(loc, cond, &trueBlock,
665 falseBlock ? falseBlock : &exitBlock);
668 builder.setInsertionPointToEnd(&trueBlock);
672 builder.create<cf::BranchOp>(loc, &exitBlock);
676 builder.setInsertionPointToEnd(falseBlock);
680 builder.create<cf::BranchOp>(loc, &exitBlock);
685 if (exitBlock.hasNoPredecessors()) {
689 builder.setInsertionPointToEnd(&exitBlock);
695 LogicalResult visit(
const slang::ast::ConcurrentAssertionStatement &stmt) {
702 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
703 switch (stmt.assertionKind) {
704 case slang::ast::AssertionKind::Assert:
705 builder.create<verif::AssertOp>(loc, property, Value(), StringAttr{});
707 case slang::ast::AssertionKind::Assume:
708 builder.create<verif::AssumeOp>(loc, property, Value(), StringAttr{});
713 mlir::emitError(loc) <<
"unsupported concurrent assertion kind: "
714 << slang::ast::toString(stmt.assertionKind);
719 <<
"concurrent assertion statements with action blocks "
720 "are not supported yet";
728 visitSystemCall(
const slang::ast::ExpressionStatement &stmt,
729 const slang::ast::CallExpression &expr,
730 const slang::ast::CallExpression::SystemCallInfo &info) {
731 const auto &subroutine = *
info.subroutine;
732 auto args = expr.arguments();
736 if (subroutine.name ==
"$stop") {
737 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
738 builder.create<moore::StopBIOp>(loc);
742 if (subroutine.name ==
"$finish") {
743 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
744 builder.create<moore::FinishBIOp>(loc, 0);
745 builder.create<moore::UnreachableOp>(loc);
750 if (subroutine.name ==
"$exit") {
760 bool isDisplay =
false;
761 bool appendNewline =
false;
762 StringRef remainingName = subroutine.name;
763 if (remainingName.consume_front(
"$display")) {
765 appendNewline =
true;
766 }
else if (remainingName.consume_front(
"$write")) {
771 using moore::IntFormat;
772 IntFormat defaultFormat = IntFormat::Decimal;
773 if (isDisplay && !remainingName.empty()) {
774 if (remainingName ==
"b")
775 defaultFormat = IntFormat::Binary;
776 else if (remainingName ==
"o")
777 defaultFormat = IntFormat::Octal;
778 else if (remainingName ==
"h")
779 defaultFormat = IntFormat::HexLower;
789 if (*message == Value{})
791 builder.create<moore::DisplayBIOp>(loc, *message);
796 using moore::Severity;
797 std::optional<Severity> severity;
798 if (subroutine.name ==
"$info")
799 severity = Severity::Info;
800 else if (subroutine.name ==
"$warning")
801 severity = Severity::Warning;
802 else if (subroutine.name ==
"$error")
803 severity = Severity::Error;
804 else if (subroutine.name ==
"$fatal")
805 severity = Severity::Fatal;
809 const slang::ast::Expression *verbosityExpr =
nullptr;
810 if (severity == Severity::Fatal && args.size() >= 1) {
811 verbosityExpr = args[0];
812 args = args.subspan(1);
819 if (*message == Value{})
820 *message = builder.create<moore::FormatLiteralOp>(loc,
"");
822 builder.create<moore::SeverityBIOp>(loc, *severity, *message);
825 if (severity == Severity::Fatal) {
826 createFinishMessage(verbosityExpr);
827 builder.create<moore::FinishBIOp>(loc, 1);
828 builder.create<moore::UnreachableOp>(loc);
840 void createFinishMessage(
const slang::ast::Expression *verbosityExpr) {
841 unsigned verbosity = 1;
845 assert(value &&
"Slang guarantees constant verbosity parameter");
850 builder.create<moore::FinishMessageBIOp>(loc, verbosity > 1);
854 template <
typename T>
855 LogicalResult visit(T &&stmt) {
856 mlir::emitError(loc,
"unsupported statement: ")
857 << slang::ast::toString(stmt.kind);
858 return mlir::failure();
861 LogicalResult visitInvalid(
const slang::ast::Statement &stmt) {
862 mlir::emitError(loc,
"invalid statement: ")
863 << slang::ast::toString(stmt.kind);
864 return mlir::failure();
869LogicalResult Context::convertStatement(
const slang::ast::Statement &stmt) {
869LogicalResult 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.
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={})
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.