CIRCT 20.0.0git
Loading...
Searching...
No Matches
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
13using namespace mlir;
14using namespace circt;
15using namespace ImportVerilog;
16
17// NOLINTBEGIN(misc-no-recursion)
18namespace {
19struct 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 LogicalResult recursiveForeach(const slang::ast::ForeachLoopStatement &stmt,
38 uint32_t level) {
39 // find current dimension we are operate.
40 const auto &loopDim = stmt.loopDims[level];
41 if (!loopDim.range.has_value()) {
42 emitError(loc) << "dynamic loop variable is unsupported";
43 }
44 auto &exitBlock = createBlock();
45 auto &stepBlock = createBlock();
46 auto &bodyBlock = createBlock();
47 auto &checkBlock = createBlock();
48
49 // Push the blocks onto the loop stack such that we can continue and break.
50 context.loopStack.push_back({&stepBlock, &exitBlock});
51 auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
52
53 const auto &iter = loopDim.loopVar;
54 auto type = context.convertType(*iter->getDeclaredType());
55 if (!type)
56 return failure();
57
58 Value initial = builder.create<moore::ConstantOp>(
59 loc, cast<moore::IntType>(type), loopDim.range->lower());
60
61 // Create loop varirable in this dimension
62 Value varOp = builder.create<moore::VariableOp>(
63 loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
64 builder.getStringAttr(iter->name), initial);
65 context.valueSymbols.insertIntoScope(context.valueSymbols.getCurScope(),
66 iter, varOp);
67
68 builder.create<cf::BranchOp>(loc, &checkBlock);
69 builder.setInsertionPointToEnd(&checkBlock);
70
71 // When the loop variable is greater than the upper bound, goto exit
72 auto upperBound = builder.create<moore::ConstantOp>(
73 loc, cast<moore::IntType>(type), loopDim.range->upper());
74
75 auto var = builder.create<moore::ReadOp>(loc, varOp);
76 Value cond = builder.create<moore::SleOp>(loc, var, upperBound);
77 if (!cond)
78 return failure();
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);
82
83 builder.setInsertionPointToEnd(&bodyBlock);
84
85 // find next dimension in this foreach statement, it finded then recuersive
86 // resolve, else perform body statement
87 bool hasNext = false;
88 for (uint32_t nextLevel = level + 1; nextLevel < stmt.loopDims.size();
89 nextLevel++) {
90 if (stmt.loopDims[nextLevel].loopVar) {
91 if (failed(recursiveForeach(stmt, nextLevel)))
92 return failure();
93 hasNext = true;
94 break;
95 }
96 }
97
98 if (!hasNext) {
99 if (failed(context.convertStatement(stmt.body)))
100 return failure();
101 }
102 if (!isTerminated())
103 builder.create<cf::BranchOp>(loc, &stepBlock);
104
105 builder.setInsertionPointToEnd(&stepBlock);
106
107 // add one to loop variable
108 var = builder.create<moore::ReadOp>(loc, varOp);
109 auto one =
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);
114
115 if (exitBlock.hasNoPredecessors()) {
116 exitBlock.erase();
117 setTerminated();
118 } else {
119 builder.setInsertionPointToEnd(&exitBlock);
120 }
121 return success();
122 }
123
124 // Skip empty statements (stray semicolons).
125 LogicalResult visit(const slang::ast::EmptyStatement &) { return success(); }
126
127 // Convert every statement in a statement list. The Verilog syntax follows a
128 // similar philosophy as C/C++, where things like `if` and `for` accept a
129 // single statement as body. But then a `{...}` block is a valid statement,
130 // which allows for the `if {...}` syntax. In Verilog, things like `final`
131 // accept a single body statement, but that can be a `begin ... end` block,
132 // which in turn has a single body statement, which then commonly is a list of
133 // statements.
134 LogicalResult visit(const slang::ast::StatementList &stmts) {
135 for (auto *stmt : stmts.list) {
136 if (isTerminated()) {
137 auto loc = context.convertLocation(stmt->sourceRange);
138 mlir::emitWarning(loc, "unreachable code");
139 break;
140 }
141 if (failed(context.convertStatement(*stmt)))
142 return failure();
143 }
144 return success();
145 }
146
147 // Inline `begin ... end` blocks into the parent.
148 LogicalResult visit(const slang::ast::BlockStatement &stmt) {
149 return context.convertStatement(stmt.body);
150 }
151
152 // Handle expression statements.
153 LogicalResult visit(const slang::ast::ExpressionStatement &stmt) {
154 // Special handling for calls to system tasks that return no result value.
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);
160 if (failed(handled))
161 return failure();
162 if (handled == true)
163 return success();
164 }
165 }
166
167 auto value = context.convertRvalueExpression(stmt.expr);
168 if (!value)
169 return failure();
170
171 // Expressions like calls to void functions return a dummy value that has no
172 // uses. If the returned value is trivially dead, remove it.
173 if (auto *defOp = value.getDefiningOp())
174 if (isOpTriviallyDead(defOp))
175 defOp->erase();
176
177 return success();
178 }
179
180 // Handle variable declarations.
181 LogicalResult visit(const slang::ast::VariableDeclStatement &stmt) {
182 const auto &var = stmt.symbol;
183 auto type = context.convertType(*var.getDeclaredType());
184 if (!type)
185 return failure();
186
187 Value initial;
188 if (const auto *init = var.getInitializer()) {
189 initial = context.convertRvalueExpression(*init);
190 if (!initial)
191 return failure();
192 }
193
194 // Collect local temporary variables.
195 auto varOp = builder.create<moore::VariableOp>(
196 loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
197 builder.getStringAttr(var.name), initial);
198 context.valueSymbols.insertIntoScope(context.valueSymbols.getCurScope(),
199 &var, varOp);
200 return success();
201 }
202
203 // Handle if statements.
204 LogicalResult visit(const slang::ast::ConditionalStatement &stmt) {
205 // Generate the condition. There may be multiple conditions linked with the
206 // `&&&` operator.
207 Value allConds;
208 for (const auto &condition : stmt.conditions) {
209 if (condition.pattern)
210 return mlir::emitError(loc,
211 "match patterns in if conditions not supported");
212 auto cond = context.convertRvalueExpression(*condition.expr);
213 if (!cond)
214 return failure();
215 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
216 if (allConds)
217 allConds = builder.create<moore::AndOp>(loc, allConds, cond);
218 else
219 allConds = cond;
220 }
221 assert(allConds && "slang guarantees at least one condition");
222 allConds =
223 builder.create<moore::ConversionOp>(loc, builder.getI1Type(), allConds);
224
225 // Create the blocks for the true and false branches, and the exit block.
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);
231
232 // Generate the true branch.
233 builder.setInsertionPointToEnd(&trueBlock);
234 if (failed(context.convertStatement(stmt.ifTrue)))
235 return failure();
236 if (!isTerminated())
237 builder.create<cf::BranchOp>(loc, &exitBlock);
238
239 // Generate the false branch if present.
240 if (stmt.ifFalse) {
241 builder.setInsertionPointToEnd(falseBlock);
242 if (failed(context.convertStatement(*stmt.ifFalse)))
243 return failure();
244 if (!isTerminated())
245 builder.create<cf::BranchOp>(loc, &exitBlock);
246 }
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 case statements.
260 LogicalResult visit(const slang::ast::CaseStatement &caseStmt) {
261 using slang::ast::CaseStatementCondition;
262 auto caseExpr = context.convertRvalueExpression(caseStmt.expr);
263 if (!caseExpr)
264 return failure();
265
266 // Check each case individually. This currently ignores the `unique`,
267 // `unique0`, and `priority` modifiers which would allow for additional
268 // optimizations.
269 auto &exitBlock = createBlock();
270
271 for (const auto &item : caseStmt.items) {
272 // Create the block that will contain the main body of the expression.
273 // This is where any of the comparisons will branch to if they match.
274 auto &matchBlock = createBlock();
275
276 // The SV standard requires expressions to be checked in the order
277 // specified by the user, and for the evaluation to stop as soon as the
278 // first matching expression is encountered.
279 for (const auto *expr : item.expressions) {
280 auto value = context.convertRvalueExpression(*expr);
281 if (!value)
282 return failure();
283 auto itemLoc = value.getLoc();
284
285 // Generate the appropriate equality operator.
286 Value cond;
287 switch (caseStmt.condition) {
288 case CaseStatementCondition::Normal:
289 cond = builder.create<moore::CaseEqOp>(itemLoc, caseExpr, value);
290 break;
291 case CaseStatementCondition::WildcardXOrZ:
292 cond = builder.create<moore::CaseXZEqOp>(itemLoc, caseExpr, value);
293 break;
294 case CaseStatementCondition::WildcardJustZ:
295 cond = builder.create<moore::CaseZEqOp>(itemLoc, caseExpr, value);
296 break;
297 case CaseStatementCondition::Inside:
298 mlir::emitError(loc, "unsupported set membership case statement");
299 return failure();
300 }
301 cond = builder.create<moore::ConversionOp>(itemLoc, builder.getI1Type(),
302 cond);
303
304 // If the condition matches, branch to the match block. Otherwise
305 // continue checking the next expression in a new block.
306 auto &nextBlock = createBlock();
307 builder.create<mlir::cf::CondBranchOp>(itemLoc, cond, &matchBlock,
308 &nextBlock);
309 builder.setInsertionPointToEnd(&nextBlock);
310 }
311
312 // The current block is the fall-through after all conditions have been
313 // checked and nothing matched. Move the match block up before this point
314 // to make the IR easier to read.
315 matchBlock.moveBefore(builder.getInsertionBlock());
316
317 // Generate the code for this item's statement in the match block.
318 OpBuilder::InsertionGuard guard(builder);
319 builder.setInsertionPointToEnd(&matchBlock);
320 if (failed(context.convertStatement(*item.stmt)))
321 return failure();
322 if (!isTerminated()) {
323 auto loc = context.convertLocation(item.stmt->sourceRange);
324 builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
325 }
326 }
327
328 // Generate the default case if present.
329 if (caseStmt.defaultCase)
330 if (failed(context.convertStatement(*caseStmt.defaultCase)))
331 return failure();
332 if (!isTerminated())
333 builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
334
335 // If control never reaches the exit block, remove it and mark control flow
336 // as terminated. Otherwise we continue inserting ops in the exit block.
337 if (exitBlock.hasNoPredecessors()) {
338 exitBlock.erase();
339 setTerminated();
340 } else {
341 builder.setInsertionPointToEnd(&exitBlock);
342 }
343 return success();
344 }
345
346 // Handle `for` loops.
347 LogicalResult visit(const slang::ast::ForLoopStatement &stmt) {
348 // Generate the initializers.
349 for (auto *initExpr : stmt.initializers)
350 if (!context.convertRvalueExpression(*initExpr))
351 return failure();
352
353 // Create the blocks for the loop condition, body, step, and exit.
354 auto &exitBlock = createBlock();
355 auto &stepBlock = createBlock();
356 auto &bodyBlock = createBlock();
357 auto &checkBlock = createBlock();
358 builder.create<cf::BranchOp>(loc, &checkBlock);
359
360 // Push the blocks onto the loop stack such that we can continue and break.
361 context.loopStack.push_back({&stepBlock, &exitBlock});
362 auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
363
364 // Generate the loop condition check.
365 builder.setInsertionPointToEnd(&checkBlock);
366 auto cond = context.convertRvalueExpression(*stmt.stopExpr);
367 if (!cond)
368 return failure();
369 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
370 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
371 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
372
373 // Generate the loop body.
374 builder.setInsertionPointToEnd(&bodyBlock);
375 if (failed(context.convertStatement(stmt.body)))
376 return failure();
377 if (!isTerminated())
378 builder.create<cf::BranchOp>(loc, &stepBlock);
379
380 // Generate the step expressions.
381 builder.setInsertionPointToEnd(&stepBlock);
382 for (auto *stepExpr : stmt.steps)
383 if (!context.convertRvalueExpression(*stepExpr))
384 return failure();
385 if (!isTerminated())
386 builder.create<cf::BranchOp>(loc, &checkBlock);
387
388 // If control never reaches the exit block, remove it and mark control flow
389 // as terminated. Otherwise we continue inserting ops in the exit block.
390 if (exitBlock.hasNoPredecessors()) {
391 exitBlock.erase();
392 setTerminated();
393 } else {
394 builder.setInsertionPointToEnd(&exitBlock);
395 }
396 return success();
397 }
398
399 LogicalResult visit(const slang::ast::ForeachLoopStatement &stmt) {
400 for (uint32_t level = 0; level < stmt.loopDims.size(); level++) {
401 if (stmt.loopDims[level].loopVar)
402 return recursiveForeach(stmt, level);
403 }
404 return success();
405 }
406
407 // Handle `repeat` loops.
408 LogicalResult visit(const slang::ast::RepeatLoopStatement &stmt) {
409 auto count = context.convertRvalueExpression(stmt.count);
410 if (!count)
411 return failure();
412
413 // Create the blocks for the loop condition, body, step, and exit.
414 auto &exitBlock = createBlock();
415 auto &stepBlock = createBlock();
416 auto &bodyBlock = createBlock();
417 auto &checkBlock = createBlock();
418 auto currentCount = checkBlock.addArgument(count.getType(), count.getLoc());
419 builder.create<cf::BranchOp>(loc, &checkBlock, count);
420
421 // Push the blocks onto the loop stack such that we can continue and break.
422 context.loopStack.push_back({&stepBlock, &exitBlock});
423 auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
424
425 // Generate the loop condition check.
426 builder.setInsertionPointToEnd(&checkBlock);
427 auto cond = builder.createOrFold<moore::BoolCastOp>(loc, currentCount);
428 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
429 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
430
431 // Generate the loop body.
432 builder.setInsertionPointToEnd(&bodyBlock);
433 if (failed(context.convertStatement(stmt.body)))
434 return failure();
435 if (!isTerminated())
436 builder.create<cf::BranchOp>(loc, &stepBlock);
437
438 // Decrement the current count and branch back to the check block.
439 builder.setInsertionPointToEnd(&stepBlock);
440 auto one = builder.create<moore::ConstantOp>(
441 count.getLoc(), cast<moore::IntType>(count.getType()), 1);
442 Value nextCount =
443 builder.create<moore::SubOp>(count.getLoc(), currentCount, one);
444 builder.create<cf::BranchOp>(loc, &checkBlock, nextCount);
445
446 // If control never reaches the exit block, remove it and mark control flow
447 // as terminated. Otherwise we continue inserting ops in the exit block.
448 if (exitBlock.hasNoPredecessors()) {
449 exitBlock.erase();
450 setTerminated();
451 } else {
452 builder.setInsertionPointToEnd(&exitBlock);
453 }
454 return success();
455 }
456
457 // Handle `while` and `do-while` loops.
458 LogicalResult createWhileLoop(const slang::ast::Expression &condExpr,
459 const slang::ast::Statement &bodyStmt,
460 bool atLeastOnce) {
461 // Create the blocks for the loop condition, body, and exit.
462 auto &exitBlock = createBlock();
463 auto &bodyBlock = createBlock();
464 auto &checkBlock = createBlock();
465 builder.create<cf::BranchOp>(loc, atLeastOnce ? &bodyBlock : &checkBlock);
466 if (atLeastOnce)
467 bodyBlock.moveBefore(&checkBlock);
468
469 // Push the blocks onto the loop stack such that we can continue and break.
470 context.loopStack.push_back({&checkBlock, &exitBlock});
471 auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
472
473 // Generate the loop condition check.
474 builder.setInsertionPointToEnd(&checkBlock);
475 auto cond = context.convertRvalueExpression(condExpr);
476 if (!cond)
477 return failure();
478 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
479 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
480 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
481
482 // Generate the loop body.
483 builder.setInsertionPointToEnd(&bodyBlock);
484 if (failed(context.convertStatement(bodyStmt)))
485 return failure();
486 if (!isTerminated())
487 builder.create<cf::BranchOp>(loc, &checkBlock);
488
489 // If control never reaches the exit block, remove it and mark control flow
490 // as terminated. Otherwise we continue inserting ops in the exit block.
491 if (exitBlock.hasNoPredecessors()) {
492 exitBlock.erase();
493 setTerminated();
494 } else {
495 builder.setInsertionPointToEnd(&exitBlock);
496 }
497 return success();
498 }
499
500 LogicalResult visit(const slang::ast::WhileLoopStatement &stmt) {
501 return createWhileLoop(stmt.cond, stmt.body, false);
502 }
503
504 LogicalResult visit(const slang::ast::DoWhileLoopStatement &stmt) {
505 return createWhileLoop(stmt.cond, stmt.body, true);
506 }
507
508 // Handle `forever` loops.
509 LogicalResult visit(const slang::ast::ForeverLoopStatement &stmt) {
510 // Create the blocks for the loop body and exit.
511 auto &exitBlock = createBlock();
512 auto &bodyBlock = createBlock();
513 builder.create<cf::BranchOp>(loc, &bodyBlock);
514
515 // Push the blocks onto the loop stack such that we can continue and break.
516 context.loopStack.push_back({&bodyBlock, &exitBlock});
517 auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
518
519 // Generate the loop body.
520 builder.setInsertionPointToEnd(&bodyBlock);
521 if (failed(context.convertStatement(stmt.body)))
522 return failure();
523 if (!isTerminated())
524 builder.create<cf::BranchOp>(loc, &bodyBlock);
525
526 // If control never reaches the exit block, remove it and mark control flow
527 // as terminated. Otherwise we continue inserting ops in the exit block.
528 if (exitBlock.hasNoPredecessors()) {
529 exitBlock.erase();
530 setTerminated();
531 } else {
532 builder.setInsertionPointToEnd(&exitBlock);
533 }
534 return success();
535 }
536
537 // Handle timing control.
538 LogicalResult visit(const slang::ast::TimedStatement &stmt) {
539 return context.convertTimingControl(stmt.timing, stmt.stmt);
540 }
541
542 // Handle return statements.
543 LogicalResult visit(const slang::ast::ReturnStatement &stmt) {
544 if (stmt.expr) {
545 auto expr = context.convertRvalueExpression(*stmt.expr);
546 if (!expr)
547 return failure();
548 builder.create<mlir::func::ReturnOp>(loc, expr);
549 } else {
550 builder.create<mlir::func::ReturnOp>(loc);
551 }
552 setTerminated();
553 return success();
554 }
555
556 // Handle continue statements.
557 LogicalResult visit(const slang::ast::ContinueStatement &stmt) {
558 if (context.loopStack.empty())
559 return mlir::emitError(loc,
560 "cannot `continue` without a surrounding loop");
561 builder.create<cf::BranchOp>(loc, context.loopStack.back().continueBlock);
562 setTerminated();
563 return success();
564 }
565
566 // Handle break statements.
567 LogicalResult visit(const slang::ast::BreakStatement &stmt) {
568 if (context.loopStack.empty())
569 return mlir::emitError(loc, "cannot `break` without a surrounding loop");
570 builder.create<cf::BranchOp>(loc, context.loopStack.back().breakBlock);
571 setTerminated();
572 return success();
573 }
574
575 // Handle immediate assertion statements.
576 LogicalResult visit(const slang::ast::ImmediateAssertionStatement &stmt) {
577 auto cond = context.convertRvalueExpression(stmt.cond);
578 cond = context.convertToBool(cond);
579 if (!cond)
580 return failure();
581
582 // Handle assertion statements that don't have an action block.
583 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
584 auto defer = moore::DeferAssert::Immediate;
585 if (stmt.isFinal)
586 defer = moore::DeferAssert::Final;
587 else if (stmt.isDeferred)
588 defer = moore::DeferAssert::Observed;
589
590 switch (stmt.assertionKind) {
591 case slang::ast::AssertionKind::Assert:
592 builder.create<moore::AssertOp>(loc, defer, cond, StringAttr{});
593 return success();
594 case slang::ast::AssertionKind::Assume:
595 builder.create<moore::AssumeOp>(loc, defer, cond, StringAttr{});
596 return success();
597 case slang::ast::AssertionKind::CoverProperty:
598 builder.create<moore::CoverOp>(loc, defer, cond, StringAttr{});
599 return success();
600 default:
601 break;
602 }
603 mlir::emitError(loc) << "unsupported immediate assertion kind: "
604 << slang::ast::toString(stmt.assertionKind);
605 return failure();
606 }
607
608 // Regard assertion statements with an action block as the "if-else".
609 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
610
611 // Create the blocks for the true and false branches, and the exit block.
612 Block &exitBlock = createBlock();
613 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
614 Block &trueBlock = createBlock();
615 builder.create<cf::CondBranchOp>(loc, cond, &trueBlock,
616 falseBlock ? falseBlock : &exitBlock);
617
618 // Generate the true branch.
619 builder.setInsertionPointToEnd(&trueBlock);
620 if (stmt.ifTrue && failed(context.convertStatement(*stmt.ifTrue)))
621 return failure();
622 if (!isTerminated())
623 builder.create<cf::BranchOp>(loc, &exitBlock);
624
625 if (stmt.ifFalse) {
626 // Generate the false branch if present.
627 builder.setInsertionPointToEnd(falseBlock);
628 if (failed(context.convertStatement(*stmt.ifFalse)))
629 return failure();
630 if (!isTerminated())
631 builder.create<cf::BranchOp>(loc, &exitBlock);
632 }
633
634 // If control never reaches the exit block, remove it and mark control flow
635 // as terminated. Otherwise we continue inserting ops in the exit block.
636 if (exitBlock.hasNoPredecessors()) {
637 exitBlock.erase();
638 setTerminated();
639 } else {
640 builder.setInsertionPointToEnd(&exitBlock);
641 }
642 return success();
643 }
644
645 /// Handle the subset of system calls that return no result value. Return
646 /// true if the called system task could be handled, false otherwise. Return
647 /// failure if an error occurred.
648 FailureOr<bool>
649 visitSystemCall(const slang::ast::ExpressionStatement &stmt,
650 const slang::ast::CallExpression &expr,
651 const slang::ast::CallExpression::SystemCallInfo &info) {
652 const auto &subroutine = *info.subroutine;
653 auto args = expr.arguments();
654
655 // Simulation Control Tasks
656
657 if (subroutine.name == "$stop") {
658 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
659 builder.create<moore::StopBIOp>(loc);
660 return true;
661 }
662
663 if (subroutine.name == "$finish") {
664 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
665 builder.create<moore::FinishBIOp>(loc, 0);
666 builder.create<moore::UnreachableOp>(loc);
667 setTerminated();
668 return true;
669 }
670
671 if (subroutine.name == "$exit") {
672 // Calls to `$exit` from outside a `program` are ignored. Since we don't
673 // yet support programs, there is nothing to do here.
674 // TODO: Fix this once we support programs.
675 return true;
676 }
677
678 // Display and Write Tasks (`$display[boh]?` or `$write[boh]?`)
679
680 // Check for a `$display` or `$write` prefix.
681 bool isDisplay = false; // display or write
682 bool appendNewline = false; // display
683 StringRef remainingName = subroutine.name;
684 if (remainingName.consume_front("$display")) {
685 isDisplay = true;
686 appendNewline = true;
687 } else if (remainingName.consume_front("$write")) {
688 isDisplay = true;
689 }
690
691 // Check for optional `b`, `o`, or `h` suffix indicating default format.
692 using moore::IntFormat;
693 IntFormat defaultFormat = IntFormat::Decimal;
694 if (isDisplay && !remainingName.empty()) {
695 if (remainingName == "b")
696 defaultFormat = IntFormat::Binary;
697 else if (remainingName == "o")
698 defaultFormat = IntFormat::Octal;
699 else if (remainingName == "h")
700 defaultFormat = IntFormat::HexLower;
701 else
702 isDisplay = false;
703 }
704
705 if (isDisplay) {
706 auto message =
707 context.convertFormatString(args, loc, defaultFormat, appendNewline);
708 if (failed(message))
709 return failure();
710 if (*message == Value{})
711 return true;
712 builder.create<moore::DisplayBIOp>(loc, *message);
713 return true;
714 }
715
716 // Severity Tasks
717 using moore::Severity;
718 std::optional<Severity> severity;
719 if (subroutine.name == "$info")
720 severity = Severity::Info;
721 else if (subroutine.name == "$warning")
722 severity = Severity::Warning;
723 else if (subroutine.name == "$error")
724 severity = Severity::Error;
725 else if (subroutine.name == "$fatal")
726 severity = Severity::Fatal;
727
728 if (severity) {
729 // The `$fatal` task has an optional leading verbosity argument.
730 const slang::ast::Expression *verbosityExpr = nullptr;
731 if (severity == Severity::Fatal && args.size() >= 1) {
732 verbosityExpr = args[0];
733 args = args.subspan(1);
734 }
735
736 // Handle the string formatting.
737 auto message = context.convertFormatString(args, loc);
738 if (failed(message))
739 return failure();
740 if (*message == Value{})
741 *message = builder.create<moore::FormatLiteralOp>(loc, "");
742
743 builder.create<moore::SeverityBIOp>(loc, *severity, *message);
744
745 // Handle the `$fatal` case which behaves like a `$finish`.
746 if (severity == Severity::Fatal) {
747 createFinishMessage(verbosityExpr);
748 builder.create<moore::FinishBIOp>(loc, 1);
749 builder.create<moore::UnreachableOp>(loc);
750 setTerminated();
751 }
752 return true;
753 }
754
755 // Give up on any other system tasks. These will be tried again as an
756 // expression later.
757 return false;
758 }
759
760 /// Create the optional diagnostic message print for finish-like ops.
761 void createFinishMessage(const slang::ast::Expression *verbosityExpr) {
762 unsigned verbosity = 1;
763 if (verbosityExpr) {
764 auto value =
765 context.evaluateConstant(*verbosityExpr).integer().as<unsigned>();
766 assert(value && "Slang guarantees constant verbosity parameter");
767 verbosity = *value;
768 }
769 if (verbosity == 0)
770 return;
771 builder.create<moore::FinishMessageBIOp>(loc, verbosity > 1);
772 }
773
774 /// Emit an error for all other statements.
775 template <typename T>
776 LogicalResult visit(T &&stmt) {
777 mlir::emitError(loc, "unsupported statement: ")
778 << slang::ast::toString(stmt.kind);
779 return mlir::failure();
780 }
781
782 LogicalResult visitInvalid(const slang::ast::Statement &stmt) {
783 mlir::emitError(loc, "invalid statement: ")
784 << slang::ast::toString(stmt.kind);
785 return mlir::failure();
786 }
787};
788} // namespace
789
790LogicalResult Context::convertStatement(const slang::ast::Statement &stmt) {
791 assert(builder.getInsertionBlock());
792 auto loc = convertLocation(stmt.sourceRange);
793 return stmt.visit(StmtVisitor(*this, loc));
794}
795// NOLINTEND(misc-no-recursion)
assert(baseType &&"element must be base type")
This helps visit TypeOp nodes.
Definition HWVisitors.h:87
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.
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)
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.