10 #include "slang/ast/SystemSubroutine.h"
11 #include "llvm/ADT/ScopeExit.h"
14 using namespace circt;
15 using 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();
38 LogicalResult visit(
const slang::ast::EmptyStatement &) {
return success(); }
47 LogicalResult visit(
const slang::ast::StatementList &stmts) {
48 for (
auto *stmt : stmts.list) {
51 mlir::emitWarning(loc,
"unreachable code");
61 LogicalResult visit(
const slang::ast::BlockStatement &stmt) {
66 LogicalResult visit(
const slang::ast::ExpressionStatement &stmt) {
68 if (
const auto *call = stmt.expr.as_if<slang::ast::CallExpression>()) {
69 if (
const auto *info =
70 std::get_if<slang::ast::CallExpression::SystemCallInfo>(
72 auto handled = visitSystemCall(stmt, *call, *info);
86 if (
auto *defOp = value.getDefiningOp())
87 if (isOpTriviallyDead(defOp))
94 LogicalResult visit(
const slang::ast::VariableDeclStatement &stmt) {
95 const auto &var = stmt.symbol;
96 auto type = context.
convertType(*var.getDeclaredType());
101 if (
const auto *init = var.getInitializer()) {
108 auto varOp = builder.create<moore::VariableOp>(
110 builder.getStringAttr(var.name), initial);
117 LogicalResult visit(
const slang::ast::ConditionalStatement &stmt) {
121 for (
const auto &condition : stmt.conditions) {
122 if (condition.pattern)
123 return mlir::emitError(loc,
124 "match patterns in if conditions not supported");
128 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
130 allConds = builder.create<moore::AndOp>(loc, allConds, cond);
134 assert(allConds &&
"slang guarantees at least one condition");
136 builder.create<moore::ConversionOp>(loc, builder.getI1Type(), allConds);
139 Block &exitBlock = createBlock();
140 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
141 Block &trueBlock = createBlock();
142 builder.create<cf::CondBranchOp>(loc, allConds, &trueBlock,
143 falseBlock ? falseBlock : &exitBlock);
146 builder.setInsertionPointToEnd(&trueBlock);
150 builder.create<cf::BranchOp>(loc, &exitBlock);
154 builder.setInsertionPointToEnd(falseBlock);
158 builder.create<cf::BranchOp>(loc, &exitBlock);
163 if (exitBlock.hasNoPredecessors()) {
167 builder.setInsertionPointToEnd(&exitBlock);
173 LogicalResult visit(
const slang::ast::CaseStatement &caseStmt) {
174 using slang::ast::CaseStatementCondition;
182 auto &exitBlock = createBlock();
184 for (
const auto &item : caseStmt.items) {
187 auto &matchBlock = createBlock();
192 for (
const auto *expr : item.expressions) {
196 auto itemLoc = value.getLoc();
200 switch (caseStmt.condition) {
201 case CaseStatementCondition::Normal:
202 cond = builder.create<moore::CaseEqOp>(itemLoc, caseExpr, value);
204 case CaseStatementCondition::WildcardXOrZ:
205 cond = builder.create<moore::CaseXZEqOp>(itemLoc, caseExpr, value);
207 case CaseStatementCondition::WildcardJustZ:
208 cond = builder.create<moore::CaseZEqOp>(itemLoc, caseExpr, value);
210 case CaseStatementCondition::Inside:
211 mlir::emitError(loc,
"unsupported set membership case statement");
214 cond = builder.create<moore::ConversionOp>(itemLoc, builder.getI1Type(),
219 auto &nextBlock = createBlock();
220 builder.create<mlir::cf::CondBranchOp>(itemLoc, cond, &matchBlock,
222 builder.setInsertionPointToEnd(&nextBlock);
228 matchBlock.moveBefore(builder.getInsertionBlock());
231 OpBuilder::InsertionGuard guard(builder);
232 builder.setInsertionPointToEnd(&matchBlock);
235 if (!isTerminated()) {
237 builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
242 if (caseStmt.defaultCase)
246 builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
250 if (exitBlock.hasNoPredecessors()) {
254 builder.setInsertionPointToEnd(&exitBlock);
260 LogicalResult visit(
const slang::ast::ForLoopStatement &stmt) {
262 for (
auto *initExpr : stmt.initializers)
267 auto &exitBlock = createBlock();
268 auto &stepBlock = createBlock();
269 auto &bodyBlock = createBlock();
270 auto &checkBlock = createBlock();
271 builder.create<cf::BranchOp>(loc, &checkBlock);
274 context.
loopStack.push_back({&stepBlock, &exitBlock});
275 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
278 builder.setInsertionPointToEnd(&checkBlock);
282 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
283 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
284 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
287 builder.setInsertionPointToEnd(&bodyBlock);
291 builder.create<cf::BranchOp>(loc, &stepBlock);
294 builder.setInsertionPointToEnd(&stepBlock);
295 for (
auto *stepExpr : stmt.steps)
299 builder.create<cf::BranchOp>(loc, &checkBlock);
303 if (exitBlock.hasNoPredecessors()) {
307 builder.setInsertionPointToEnd(&exitBlock);
313 LogicalResult visit(
const slang::ast::RepeatLoopStatement &stmt) {
319 auto &exitBlock = createBlock();
320 auto &stepBlock = createBlock();
321 auto &bodyBlock = createBlock();
322 auto &checkBlock = createBlock();
323 auto currentCount = checkBlock.addArgument(count.getType(), count.getLoc());
324 builder.create<cf::BranchOp>(loc, &checkBlock, count);
327 context.
loopStack.push_back({&stepBlock, &exitBlock});
328 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
331 builder.setInsertionPointToEnd(&checkBlock);
332 auto cond = builder.createOrFold<moore::BoolCastOp>(loc, currentCount);
333 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
334 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
337 builder.setInsertionPointToEnd(&bodyBlock);
341 builder.create<cf::BranchOp>(loc, &stepBlock);
344 builder.setInsertionPointToEnd(&stepBlock);
345 auto one = builder.create<moore::ConstantOp>(
346 count.getLoc(), cast<moore::IntType>(count.getType()), 1);
348 builder.create<moore::SubOp>(count.getLoc(), currentCount, one);
349 builder.create<cf::BranchOp>(loc, &checkBlock, nextCount);
353 if (exitBlock.hasNoPredecessors()) {
357 builder.setInsertionPointToEnd(&exitBlock);
363 LogicalResult createWhileLoop(
const slang::ast::Expression &condExpr,
364 const slang::ast::Statement &bodyStmt,
367 auto &exitBlock = createBlock();
368 auto &bodyBlock = createBlock();
369 auto &checkBlock = createBlock();
370 builder.create<cf::BranchOp>(loc, atLeastOnce ? &bodyBlock : &checkBlock);
372 bodyBlock.moveBefore(&checkBlock);
375 context.
loopStack.push_back({&checkBlock, &exitBlock});
376 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
379 builder.setInsertionPointToEnd(&checkBlock);
383 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
384 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
385 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
388 builder.setInsertionPointToEnd(&bodyBlock);
392 builder.create<cf::BranchOp>(loc, &checkBlock);
396 if (exitBlock.hasNoPredecessors()) {
400 builder.setInsertionPointToEnd(&exitBlock);
405 LogicalResult visit(
const slang::ast::WhileLoopStatement &stmt) {
406 return createWhileLoop(stmt.cond, stmt.body,
false);
409 LogicalResult visit(
const slang::ast::DoWhileLoopStatement &stmt) {
410 return createWhileLoop(stmt.cond, stmt.body,
true);
414 LogicalResult visit(
const slang::ast::ForeverLoopStatement &stmt) {
416 auto &exitBlock = createBlock();
417 auto &bodyBlock = createBlock();
418 builder.create<cf::BranchOp>(loc, &bodyBlock);
421 context.
loopStack.push_back({&bodyBlock, &exitBlock});
422 auto done = llvm::make_scope_exit([&] { context.
loopStack.pop_back(); });
425 builder.setInsertionPointToEnd(&bodyBlock);
429 builder.create<cf::BranchOp>(loc, &bodyBlock);
433 if (exitBlock.hasNoPredecessors()) {
437 builder.setInsertionPointToEnd(&exitBlock);
443 LogicalResult visit(
const slang::ast::TimedStatement &stmt) {
448 LogicalResult visit(
const slang::ast::ReturnStatement &stmt) {
453 builder.create<mlir::func::ReturnOp>(loc, expr);
455 builder.create<mlir::func::ReturnOp>(loc);
462 LogicalResult visit(
const slang::ast::ContinueStatement &stmt) {
464 return mlir::emitError(loc,
465 "cannot `continue` without a surrounding loop");
466 builder.create<cf::BranchOp>(loc, context.
loopStack.back().continueBlock);
472 LogicalResult visit(
const slang::ast::BreakStatement &stmt) {
474 return mlir::emitError(loc,
"cannot `break` without a surrounding loop");
475 builder.create<cf::BranchOp>(loc, context.
loopStack.back().breakBlock);
481 LogicalResult visit(
const slang::ast::ImmediateAssertionStatement &stmt) {
488 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
489 auto defer = moore::DeferAssert::Immediate;
491 defer = moore::DeferAssert::Final;
492 else if (stmt.isDeferred)
493 defer = moore::DeferAssert::Observed;
495 switch (stmt.assertionKind) {
496 case slang::ast::AssertionKind::Assert:
497 builder.create<moore::AssertOp>(loc, defer, cond, StringAttr{});
499 case slang::ast::AssertionKind::Assume:
500 builder.create<moore::AssumeOp>(loc, defer, cond, StringAttr{});
502 case slang::ast::AssertionKind::CoverProperty:
503 builder.create<moore::CoverOp>(loc, defer, cond, StringAttr{});
508 mlir::emitError(loc) <<
"unsupported immediate assertion kind: "
514 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
517 Block &exitBlock = createBlock();
518 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
519 Block &trueBlock = createBlock();
520 builder.create<cf::CondBranchOp>(loc, cond, &trueBlock,
521 falseBlock ? falseBlock : &exitBlock);
524 builder.setInsertionPointToEnd(&trueBlock);
528 builder.create<cf::BranchOp>(loc, &exitBlock);
532 builder.setInsertionPointToEnd(falseBlock);
536 builder.create<cf::BranchOp>(loc, &exitBlock);
541 if (exitBlock.hasNoPredecessors()) {
545 builder.setInsertionPointToEnd(&exitBlock);
554 visitSystemCall(
const slang::ast::ExpressionStatement &stmt,
555 const slang::ast::CallExpression &expr,
556 const slang::ast::CallExpression::SystemCallInfo &info) {
557 const auto &subroutine = *info.subroutine;
558 auto args = expr.arguments();
562 if (subroutine.name ==
"$stop") {
563 createFinishMessage(args.size() >= 1 ? args[0] :
nullptr);
564 builder.create<moore::StopBIOp>(loc);
568 if (subroutine.name ==
"$finish") {
569 createFinishMessage(args.size() >= 1 ? args[0] :
nullptr);
570 builder.create<moore::FinishBIOp>(loc, 0);
571 builder.create<moore::UnreachableOp>(loc);
576 if (subroutine.name ==
"$exit") {
586 bool isDisplay =
false;
587 bool appendNewline =
false;
588 StringRef remainingName = subroutine.name;
589 if (remainingName.consume_front(
"$display")) {
591 appendNewline =
true;
592 }
else if (remainingName.consume_front(
"$write")) {
597 using moore::IntFormat;
598 IntFormat defaultFormat = IntFormat::Decimal;
599 if (isDisplay && !remainingName.empty()) {
600 if (remainingName ==
"b")
601 defaultFormat = IntFormat::Binary;
602 else if (remainingName ==
"o")
603 defaultFormat = IntFormat::Octal;
604 else if (remainingName ==
"h")
605 defaultFormat = IntFormat::HexLower;
615 if (*message == Value{})
617 builder.create<moore::DisplayBIOp>(loc, *message);
622 using moore::Severity;
623 std::optional<Severity> severity;
624 if (subroutine.name ==
"$info")
625 severity = Severity::Info;
626 else if (subroutine.name ==
"$warning")
627 severity = Severity::Warning;
628 else if (subroutine.name ==
"$error")
629 severity = Severity::Error;
630 else if (subroutine.name ==
"$fatal")
631 severity = Severity::Fatal;
635 const slang::ast::Expression *verbosityExpr =
nullptr;
636 if (severity == Severity::Fatal && args.size() >= 1) {
637 verbosityExpr = args[0];
638 args = args.subspan(1);
645 if (*message == Value{})
646 *message = builder.create<moore::FormatLiteralOp>(loc,
"");
648 builder.create<moore::SeverityBIOp>(loc, *severity, *message);
651 if (severity == Severity::Fatal) {
652 createFinishMessage(verbosityExpr);
653 builder.create<moore::FinishBIOp>(loc, 1);
654 builder.create<moore::UnreachableOp>(loc);
666 void createFinishMessage(
const slang::ast::Expression *verbosityExpr) {
667 unsigned verbosity = 1;
671 assert(value &&
"Slang guarantees constant verbosity parameter");
676 builder.create<moore::FinishMessageBIOp>(loc, verbosity > 1);
680 template <
typename T>
681 LogicalResult visit(T &&stmt) {
682 mlir::emitError(loc,
"unsupported statement: ")
684 return mlir::failure();
687 LogicalResult visitInvalid(
const slang::ast::Statement &stmt) {
688 mlir::emitError(loc,
"invalid statement: ")
690 return mlir::failure();
695 LogicalResult Context::convertStatement(
const slang::ast::Statement &stmt) {
696 assert(builder.getInsertionBlock());
assert(baseType &&"element must be base type")
const char * toString(Flow flow)
static Location convertLocation(MLIRContext *context, const slang::SourceManager &sourceManager, SmallDenseMap< slang::BufferID, StringRef > &bufferFilePaths, slang::SourceLocation loc)
Convert a slang SourceLocation to an MLIR Location.
This helps visit TypeOp nodes.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
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)
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.