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 emitError(loc) <<
"dynamic loop variable is unsupported";
45 auto &exitBlock = createBlock();
46 auto &stepBlock = createBlock();
47 auto &bodyBlock = createBlock();
48 auto &checkBlock = createBlock();
51 context.
loopStack.push_back({&stepBlock, &exitBlock});
52 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
54 const auto &iter = loopDim.loopVar;
55 auto type = context.
convertType(*iter->getDeclaredType());
59 Value initial = moore::ConstantOp::create(
60 builder, loc, cast<moore::IntType>(type), loopDim.range->lower());
63 Value varOp = moore::VariableOp::create(
64 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
65 builder.getStringAttr(iter->name), initial);
69 cf::BranchOp::create(builder, loc, &checkBlock);
70 builder.setInsertionPointToEnd(&checkBlock);
73 auto upperBound = moore::ConstantOp::create(
74 builder, loc, cast<moore::IntType>(type), loopDim.range->upper());
76 auto var = moore::ReadOp::create(builder, loc, varOp);
77 Value cond = moore::SleOp::create(builder, loc, var, upperBound);
80 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
81 cond = moore::ConversionOp::create(builder, loc, builder.getI1Type(), cond);
82 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
84 builder.setInsertionPointToEnd(&bodyBlock);
89 for (uint32_t nextLevel = level + 1; nextLevel < stmt.loopDims.size();
91 if (stmt.loopDims[nextLevel].loopVar) {
92 if (failed(recursiveForeach(stmt, nextLevel)))
104 cf::BranchOp::create(builder, loc, &stepBlock);
106 builder.setInsertionPointToEnd(&stepBlock);
109 var = moore::ReadOp::create(builder, loc, varOp);
111 moore::ConstantOp::create(builder, loc, cast<moore::IntType>(type), 1);
112 auto postValue = moore::AddOp::create(builder, loc, var, one).getResult();
113 moore::BlockingAssignOp::create(builder, loc, varOp, postValue);
114 cf::BranchOp::create(builder, loc, &checkBlock);
116 if (exitBlock.hasNoPredecessors()) {
120 builder.setInsertionPointToEnd(&exitBlock);
126 LogicalResult visit(
const slang::ast::EmptyStatement &) {
return success(); }
135 LogicalResult visit(
const slang::ast::StatementList &stmts) {
136 for (
auto *stmt : stmts.list) {
137 if (isTerminated()) {
139 mlir::emitWarning(loc,
"unreachable code");
149 LogicalResult visit(
const slang::ast::BlockStatement &stmt) {
154 LogicalResult visit(
const slang::ast::ExpressionStatement &stmt) {
156 if (
const auto *call = stmt.expr.as_if<slang::ast::CallExpression>()) {
157 if (
const auto *info =
158 std::get_if<slang::ast::CallExpression::SystemCallInfo>(
159 &call->subroutine)) {
160 auto handled = visitSystemCall(stmt, *call, *info);
174 if (
auto *defOp = value.getDefiningOp())
175 if (isOpTriviallyDead(defOp))
182 LogicalResult visit(
const slang::ast::VariableDeclStatement &stmt) {
183 const auto &var = stmt.symbol;
184 auto type = context.
convertType(*var.getDeclaredType());
189 if (
const auto *init = var.getInitializer()) {
196 auto varOp = moore::VariableOp::create(
197 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
198 builder.getStringAttr(var.name), initial);
205 LogicalResult visit(
const slang::ast::ConditionalStatement &stmt) {
209 for (
const auto &condition : stmt.conditions) {
210 if (condition.pattern)
211 return mlir::emitError(loc,
212 "match patterns in if conditions not supported");
216 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
218 allConds = moore::AndOp::create(builder, loc, allConds, cond);
222 assert(allConds &&
"slang guarantees at least one condition");
223 allConds = moore::ConversionOp::create(builder, loc, builder.getI1Type(),
227 Block &exitBlock = createBlock();
228 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
229 Block &trueBlock = createBlock();
230 cf::CondBranchOp::create(builder, loc, allConds, &trueBlock,
231 falseBlock ? falseBlock : &exitBlock);
234 builder.setInsertionPointToEnd(&trueBlock);
238 cf::BranchOp::create(builder, loc, &exitBlock);
242 builder.setInsertionPointToEnd(falseBlock);
246 cf::BranchOp::create(builder, loc, &exitBlock);
251 if (exitBlock.hasNoPredecessors()) {
255 builder.setInsertionPointToEnd(&exitBlock);
261 LogicalResult visit(
const slang::ast::CaseStatement &caseStmt) {
262 using slang::ast::AttributeSymbol;
263 using slang::ast::CaseStatementCondition;
271 auto &exitBlock = createBlock();
272 Block *lastMatchBlock =
nullptr;
273 SmallVector<moore::FVIntegerAttr> itemConsts;
275 for (
const auto &item : caseStmt.items) {
278 auto &matchBlock = createBlock();
279 lastMatchBlock = &matchBlock;
284 for (
const auto *expr : item.expressions) {
288 auto itemLoc = value.getLoc();
291 auto maybeConst = value;
292 if (
auto defOp = maybeConst.getDefiningOp<moore::ConversionOp>())
293 maybeConst = defOp.getInput();
294 if (
auto defOp = maybeConst.getDefiningOp<moore::ConstantOp>())
295 itemConsts.push_back(defOp.getValueAttr());
299 switch (caseStmt.condition) {
300 case CaseStatementCondition::Normal:
301 cond = moore::CaseEqOp::create(builder, itemLoc, caseExpr, value);
303 case CaseStatementCondition::WildcardXOrZ:
304 cond = moore::CaseXZEqOp::create(builder, itemLoc, caseExpr, value);
306 case CaseStatementCondition::WildcardJustZ:
307 cond = moore::CaseZEqOp::create(builder, itemLoc, caseExpr, value);
309 case CaseStatementCondition::Inside:
310 mlir::emitError(loc,
"unsupported set membership case statement");
313 cond = moore::ConversionOp::create(builder, itemLoc,
314 builder.getI1Type(), cond);
318 auto &nextBlock = createBlock();
319 mlir::cf::CondBranchOp::create(builder, itemLoc, cond, &matchBlock,
321 builder.setInsertionPointToEnd(&nextBlock);
327 matchBlock.moveBefore(builder.getInsertionBlock());
330 OpBuilder::InsertionGuard guard(builder);
331 builder.setInsertionPointToEnd(&matchBlock);
334 if (!isTerminated()) {
336 mlir::cf::BranchOp::create(builder, loc, &exitBlock);
340 const auto caseStmtAttrs = context.
compilation.getAttributes(caseStmt);
341 const bool hasFullCaseAttr =
342 llvm::find_if(caseStmtAttrs, [](
const AttributeSymbol *attr) {
343 return attr->name ==
"full_case";
344 }) != caseStmtAttrs.end();
355 auto twoStateExhaustive =
false;
356 if (
auto intType = dyn_cast<moore::IntType>(caseExpr.getType());
357 intType && intType.getWidth() < 32 &&
358 itemConsts.size() == (1 << intType.getWidth())) {
360 llvm::sort(itemConsts, [](
auto a,
auto b) {
361 return a.getValue().getRawValue().ult(b.getValue().getRawValue());
369 for (
auto value : itemConsts) {
370 if (value.getValue() != nextValue)
374 twoStateExhaustive = nextValue.isZero();
385 if ((twoStateExhaustive || (hasFullCaseAttr && !caseStmt.defaultCase)) &&
387 caseStmt.condition == CaseStatementCondition::Normal) {
388 mlir::cf::BranchOp::create(builder, loc, lastMatchBlock);
391 if (caseStmt.defaultCase)
395 mlir::cf::BranchOp::create(builder, loc, &exitBlock);
400 if (exitBlock.hasNoPredecessors()) {
404 builder.setInsertionPointToEnd(&exitBlock);
410 LogicalResult visit(
const slang::ast::ForLoopStatement &stmt) {
412 for (
auto *initExpr : stmt.initializers)
413 if (!context.convertRvalueExpression(*initExpr))
417 auto &exitBlock = createBlock();
418 auto &stepBlock = createBlock();
419 auto &bodyBlock = createBlock();
420 auto &checkBlock = createBlock();
421 cf::BranchOp::create(builder, loc, &checkBlock);
424 context.
loopStack.push_back({&stepBlock, &exitBlock});
425 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
428 builder.setInsertionPointToEnd(&checkBlock);
432 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
433 cond = moore::ConversionOp::create(builder, loc, builder.getI1Type(), cond);
434 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
437 builder.setInsertionPointToEnd(&bodyBlock);
441 cf::BranchOp::create(builder, loc, &stepBlock);
444 builder.setInsertionPointToEnd(&stepBlock);
445 for (
auto *stepExpr : stmt.steps)
446 if (!context.convertRvalueExpression(*stepExpr))
449 cf::BranchOp::create(builder, loc, &checkBlock);
453 if (exitBlock.hasNoPredecessors()) {
457 builder.setInsertionPointToEnd(&exitBlock);
462 LogicalResult visit(
const slang::ast::ForeachLoopStatement &stmt) {
463 for (uint32_t level = 0; level < stmt.loopDims.size(); level++) {
464 if (stmt.loopDims[level].loopVar)
465 return recursiveForeach(stmt, level);
471 LogicalResult visit(
const slang::ast::RepeatLoopStatement &stmt) {
477 auto &exitBlock = createBlock();
478 auto &stepBlock = createBlock();
479 auto &bodyBlock = createBlock();
480 auto &checkBlock = createBlock();
481 auto currentCount = checkBlock.addArgument(count.getType(), count.getLoc());
482 cf::BranchOp::create(builder, loc, &checkBlock, count);
485 context.
loopStack.push_back({&stepBlock, &exitBlock});
486 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
489 builder.setInsertionPointToEnd(&checkBlock);
490 auto cond = builder.createOrFold<moore::BoolCastOp>(loc, currentCount);
491 cond = moore::ConversionOp::create(builder, loc, builder.getI1Type(), cond);
492 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
495 builder.setInsertionPointToEnd(&bodyBlock);
499 cf::BranchOp::create(builder, loc, &stepBlock);
502 builder.setInsertionPointToEnd(&stepBlock);
503 auto one = moore::ConstantOp::create(
504 builder, count.getLoc(), cast<moore::IntType>(count.getType()), 1);
506 moore::SubOp::create(builder, count.getLoc(), currentCount, one);
507 cf::BranchOp::create(builder, loc, &checkBlock, nextCount);
511 if (exitBlock.hasNoPredecessors()) {
515 builder.setInsertionPointToEnd(&exitBlock);
521 LogicalResult createWhileLoop(
const slang::ast::Expression &condExpr,
522 const slang::ast::Statement &bodyStmt,
525 auto &exitBlock = createBlock();
526 auto &bodyBlock = createBlock();
527 auto &checkBlock = createBlock();
528 cf::BranchOp::create(builder, loc, atLeastOnce ? &bodyBlock : &checkBlock);
530 bodyBlock.moveBefore(&checkBlock);
533 context.
loopStack.push_back({&checkBlock, &exitBlock});
534 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
537 builder.setInsertionPointToEnd(&checkBlock);
541 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
542 cond = moore::ConversionOp::create(builder, loc, builder.getI1Type(), cond);
543 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
546 builder.setInsertionPointToEnd(&bodyBlock);
550 cf::BranchOp::create(builder, loc, &checkBlock);
554 if (exitBlock.hasNoPredecessors()) {
558 builder.setInsertionPointToEnd(&exitBlock);
563 LogicalResult visit(
const slang::ast::WhileLoopStatement &stmt) {
564 return createWhileLoop(stmt.cond, stmt.body,
false);
567 LogicalResult visit(
const slang::ast::DoWhileLoopStatement &stmt) {
568 return createWhileLoop(stmt.cond, stmt.body,
true);
572 LogicalResult visit(
const slang::ast::ForeverLoopStatement &stmt) {
574 auto &exitBlock = createBlock();
575 auto &bodyBlock = createBlock();
576 cf::BranchOp::create(builder, loc, &bodyBlock);
579 context.
loopStack.push_back({&bodyBlock, &exitBlock});
580 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
583 builder.setInsertionPointToEnd(&bodyBlock);
587 cf::BranchOp::create(builder, loc, &bodyBlock);
591 if (exitBlock.hasNoPredecessors()) {
595 builder.setInsertionPointToEnd(&exitBlock);
601 LogicalResult visit(
const slang::ast::TimedStatement &stmt) {
606 LogicalResult visit(
const slang::ast::ReturnStatement &stmt) {
611 mlir::func::ReturnOp::create(builder, loc, expr);
613 mlir::func::ReturnOp::create(builder, loc);
620 LogicalResult visit(
const slang::ast::ContinueStatement &stmt) {
622 return mlir::emitError(loc,
623 "cannot `continue` without a surrounding loop");
624 cf::BranchOp::create(builder, loc, context.
loopStack.back().continueBlock);
630 LogicalResult visit(
const slang::ast::BreakStatement &stmt) {
632 return mlir::emitError(loc,
"cannot `break` without a surrounding loop");
633 cf::BranchOp::create(builder, loc, context.
loopStack.back().breakBlock);
639 LogicalResult visit(
const slang::ast::ImmediateAssertionStatement &stmt) {
646 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
647 auto defer = moore::DeferAssert::Immediate;
649 defer = moore::DeferAssert::Final;
650 else if (stmt.isDeferred)
651 defer = moore::DeferAssert::Observed;
653 switch (stmt.assertionKind) {
654 case slang::ast::AssertionKind::Assert:
655 moore::AssertOp::create(builder, loc, defer, cond, StringAttr{});
657 case slang::ast::AssertionKind::Assume:
658 moore::AssumeOp::create(builder, loc, defer, cond, StringAttr{});
660 case slang::ast::AssertionKind::CoverProperty:
661 moore::CoverOp::create(builder, loc, defer, cond, StringAttr{});
666 mlir::emitError(loc) <<
"unsupported immediate assertion kind: "
667 << slang::ast::toString(stmt.assertionKind);
672 cond = moore::ConversionOp::create(builder, loc, builder.getI1Type(), cond);
675 Block &exitBlock = createBlock();
676 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
677 Block &trueBlock = createBlock();
678 cf::CondBranchOp::create(builder, loc, cond, &trueBlock,
679 falseBlock ? falseBlock : &exitBlock);
682 builder.setInsertionPointToEnd(&trueBlock);
686 cf::BranchOp::create(builder, loc, &exitBlock);
690 builder.setInsertionPointToEnd(falseBlock);
694 cf::BranchOp::create(builder, loc, &exitBlock);
699 if (exitBlock.hasNoPredecessors()) {
703 builder.setInsertionPointToEnd(&exitBlock);
709 LogicalResult visit(
const slang::ast::ConcurrentAssertionStatement &stmt) {
716 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
717 switch (stmt.assertionKind) {
718 case slang::ast::AssertionKind::Assert:
719 verif::AssertOp::create(builder, loc, property, Value(), StringAttr{});
721 case slang::ast::AssertionKind::Assume:
722 verif::AssumeOp::create(builder, loc, property, Value(), StringAttr{});
727 mlir::emitError(loc) <<
"unsupported concurrent assertion kind: "
728 << slang::ast::toString(stmt.assertionKind);
733 <<
"concurrent assertion statements with action blocks "
734 "are not supported yet";
742 visitSystemCall(
const slang::ast::ExpressionStatement &stmt,
743 const slang::ast::CallExpression &expr,
744 const slang::ast::CallExpression::SystemCallInfo &info) {
745 const auto &subroutine = *
info.subroutine;
746 auto args = expr.arguments();
750 if (subroutine.name ==
"$stop") {
751 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
752 moore::StopBIOp::create(builder, loc);
756 if (subroutine.name ==
"$finish") {
757 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
758 moore::FinishBIOp::create(builder, loc, 0);
759 moore::UnreachableOp::create(builder, loc);
764 if (subroutine.name ==
"$exit") {
774 bool isDisplay =
false;
775 bool appendNewline =
false;
776 StringRef remainingName = subroutine.name;
777 if (remainingName.consume_front(
"$display")) {
779 appendNewline =
true;
780 }
else if (remainingName.consume_front(
"$write")) {
785 using moore::IntFormat;
786 IntFormat defaultFormat = IntFormat::Decimal;
787 if (isDisplay && !remainingName.empty()) {
788 if (remainingName ==
"b")
789 defaultFormat = IntFormat::Binary;
790 else if (remainingName ==
"o")
791 defaultFormat = IntFormat::Octal;
792 else if (remainingName ==
"h")
793 defaultFormat = IntFormat::HexLower;
803 if (*message == Value{})
805 moore::DisplayBIOp::create(builder, loc, *message);
810 using moore::Severity;
811 std::optional<Severity> severity;
812 if (subroutine.name ==
"$info")
813 severity = Severity::Info;
814 else if (subroutine.name ==
"$warning")
815 severity = Severity::Warning;
816 else if (subroutine.name ==
"$error")
817 severity = Severity::Error;
818 else if (subroutine.name ==
"$fatal")
819 severity = Severity::Fatal;
823 const slang::ast::Expression *verbosityExpr =
nullptr;
824 if (severity == Severity::Fatal && args.size() >= 1) {
825 verbosityExpr = args[0];
826 args = args.subspan(1);
833 if (*message == Value{})
834 *message = moore::FormatLiteralOp::create(builder, loc,
"");
836 moore::SeverityBIOp::create(builder, loc, *severity, *message);
839 if (severity == Severity::Fatal) {
840 createFinishMessage(verbosityExpr);
841 moore::FinishBIOp::create(builder, loc, 1);
842 moore::UnreachableOp::create(builder, loc);
854 void createFinishMessage(
const slang::ast::Expression *verbosityExpr) {
855 unsigned verbosity = 1;
859 assert(value &&
"Slang guarantees constant verbosity parameter");
864 moore::FinishMessageBIOp::create(builder, loc, verbosity > 1);
868 template <
typename T>
869 LogicalResult visit(T &&stmt) {
870 mlir::emitError(loc,
"unsupported statement: ")
871 << slang::ast::toString(stmt.kind);
872 return mlir::failure();
875 LogicalResult visitInvalid(
const slang::ast::Statement &stmt) {
876 mlir::emitError(loc,
"invalid statement: ")
877 << slang::ast::toString(stmt.kind);
878 return mlir::failure();
883LogicalResult Context::convertStatement(
const slang::ast::Statement &stmt) {
883LogicalResult 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.
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.
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.