CIRCT  20.0.0git
Statements.cpp
Go to the documentation of this file.
1 //===- Statements.cpp - Slang statement conversion ------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "slang/ast/SystemSubroutine.h"
11 #include "llvm/ADT/ScopeExit.h"
12 
13 using namespace mlir;
14 using namespace circt;
15 using namespace ImportVerilog;
16 
17 // NOLINTBEGIN(misc-no-recursion)
18 namespace {
19 struct StmtVisitor {
20  Context &context;
21  Location loc;
22  OpBuilder &builder;
23 
24  StmtVisitor(Context &context, Location loc)
25  : context(context), loc(loc), builder(context.builder) {}
26 
27  bool isTerminated() const { return !builder.getInsertionBlock(); }
28  void setTerminated() { builder.clearInsertionPoint(); }
29 
30  Block &createBlock() {
31  assert(builder.getInsertionBlock());
32  auto block = std::make_unique<Block>();
33  block->insertAfter(builder.getInsertionBlock());
34  return *block.release();
35  }
36 
37  // Skip empty statements (stray semicolons).
38  LogicalResult visit(const slang::ast::EmptyStatement &) { return success(); }
39 
40  // Convert every statement in a statement list. The Verilog syntax follows a
41  // similar philosophy as C/C++, where things like `if` and `for` accept a
42  // single statement as body. But then a `{...}` block is a valid statement,
43  // which allows for the `if {...}` syntax. In Verilog, things like `final`
44  // accept a single body statement, but that can be a `begin ... end` block,
45  // which in turn has a single body statement, which then commonly is a list of
46  // statements.
47  LogicalResult visit(const slang::ast::StatementList &stmts) {
48  for (auto *stmt : stmts.list) {
49  if (isTerminated()) {
50  auto loc = context.convertLocation(stmt->sourceRange);
51  mlir::emitWarning(loc, "unreachable code");
52  break;
53  }
54  if (failed(context.convertStatement(*stmt)))
55  return failure();
56  }
57  return success();
58  }
59 
60  // Inline `begin ... end` blocks into the parent.
61  LogicalResult visit(const slang::ast::BlockStatement &stmt) {
62  return context.convertStatement(stmt.body);
63  }
64 
65  // Handle expression statements.
66  LogicalResult visit(const slang::ast::ExpressionStatement &stmt) {
67  // Special handling for calls to system tasks that return no result value.
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>(
71  &call->subroutine)) {
72  auto handled = visitSystemCall(stmt, *call, *info);
73  if (failed(handled))
74  return failure();
75  if (handled == true)
76  return success();
77  }
78  }
79 
80  auto value = context.convertRvalueExpression(stmt.expr);
81  if (!value)
82  return failure();
83 
84  // Expressions like calls to void functions return a dummy value that has no
85  // uses. If the returned value is trivially dead, remove it.
86  if (auto *defOp = value.getDefiningOp())
87  if (isOpTriviallyDead(defOp))
88  defOp->erase();
89 
90  return success();
91  }
92 
93  // Handle variable declarations.
94  LogicalResult visit(const slang::ast::VariableDeclStatement &stmt) {
95  const auto &var = stmt.symbol;
96  auto type = context.convertType(*var.getDeclaredType());
97  if (!type)
98  return failure();
99 
100  Value initial;
101  if (const auto *init = var.getInitializer()) {
102  initial = context.convertRvalueExpression(*init);
103  if (!initial)
104  return failure();
105  }
106 
107  // Collect local temporary variables.
108  auto varOp = builder.create<moore::VariableOp>(
109  loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
110  builder.getStringAttr(var.name), initial);
111  context.valueSymbols.insertIntoScope(context.valueSymbols.getCurScope(),
112  &var, varOp);
113  return success();
114  }
115 
116  // Handle if statements.
117  LogicalResult visit(const slang::ast::ConditionalStatement &stmt) {
118  // Generate the condition. There may be multiple conditions linked with the
119  // `&&&` operator.
120  Value allConds;
121  for (const auto &condition : stmt.conditions) {
122  if (condition.pattern)
123  return mlir::emitError(loc,
124  "match patterns in if conditions not supported");
125  auto cond = context.convertRvalueExpression(*condition.expr);
126  if (!cond)
127  return failure();
128  cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
129  if (allConds)
130  allConds = builder.create<moore::AndOp>(loc, allConds, cond);
131  else
132  allConds = cond;
133  }
134  assert(allConds && "slang guarantees at least one condition");
135  allConds =
136  builder.create<moore::ConversionOp>(loc, builder.getI1Type(), allConds);
137 
138  // Create the blocks for the true and false branches, and the exit block.
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);
144 
145  // Generate the true branch.
146  builder.setInsertionPointToEnd(&trueBlock);
147  if (failed(context.convertStatement(stmt.ifTrue)))
148  return failure();
149  if (!isTerminated())
150  builder.create<cf::BranchOp>(loc, &exitBlock);
151 
152  // Generate the false branch if present.
153  if (stmt.ifFalse) {
154  builder.setInsertionPointToEnd(falseBlock);
155  if (failed(context.convertStatement(*stmt.ifFalse)))
156  return failure();
157  if (!isTerminated())
158  builder.create<cf::BranchOp>(loc, &exitBlock);
159  }
160 
161  // If control never reaches the exit block, remove it and mark control flow
162  // as terminated. Otherwise we continue inserting ops in the exit block.
163  if (exitBlock.hasNoPredecessors()) {
164  exitBlock.erase();
165  setTerminated();
166  } else {
167  builder.setInsertionPointToEnd(&exitBlock);
168  }
169  return success();
170  }
171 
172  // Handle case statements.
173  LogicalResult visit(const slang::ast::CaseStatement &caseStmt) {
174  using slang::ast::CaseStatementCondition;
175  auto caseExpr = context.convertRvalueExpression(caseStmt.expr);
176  if (!caseExpr)
177  return failure();
178 
179  // Check each case individually. This currently ignores the `unique`,
180  // `unique0`, and `priority` modifiers which would allow for additional
181  // optimizations.
182  auto &exitBlock = createBlock();
183 
184  for (const auto &item : caseStmt.items) {
185  // Create the block that will contain the main body of the expression.
186  // This is where any of the comparisons will branch to if they match.
187  auto &matchBlock = createBlock();
188 
189  // The SV standard requires expressions to be checked in the order
190  // specified by the user, and for the evaluation to stop as soon as the
191  // first matching expression is encountered.
192  for (const auto *expr : item.expressions) {
193  auto value = context.convertRvalueExpression(*expr);
194  if (!value)
195  return failure();
196  auto itemLoc = value.getLoc();
197 
198  // Generate the appropriate equality operator.
199  Value cond;
200  switch (caseStmt.condition) {
201  case CaseStatementCondition::Normal:
202  cond = builder.create<moore::CaseEqOp>(itemLoc, caseExpr, value);
203  break;
204  case CaseStatementCondition::WildcardXOrZ:
205  cond = builder.create<moore::CaseXZEqOp>(itemLoc, caseExpr, value);
206  break;
207  case CaseStatementCondition::WildcardJustZ:
208  cond = builder.create<moore::CaseZEqOp>(itemLoc, caseExpr, value);
209  break;
210  case CaseStatementCondition::Inside:
211  mlir::emitError(loc, "unsupported set membership case statement");
212  return failure();
213  }
214  cond = builder.create<moore::ConversionOp>(itemLoc, builder.getI1Type(),
215  cond);
216 
217  // If the condition matches, branch to the match block. Otherwise
218  // continue checking the next expression in a new block.
219  auto &nextBlock = createBlock();
220  builder.create<mlir::cf::CondBranchOp>(itemLoc, cond, &matchBlock,
221  &nextBlock);
222  builder.setInsertionPointToEnd(&nextBlock);
223  }
224 
225  // The current block is the fall-through after all conditions have been
226  // checked and nothing matched. Move the match block up before this point
227  // to make the IR easier to read.
228  matchBlock.moveBefore(builder.getInsertionBlock());
229 
230  // Generate the code for this item's statement in the match block.
231  OpBuilder::InsertionGuard guard(builder);
232  builder.setInsertionPointToEnd(&matchBlock);
233  if (failed(context.convertStatement(*item.stmt)))
234  return failure();
235  if (!isTerminated()) {
236  auto loc = context.convertLocation(item.stmt->sourceRange);
237  builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
238  }
239  }
240 
241  // Generate the default case if present.
242  if (caseStmt.defaultCase)
243  if (failed(context.convertStatement(*caseStmt.defaultCase)))
244  return failure();
245  if (!isTerminated())
246  builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
247 
248  // If control never reaches the exit block, remove it and mark control flow
249  // as terminated. Otherwise we continue inserting ops in the exit block.
250  if (exitBlock.hasNoPredecessors()) {
251  exitBlock.erase();
252  setTerminated();
253  } else {
254  builder.setInsertionPointToEnd(&exitBlock);
255  }
256  return success();
257  }
258 
259  // Handle `for` loops.
260  LogicalResult visit(const slang::ast::ForLoopStatement &stmt) {
261  // Generate the initializers.
262  for (auto *initExpr : stmt.initializers)
263  if (!context.convertRvalueExpression(*initExpr))
264  return failure();
265 
266  // Create the blocks for the loop condition, body, step, and exit.
267  auto &exitBlock = createBlock();
268  auto &stepBlock = createBlock();
269  auto &bodyBlock = createBlock();
270  auto &checkBlock = createBlock();
271  builder.create<cf::BranchOp>(loc, &checkBlock);
272 
273  // Push the blocks onto the loop stack such that we can continue and break.
274  context.loopStack.push_back({&stepBlock, &exitBlock});
275  auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
276 
277  // Generate the loop condition check.
278  builder.setInsertionPointToEnd(&checkBlock);
279  auto cond = context.convertRvalueExpression(*stmt.stopExpr);
280  if (!cond)
281  return failure();
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);
285 
286  // Generate the loop body.
287  builder.setInsertionPointToEnd(&bodyBlock);
288  if (failed(context.convertStatement(stmt.body)))
289  return failure();
290  if (!isTerminated())
291  builder.create<cf::BranchOp>(loc, &stepBlock);
292 
293  // Generate the step expressions.
294  builder.setInsertionPointToEnd(&stepBlock);
295  for (auto *stepExpr : stmt.steps)
296  if (!context.convertRvalueExpression(*stepExpr))
297  return failure();
298  if (!isTerminated())
299  builder.create<cf::BranchOp>(loc, &checkBlock);
300 
301  // If control never reaches the exit block, remove it and mark control flow
302  // as terminated. Otherwise we continue inserting ops in the exit block.
303  if (exitBlock.hasNoPredecessors()) {
304  exitBlock.erase();
305  setTerminated();
306  } else {
307  builder.setInsertionPointToEnd(&exitBlock);
308  }
309  return success();
310  }
311 
312  // Handle `repeat` loops.
313  LogicalResult visit(const slang::ast::RepeatLoopStatement &stmt) {
314  auto count = context.convertRvalueExpression(stmt.count);
315  if (!count)
316  return failure();
317 
318  // Create the blocks for the loop condition, body, step, and exit.
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);
325 
326  // Push the blocks onto the loop stack such that we can continue and break.
327  context.loopStack.push_back({&stepBlock, &exitBlock});
328  auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
329 
330  // Generate the loop condition check.
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);
335 
336  // Generate the loop body.
337  builder.setInsertionPointToEnd(&bodyBlock);
338  if (failed(context.convertStatement(stmt.body)))
339  return failure();
340  if (!isTerminated())
341  builder.create<cf::BranchOp>(loc, &stepBlock);
342 
343  // Decrement the current count and branch back to the check block.
344  builder.setInsertionPointToEnd(&stepBlock);
345  auto one = builder.create<moore::ConstantOp>(
346  count.getLoc(), cast<moore::IntType>(count.getType()), 1);
347  Value nextCount =
348  builder.create<moore::SubOp>(count.getLoc(), currentCount, one);
349  builder.create<cf::BranchOp>(loc, &checkBlock, nextCount);
350 
351  // If control never reaches the exit block, remove it and mark control flow
352  // as terminated. Otherwise we continue inserting ops in the exit block.
353  if (exitBlock.hasNoPredecessors()) {
354  exitBlock.erase();
355  setTerminated();
356  } else {
357  builder.setInsertionPointToEnd(&exitBlock);
358  }
359  return success();
360  }
361 
362  // Handle `while` and `do-while` loops.
363  LogicalResult createWhileLoop(const slang::ast::Expression &condExpr,
364  const slang::ast::Statement &bodyStmt,
365  bool atLeastOnce) {
366  // Create the blocks for the loop condition, body, and exit.
367  auto &exitBlock = createBlock();
368  auto &bodyBlock = createBlock();
369  auto &checkBlock = createBlock();
370  builder.create<cf::BranchOp>(loc, atLeastOnce ? &bodyBlock : &checkBlock);
371  if (atLeastOnce)
372  bodyBlock.moveBefore(&checkBlock);
373 
374  // Push the blocks onto the loop stack such that we can continue and break.
375  context.loopStack.push_back({&checkBlock, &exitBlock});
376  auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
377 
378  // Generate the loop condition check.
379  builder.setInsertionPointToEnd(&checkBlock);
380  auto cond = context.convertRvalueExpression(condExpr);
381  if (!cond)
382  return failure();
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);
386 
387  // Generate the loop body.
388  builder.setInsertionPointToEnd(&bodyBlock);
389  if (failed(context.convertStatement(bodyStmt)))
390  return failure();
391  if (!isTerminated())
392  builder.create<cf::BranchOp>(loc, &checkBlock);
393 
394  // If control never reaches the exit block, remove it and mark control flow
395  // as terminated. Otherwise we continue inserting ops in the exit block.
396  if (exitBlock.hasNoPredecessors()) {
397  exitBlock.erase();
398  setTerminated();
399  } else {
400  builder.setInsertionPointToEnd(&exitBlock);
401  }
402  return success();
403  }
404 
405  LogicalResult visit(const slang::ast::WhileLoopStatement &stmt) {
406  return createWhileLoop(stmt.cond, stmt.body, false);
407  }
408 
409  LogicalResult visit(const slang::ast::DoWhileLoopStatement &stmt) {
410  return createWhileLoop(stmt.cond, stmt.body, true);
411  }
412 
413  // Handle `forever` loops.
414  LogicalResult visit(const slang::ast::ForeverLoopStatement &stmt) {
415  // Create the blocks for the loop body and exit.
416  auto &exitBlock = createBlock();
417  auto &bodyBlock = createBlock();
418  builder.create<cf::BranchOp>(loc, &bodyBlock);
419 
420  // Push the blocks onto the loop stack such that we can continue and break.
421  context.loopStack.push_back({&bodyBlock, &exitBlock});
422  auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
423 
424  // Generate the loop body.
425  builder.setInsertionPointToEnd(&bodyBlock);
426  if (failed(context.convertStatement(stmt.body)))
427  return failure();
428  if (!isTerminated())
429  builder.create<cf::BranchOp>(loc, &bodyBlock);
430 
431  // If control never reaches the exit block, remove it and mark control flow
432  // as terminated. Otherwise we continue inserting ops in the exit block.
433  if (exitBlock.hasNoPredecessors()) {
434  exitBlock.erase();
435  setTerminated();
436  } else {
437  builder.setInsertionPointToEnd(&exitBlock);
438  }
439  return success();
440  }
441 
442  // Handle timing control.
443  LogicalResult visit(const slang::ast::TimedStatement &stmt) {
444  return context.convertTimingControl(stmt.timing, stmt.stmt);
445  }
446 
447  // Handle return statements.
448  LogicalResult visit(const slang::ast::ReturnStatement &stmt) {
449  if (stmt.expr) {
450  auto expr = context.convertRvalueExpression(*stmt.expr);
451  if (!expr)
452  return failure();
453  builder.create<mlir::func::ReturnOp>(loc, expr);
454  } else {
455  builder.create<mlir::func::ReturnOp>(loc);
456  }
457  setTerminated();
458  return success();
459  }
460 
461  // Handle continue statements.
462  LogicalResult visit(const slang::ast::ContinueStatement &stmt) {
463  if (context.loopStack.empty())
464  return mlir::emitError(loc,
465  "cannot `continue` without a surrounding loop");
466  builder.create<cf::BranchOp>(loc, context.loopStack.back().continueBlock);
467  setTerminated();
468  return success();
469  }
470 
471  // Handle break statements.
472  LogicalResult visit(const slang::ast::BreakStatement &stmt) {
473  if (context.loopStack.empty())
474  return mlir::emitError(loc, "cannot `break` without a surrounding loop");
475  builder.create<cf::BranchOp>(loc, context.loopStack.back().breakBlock);
476  setTerminated();
477  return success();
478  }
479 
480  // Handle immediate assertion statements.
481  LogicalResult visit(const slang::ast::ImmediateAssertionStatement &stmt) {
482  auto cond = context.convertRvalueExpression(stmt.cond);
483  cond = context.convertToBool(cond);
484  if (!cond)
485  return failure();
486 
487  // Handle assertion statements that don't have an action block.
488  if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
489  auto defer = moore::DeferAssert::Immediate;
490  if (stmt.isFinal)
491  defer = moore::DeferAssert::Final;
492  else if (stmt.isDeferred)
493  defer = moore::DeferAssert::Observed;
494 
495  switch (stmt.assertionKind) {
496  case slang::ast::AssertionKind::Assert:
497  builder.create<moore::AssertOp>(loc, defer, cond, StringAttr{});
498  return success();
499  case slang::ast::AssertionKind::Assume:
500  builder.create<moore::AssumeOp>(loc, defer, cond, StringAttr{});
501  return success();
502  case slang::ast::AssertionKind::CoverProperty:
503  builder.create<moore::CoverOp>(loc, defer, cond, StringAttr{});
504  return success();
505  default:
506  break;
507  }
508  mlir::emitError(loc) << "unsupported immediate assertion kind: "
509  << slang::ast::toString(stmt.assertionKind);
510  return failure();
511  }
512 
513  // Regard assertion statements with an action block as the "if-else".
514  cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
515 
516  // Create the blocks for the true and false branches, and the exit block.
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);
522 
523  // Generate the true branch.
524  builder.setInsertionPointToEnd(&trueBlock);
525  if (stmt.ifTrue && failed(context.convertStatement(*stmt.ifTrue)))
526  return failure();
527  if (!isTerminated())
528  builder.create<cf::BranchOp>(loc, &exitBlock);
529 
530  if (stmt.ifFalse) {
531  // Generate the false branch if present.
532  builder.setInsertionPointToEnd(falseBlock);
533  if (failed(context.convertStatement(*stmt.ifFalse)))
534  return failure();
535  if (!isTerminated())
536  builder.create<cf::BranchOp>(loc, &exitBlock);
537  }
538 
539  // If control never reaches the exit block, remove it and mark control flow
540  // as terminated. Otherwise we continue inserting ops in the exit block.
541  if (exitBlock.hasNoPredecessors()) {
542  exitBlock.erase();
543  setTerminated();
544  } else {
545  builder.setInsertionPointToEnd(&exitBlock);
546  }
547  return success();
548  }
549 
550  /// Handle the subset of system calls that return no result value. Return
551  /// true if the called system task could be handled, false otherwise. Return
552  /// failure if an error occurred.
553  FailureOr<bool>
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();
559 
560  // Simulation Control Tasks
561 
562  if (subroutine.name == "$stop") {
563  createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
564  builder.create<moore::StopBIOp>(loc);
565  return true;
566  }
567 
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);
572  setTerminated();
573  return true;
574  }
575 
576  if (subroutine.name == "$exit") {
577  // Calls to `$exit` from outside a `program` are ignored. Since we don't
578  // yet support programs, there is nothing to do here.
579  // TODO: Fix this once we support programs.
580  return true;
581  }
582 
583  // Display and Write Tasks (`$display[boh]?` or `$write[boh]?`)
584 
585  // Check for a `$display` or `$write` prefix.
586  bool isDisplay = false; // display or write
587  bool appendNewline = false; // display
588  StringRef remainingName = subroutine.name;
589  if (remainingName.consume_front("$display")) {
590  isDisplay = true;
591  appendNewline = true;
592  } else if (remainingName.consume_front("$write")) {
593  isDisplay = true;
594  }
595 
596  // Check for optional `b`, `o`, or `h` suffix indicating default format.
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;
606  else
607  isDisplay = false;
608  }
609 
610  if (isDisplay) {
611  auto message =
612  context.convertFormatString(args, loc, defaultFormat, appendNewline);
613  if (failed(message))
614  return failure();
615  if (*message == Value{})
616  return true;
617  builder.create<moore::DisplayBIOp>(loc, *message);
618  return true;
619  }
620 
621  // Severity Tasks
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;
632 
633  if (severity) {
634  // The `$fatal` task has an optional leading verbosity argument.
635  const slang::ast::Expression *verbosityExpr = nullptr;
636  if (severity == Severity::Fatal && args.size() >= 1) {
637  verbosityExpr = args[0];
638  args = args.subspan(1);
639  }
640 
641  // Handle the string formatting.
642  auto message = context.convertFormatString(args, loc);
643  if (failed(message))
644  return failure();
645  if (*message == Value{})
646  *message = builder.create<moore::FormatLiteralOp>(loc, "");
647 
648  builder.create<moore::SeverityBIOp>(loc, *severity, *message);
649 
650  // Handle the `$fatal` case which behaves like a `$finish`.
651  if (severity == Severity::Fatal) {
652  createFinishMessage(verbosityExpr);
653  builder.create<moore::FinishBIOp>(loc, 1);
654  builder.create<moore::UnreachableOp>(loc);
655  setTerminated();
656  }
657  return true;
658  }
659 
660  // Give up on any other system tasks. These will be tried again as an
661  // expression later.
662  return false;
663  }
664 
665  /// Create the optional diagnostic message print for finish-like ops.
666  void createFinishMessage(const slang::ast::Expression *verbosityExpr) {
667  unsigned verbosity = 1;
668  if (verbosityExpr) {
669  auto value =
670  context.evaluateConstant(*verbosityExpr).integer().as<unsigned>();
671  assert(value && "Slang guarantees constant verbosity parameter");
672  verbosity = *value;
673  }
674  if (verbosity == 0)
675  return;
676  builder.create<moore::FinishMessageBIOp>(loc, verbosity > 1);
677  }
678 
679  /// Emit an error for all other statements.
680  template <typename T>
681  LogicalResult visit(T &&stmt) {
682  mlir::emitError(loc, "unsupported statement: ")
683  << slang::ast::toString(stmt.kind);
684  return mlir::failure();
685  }
686 
687  LogicalResult visitInvalid(const slang::ast::Statement &stmt) {
688  mlir::emitError(loc, "invalid statement: ")
689  << slang::ast::toString(stmt.kind);
690  return mlir::failure();
691  }
692 };
693 } // namespace
694 
695 LogicalResult Context::convertStatement(const slang::ast::Statement &stmt) {
696  assert(builder.getInsertionBlock());
697  auto loc = convertLocation(stmt.sourceRange);
698  return stmt.visit(StmtVisitor(*this, loc));
699 }
700 // NOLINTEND(misc-no-recursion)
assert(baseType &&"element must be base type")
const char * toString(Flow flow)
Definition: FIRRTLOps.cpp:197
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.
Definition: HWVisitors.h:87
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
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.
Definition: Types.cpp:167
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...
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
LogicalResult convertStatement(const slang::ast::Statement &stmt)
Definition: Statements.cpp:695
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.