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 (
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 = moore::VariableOp::create(
196 builder, 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 = moore::AndOp::create(builder, loc, allConds, cond);
221 assert(allConds &&
"slang guarantees at least one condition");
222 allConds = moore::ToBuiltinBoolOp::create(builder, loc, allConds);
225 Block &exitBlock = createBlock();
226 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
227 Block &trueBlock = createBlock();
228 cf::CondBranchOp::create(builder, loc, allConds, &trueBlock,
229 falseBlock ? falseBlock : &exitBlock);
232 builder.setInsertionPointToEnd(&trueBlock);
236 cf::BranchOp::create(builder, loc, &exitBlock);
240 builder.setInsertionPointToEnd(falseBlock);
244 cf::BranchOp::create(builder, loc, &exitBlock);
249 if (exitBlock.hasNoPredecessors()) {
253 builder.setInsertionPointToEnd(&exitBlock);
259 LogicalResult visit(
const slang::ast::CaseStatement &caseStmt) {
260 using slang::ast::AttributeSymbol;
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 while (isa_and_nonnull<moore::ConversionOp, moore::IntToLogicOp,
291 moore::LogicToIntOp>(maybeConst.getDefiningOp()))
292 maybeConst = maybeConst.getDefiningOp()->getOperand(0);
293 if (
auto defOp = maybeConst.getDefiningOp<moore::ConstantOp>())
294 itemConsts.push_back(defOp.getValueAttr());
298 switch (caseStmt.condition) {
299 case CaseStatementCondition::Normal:
300 cond = moore::CaseEqOp::create(builder, itemLoc, caseExpr, value);
302 case CaseStatementCondition::WildcardXOrZ:
303 cond = moore::CaseXZEqOp::create(builder, itemLoc, caseExpr, value);
305 case CaseStatementCondition::WildcardJustZ:
306 cond = moore::CaseZEqOp::create(builder, itemLoc, caseExpr, value);
308 case CaseStatementCondition::Inside:
309 mlir::emitError(loc,
"unsupported set membership case statement");
312 cond = moore::ToBuiltinBoolOp::create(builder, itemLoc, cond);
316 auto &nextBlock = createBlock();
317 mlir::cf::CondBranchOp::create(builder, 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 mlir::cf::BranchOp::create(builder, loc, &exitBlock);
338 const auto caseStmtAttrs = context.
compilation.getAttributes(caseStmt);
339 const bool hasFullCaseAttr =
340 llvm::find_if(caseStmtAttrs, [](
const AttributeSymbol *attr) {
341 return attr->name ==
"full_case";
342 }) != caseStmtAttrs.end();
353 auto twoStateExhaustive =
false;
354 if (
auto intType = dyn_cast<moore::IntType>(caseExpr.getType());
355 intType && intType.getWidth() < 32 &&
356 itemConsts.size() == (1 << intType.getWidth())) {
358 llvm::sort(itemConsts, [](
auto a,
auto b) {
359 return a.getValue().getRawValue().ult(b.getValue().getRawValue());
367 for (
auto value : itemConsts) {
368 if (value.getValue() != nextValue)
372 twoStateExhaustive = nextValue.isZero();
383 if ((twoStateExhaustive || (hasFullCaseAttr && !caseStmt.defaultCase)) &&
385 caseStmt.condition == CaseStatementCondition::Normal) {
386 mlir::cf::BranchOp::create(builder, loc, lastMatchBlock);
389 if (caseStmt.defaultCase)
393 mlir::cf::BranchOp::create(builder, loc, &exitBlock);
398 if (exitBlock.hasNoPredecessors()) {
402 builder.setInsertionPointToEnd(&exitBlock);
408 LogicalResult visit(
const slang::ast::ForLoopStatement &stmt) {
410 for (
auto *initExpr : stmt.initializers)
411 if (!context.convertRvalueExpression(*initExpr))
415 auto &exitBlock = createBlock();
416 auto &stepBlock = createBlock();
417 auto &bodyBlock = createBlock();
418 auto &checkBlock = createBlock();
419 cf::BranchOp::create(builder, loc, &checkBlock);
422 context.
loopStack.push_back({&stepBlock, &exitBlock});
423 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
426 builder.setInsertionPointToEnd(&checkBlock);
430 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
431 cond = moore::ToBuiltinBoolOp::create(builder, loc, cond);
432 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
435 builder.setInsertionPointToEnd(&bodyBlock);
439 cf::BranchOp::create(builder, loc, &stepBlock);
442 builder.setInsertionPointToEnd(&stepBlock);
443 for (
auto *stepExpr : stmt.steps)
444 if (!context.convertRvalueExpression(*stepExpr))
447 cf::BranchOp::create(builder, loc, &checkBlock);
451 if (exitBlock.hasNoPredecessors()) {
455 builder.setInsertionPointToEnd(&exitBlock);
460 LogicalResult visit(
const slang::ast::ForeachLoopStatement &stmt) {
461 for (uint32_t level = 0; level < stmt.loopDims.size(); level++) {
462 if (stmt.loopDims[level].loopVar)
463 return recursiveForeach(stmt, level);
469 LogicalResult visit(
const slang::ast::RepeatLoopStatement &stmt) {
475 auto &exitBlock = createBlock();
476 auto &stepBlock = createBlock();
477 auto &bodyBlock = createBlock();
478 auto &checkBlock = createBlock();
479 auto currentCount = checkBlock.addArgument(count.getType(), count.getLoc());
480 cf::BranchOp::create(builder, loc, &checkBlock, count);
483 context.
loopStack.push_back({&stepBlock, &exitBlock});
484 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
487 builder.setInsertionPointToEnd(&checkBlock);
488 auto cond = builder.createOrFold<moore::BoolCastOp>(loc, currentCount);
489 cond = moore::ToBuiltinBoolOp::create(builder, loc, cond);
490 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
493 builder.setInsertionPointToEnd(&bodyBlock);
497 cf::BranchOp::create(builder, loc, &stepBlock);
500 builder.setInsertionPointToEnd(&stepBlock);
501 auto one = moore::ConstantOp::create(
502 builder, count.getLoc(), cast<moore::IntType>(count.getType()), 1);
504 moore::SubOp::create(builder, count.getLoc(), currentCount, one);
505 cf::BranchOp::create(builder, loc, &checkBlock, nextCount);
509 if (exitBlock.hasNoPredecessors()) {
513 builder.setInsertionPointToEnd(&exitBlock);
519 LogicalResult createWhileLoop(
const slang::ast::Expression &condExpr,
520 const slang::ast::Statement &bodyStmt,
523 auto &exitBlock = createBlock();
524 auto &bodyBlock = createBlock();
525 auto &checkBlock = createBlock();
526 cf::BranchOp::create(builder, loc, atLeastOnce ? &bodyBlock : &checkBlock);
528 bodyBlock.moveBefore(&checkBlock);
531 context.
loopStack.push_back({&checkBlock, &exitBlock});
532 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
535 builder.setInsertionPointToEnd(&checkBlock);
539 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
540 cond = moore::ToBuiltinBoolOp::create(builder, loc, cond);
541 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
544 builder.setInsertionPointToEnd(&bodyBlock);
548 cf::BranchOp::create(builder, loc, &checkBlock);
552 if (exitBlock.hasNoPredecessors()) {
556 builder.setInsertionPointToEnd(&exitBlock);
561 LogicalResult visit(
const slang::ast::WhileLoopStatement &stmt) {
562 return createWhileLoop(stmt.cond, stmt.body,
false);
565 LogicalResult visit(
const slang::ast::DoWhileLoopStatement &stmt) {
566 return createWhileLoop(stmt.cond, stmt.body,
true);
570 LogicalResult visit(
const slang::ast::ForeverLoopStatement &stmt) {
572 auto &exitBlock = createBlock();
573 auto &bodyBlock = createBlock();
574 cf::BranchOp::create(builder, loc, &bodyBlock);
577 context.
loopStack.push_back({&bodyBlock, &exitBlock});
578 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
581 builder.setInsertionPointToEnd(&bodyBlock);
585 cf::BranchOp::create(builder, loc, &bodyBlock);
589 if (exitBlock.hasNoPredecessors()) {
593 builder.setInsertionPointToEnd(&exitBlock);
599 LogicalResult visit(
const slang::ast::TimedStatement &stmt) {
604 LogicalResult visit(
const slang::ast::ReturnStatement &stmt) {
609 mlir::func::ReturnOp::create(builder, loc, expr);
611 mlir::func::ReturnOp::create(builder, loc);
618 LogicalResult visit(
const slang::ast::ContinueStatement &stmt) {
620 return mlir::emitError(loc,
621 "cannot `continue` without a surrounding loop");
622 cf::BranchOp::create(builder, loc, context.
loopStack.back().continueBlock);
628 LogicalResult visit(
const slang::ast::BreakStatement &stmt) {
630 return mlir::emitError(loc,
"cannot `break` without a surrounding loop");
631 cf::BranchOp::create(builder, loc, context.
loopStack.back().breakBlock);
637 LogicalResult visit(
const slang::ast::ImmediateAssertionStatement &stmt) {
644 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
645 auto defer = moore::DeferAssert::Immediate;
647 defer = moore::DeferAssert::Final;
648 else if (stmt.isDeferred)
649 defer = moore::DeferAssert::Observed;
651 switch (stmt.assertionKind) {
652 case slang::ast::AssertionKind::Assert:
653 moore::AssertOp::create(builder, loc, defer, cond, StringAttr{});
655 case slang::ast::AssertionKind::Assume:
656 moore::AssumeOp::create(builder, loc, defer, cond, StringAttr{});
658 case slang::ast::AssertionKind::CoverProperty:
659 moore::CoverOp::create(builder, loc, defer, cond, StringAttr{});
664 mlir::emitError(loc) <<
"unsupported immediate assertion kind: "
665 << slang::ast::toString(stmt.assertionKind);
670 cond = moore::ToBuiltinBoolOp::create(builder, loc, cond);
673 Block &exitBlock = createBlock();
674 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
675 Block &trueBlock = createBlock();
676 cf::CondBranchOp::create(builder, loc, cond, &trueBlock,
677 falseBlock ? falseBlock : &exitBlock);
680 builder.setInsertionPointToEnd(&trueBlock);
684 cf::BranchOp::create(builder, loc, &exitBlock);
688 builder.setInsertionPointToEnd(falseBlock);
692 cf::BranchOp::create(builder, loc, &exitBlock);
697 if (exitBlock.hasNoPredecessors()) {
701 builder.setInsertionPointToEnd(&exitBlock);
707 LogicalResult visit(
const slang::ast::ConcurrentAssertionStatement &stmt) {
714 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
715 switch (stmt.assertionKind) {
716 case slang::ast::AssertionKind::Assert:
717 verif::AssertOp::create(builder, loc, property, Value(), StringAttr{});
719 case slang::ast::AssertionKind::Assume:
720 verif::AssumeOp::create(builder, loc, property, Value(), StringAttr{});
725 mlir::emitError(loc) <<
"unsupported concurrent assertion kind: "
726 << slang::ast::toString(stmt.assertionKind);
731 <<
"concurrent assertion statements with action blocks "
732 "are not supported yet";
740 visitSystemCall(
const slang::ast::ExpressionStatement &stmt,
741 const slang::ast::CallExpression &expr,
742 const slang::ast::CallExpression::SystemCallInfo &info) {
743 const auto &subroutine = *
info.subroutine;
744 auto args = expr.arguments();
748 if (subroutine.name ==
"$stop") {
749 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
750 moore::StopBIOp::create(builder, loc);
754 if (subroutine.name ==
"$finish") {
755 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
756 moore::FinishBIOp::create(builder, loc, 0);
757 moore::UnreachableOp::create(builder, loc);
762 if (subroutine.name ==
"$exit") {
772 bool isDisplay =
false;
773 bool appendNewline =
false;
774 StringRef remainingName = subroutine.name;
775 if (remainingName.consume_front(
"$display")) {
777 appendNewline =
true;
778 }
else if (remainingName.consume_front(
"$write")) {
783 using moore::IntFormat;
784 IntFormat defaultFormat = IntFormat::Decimal;
785 if (isDisplay && !remainingName.empty()) {
786 if (remainingName ==
"b")
787 defaultFormat = IntFormat::Binary;
788 else if (remainingName ==
"o")
789 defaultFormat = IntFormat::Octal;
790 else if (remainingName ==
"h")
791 defaultFormat = IntFormat::HexLower;
801 if (*message == Value{})
803 moore::DisplayBIOp::create(builder, loc, *message);
808 using moore::Severity;
809 std::optional<Severity> severity;
810 if (subroutine.name ==
"$info")
811 severity = Severity::Info;
812 else if (subroutine.name ==
"$warning")
813 severity = Severity::Warning;
814 else if (subroutine.name ==
"$error")
815 severity = Severity::Error;
816 else if (subroutine.name ==
"$fatal")
817 severity = Severity::Fatal;
821 const slang::ast::Expression *verbosityExpr =
nullptr;
822 if (severity == Severity::Fatal && args.size() >= 1) {
823 verbosityExpr = args[0];
824 args = args.subspan(1);
831 if (*message == Value{})
832 *message = moore::FormatLiteralOp::create(builder, loc,
"");
834 moore::SeverityBIOp::create(builder, loc, *severity, *message);
837 if (severity == Severity::Fatal) {
838 createFinishMessage(verbosityExpr);
839 moore::FinishBIOp::create(builder, loc, 1);
840 moore::UnreachableOp::create(builder, loc);
852 void createFinishMessage(
const slang::ast::Expression *verbosityExpr) {
853 unsigned verbosity = 1;
857 assert(value &&
"Slang guarantees constant verbosity parameter");
862 moore::FinishMessageBIOp::create(builder, loc, verbosity > 1);
866 template <
typename T>
867 LogicalResult visit(T &&stmt) {
868 mlir::emitError(loc,
"unsupported statement: ")
869 << slang::ast::toString(stmt.kind);
870 return mlir::failure();
873 LogicalResult visitInvalid(
const slang::ast::Statement &stmt) {
874 mlir::emitError(loc,
"invalid statement: ")
875 << slang::ast::toString(stmt.kind);
876 return mlir::failure();
881LogicalResult 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.
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.