Loading [MathJax]/extensions/tex2jax.js
CIRCT 21.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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 Block *lastMatchBlock = nullptr;
271 SmallVector<moore::FVIntegerAttr> itemConsts;
272
273 for (const auto &item : caseStmt.items) {
274 // Create the block that will contain the main body of the expression.
275 // This is where any of the comparisons will branch to if they match.
276 auto &matchBlock = createBlock();
277 lastMatchBlock = &matchBlock;
278
279 // The SV standard requires expressions to be checked in the order
280 // specified by the user, and for the evaluation to stop as soon as the
281 // first matching expression is encountered.
282 for (const auto *expr : item.expressions) {
283 auto value = context.convertRvalueExpression(*expr);
284 if (!value)
285 return failure();
286 auto itemLoc = value.getLoc();
287
288 // Take note if the expression is a constant.
289 auto maybeConst = value;
290 if (auto defOp = maybeConst.getDefiningOp<moore::ConversionOp>())
291 maybeConst = defOp.getInput();
292 if (auto defOp = maybeConst.getDefiningOp<moore::ConstantOp>())
293 itemConsts.push_back(defOp.getValueAttr());
294
295 // Generate the appropriate equality operator.
296 Value cond;
297 switch (caseStmt.condition) {
298 case CaseStatementCondition::Normal:
299 cond = builder.create<moore::CaseEqOp>(itemLoc, caseExpr, value);
300 break;
301 case CaseStatementCondition::WildcardXOrZ:
302 cond = builder.create<moore::CaseXZEqOp>(itemLoc, caseExpr, value);
303 break;
304 case CaseStatementCondition::WildcardJustZ:
305 cond = builder.create<moore::CaseZEqOp>(itemLoc, caseExpr, value);
306 break;
307 case CaseStatementCondition::Inside:
308 mlir::emitError(loc, "unsupported set membership case statement");
309 return failure();
310 }
311 cond = builder.create<moore::ConversionOp>(itemLoc, builder.getI1Type(),
312 cond);
313
314 // If the condition matches, branch to the match block. Otherwise
315 // continue checking the next expression in a new block.
316 auto &nextBlock = createBlock();
317 builder.create<mlir::cf::CondBranchOp>(itemLoc, cond, &matchBlock,
318 &nextBlock);
319 builder.setInsertionPointToEnd(&nextBlock);
320 }
321
322 // The current block is the fall-through after all conditions have been
323 // checked and nothing matched. Move the match block up before this point
324 // to make the IR easier to read.
325 matchBlock.moveBefore(builder.getInsertionBlock());
326
327 // Generate the code for this item's statement in the match block.
328 OpBuilder::InsertionGuard guard(builder);
329 builder.setInsertionPointToEnd(&matchBlock);
330 if (failed(context.convertStatement(*item.stmt)))
331 return failure();
332 if (!isTerminated()) {
333 auto loc = context.convertLocation(item.stmt->sourceRange);
334 builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
335 }
336 }
337
338 // Check if the case statement looks exhaustive assuming two-state values.
339 // We use this information to work around a common bug in input Verilog
340 // where a case statement enumerates all possible two-state values of the
341 // case expression, but forgets to deal with cases involving X and Z bits in
342 // the input.
343 //
344 // Once the core dialects start supporting four-state values we may want to
345 // tuck this behind an import option that is on by default, since it does
346 // not preserve semantics.
347 auto twoStateExhaustive = false;
348 if (auto intType = dyn_cast<moore::IntType>(caseExpr.getType());
349 intType && intType.getWidth() < 32 &&
350 itemConsts.size() == (1 << intType.getWidth())) {
351 // Sort the constants by value.
352 llvm::sort(itemConsts, [](auto a, auto b) {
353 return a.getValue().getRawValue().ult(b.getValue().getRawValue());
354 });
355
356 // Ensure that every possible value of the case expression is present. Do
357 // this by starting at 0 and iterating over all sorted items. Each item
358 // must be the previous item + 1. At the end, the addition must exactly
359 // overflow and take us back to zero.
360 auto nextValue = FVInt::getZero(intType.getWidth());
361 for (auto value : itemConsts) {
362 if (value.getValue() != nextValue)
363 break;
364 nextValue += 1;
365 }
366 twoStateExhaustive = nextValue.isZero();
367 }
368
369 // If the case statement is exhaustive assuming two-state values, don't
370 // generate the default case. Instead, branch to the last match block. This
371 // will essentially make the last case item the "default".
372 if (twoStateExhaustive && lastMatchBlock &&
373 caseStmt.condition == CaseStatementCondition::Normal) {
374 builder.create<mlir::cf::BranchOp>(loc, lastMatchBlock);
375 } else {
376 // Generate the default case if present.
377 if (caseStmt.defaultCase)
378 if (failed(context.convertStatement(*caseStmt.defaultCase)))
379 return failure();
380 if (!isTerminated())
381 builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
382 }
383
384 // If control never reaches the exit block, remove it and mark control flow
385 // as terminated. Otherwise we continue inserting ops in the exit block.
386 if (exitBlock.hasNoPredecessors()) {
387 exitBlock.erase();
388 setTerminated();
389 } else {
390 builder.setInsertionPointToEnd(&exitBlock);
391 }
392 return success();
393 }
394
395 // Handle `for` loops.
396 LogicalResult visit(const slang::ast::ForLoopStatement &stmt) {
397 // Generate the initializers.
398 for (auto *initExpr : stmt.initializers)
399 if (!context.convertRvalueExpression(*initExpr))
400 return failure();
401
402 // Create the blocks for the loop condition, body, step, and exit.
403 auto &exitBlock = createBlock();
404 auto &stepBlock = createBlock();
405 auto &bodyBlock = createBlock();
406 auto &checkBlock = createBlock();
407 builder.create<cf::BranchOp>(loc, &checkBlock);
408
409 // Push the blocks onto the loop stack such that we can continue and break.
410 context.loopStack.push_back({&stepBlock, &exitBlock});
411 auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
412
413 // Generate the loop condition check.
414 builder.setInsertionPointToEnd(&checkBlock);
415 auto cond = context.convertRvalueExpression(*stmt.stopExpr);
416 if (!cond)
417 return failure();
418 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
419 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
420 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
421
422 // Generate the loop body.
423 builder.setInsertionPointToEnd(&bodyBlock);
424 if (failed(context.convertStatement(stmt.body)))
425 return failure();
426 if (!isTerminated())
427 builder.create<cf::BranchOp>(loc, &stepBlock);
428
429 // Generate the step expressions.
430 builder.setInsertionPointToEnd(&stepBlock);
431 for (auto *stepExpr : stmt.steps)
432 if (!context.convertRvalueExpression(*stepExpr))
433 return failure();
434 if (!isTerminated())
435 builder.create<cf::BranchOp>(loc, &checkBlock);
436
437 // If control never reaches the exit block, remove it and mark control flow
438 // as terminated. Otherwise we continue inserting ops in the exit block.
439 if (exitBlock.hasNoPredecessors()) {
440 exitBlock.erase();
441 setTerminated();
442 } else {
443 builder.setInsertionPointToEnd(&exitBlock);
444 }
445 return success();
446 }
447
448 LogicalResult visit(const slang::ast::ForeachLoopStatement &stmt) {
449 for (uint32_t level = 0; level < stmt.loopDims.size(); level++) {
450 if (stmt.loopDims[level].loopVar)
451 return recursiveForeach(stmt, level);
452 }
453 return success();
454 }
455
456 // Handle `repeat` loops.
457 LogicalResult visit(const slang::ast::RepeatLoopStatement &stmt) {
458 auto count = context.convertRvalueExpression(stmt.count);
459 if (!count)
460 return failure();
461
462 // Create the blocks for the loop condition, body, step, and exit.
463 auto &exitBlock = createBlock();
464 auto &stepBlock = createBlock();
465 auto &bodyBlock = createBlock();
466 auto &checkBlock = createBlock();
467 auto currentCount = checkBlock.addArgument(count.getType(), count.getLoc());
468 builder.create<cf::BranchOp>(loc, &checkBlock, count);
469
470 // Push the blocks onto the loop stack such that we can continue and break.
471 context.loopStack.push_back({&stepBlock, &exitBlock});
472 auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
473
474 // Generate the loop condition check.
475 builder.setInsertionPointToEnd(&checkBlock);
476 auto cond = builder.createOrFold<moore::BoolCastOp>(loc, currentCount);
477 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
478 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
479
480 // Generate the loop body.
481 builder.setInsertionPointToEnd(&bodyBlock);
482 if (failed(context.convertStatement(stmt.body)))
483 return failure();
484 if (!isTerminated())
485 builder.create<cf::BranchOp>(loc, &stepBlock);
486
487 // Decrement the current count and branch back to the check block.
488 builder.setInsertionPointToEnd(&stepBlock);
489 auto one = builder.create<moore::ConstantOp>(
490 count.getLoc(), cast<moore::IntType>(count.getType()), 1);
491 Value nextCount =
492 builder.create<moore::SubOp>(count.getLoc(), currentCount, one);
493 builder.create<cf::BranchOp>(loc, &checkBlock, nextCount);
494
495 // If control never reaches the exit block, remove it and mark control flow
496 // as terminated. Otherwise we continue inserting ops in the exit block.
497 if (exitBlock.hasNoPredecessors()) {
498 exitBlock.erase();
499 setTerminated();
500 } else {
501 builder.setInsertionPointToEnd(&exitBlock);
502 }
503 return success();
504 }
505
506 // Handle `while` and `do-while` loops.
507 LogicalResult createWhileLoop(const slang::ast::Expression &condExpr,
508 const slang::ast::Statement &bodyStmt,
509 bool atLeastOnce) {
510 // Create the blocks for the loop condition, body, and exit.
511 auto &exitBlock = createBlock();
512 auto &bodyBlock = createBlock();
513 auto &checkBlock = createBlock();
514 builder.create<cf::BranchOp>(loc, atLeastOnce ? &bodyBlock : &checkBlock);
515 if (atLeastOnce)
516 bodyBlock.moveBefore(&checkBlock);
517
518 // Push the blocks onto the loop stack such that we can continue and break.
519 context.loopStack.push_back({&checkBlock, &exitBlock});
520 auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
521
522 // Generate the loop condition check.
523 builder.setInsertionPointToEnd(&checkBlock);
524 auto cond = context.convertRvalueExpression(condExpr);
525 if (!cond)
526 return failure();
527 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
528 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
529 builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
530
531 // Generate the loop body.
532 builder.setInsertionPointToEnd(&bodyBlock);
533 if (failed(context.convertStatement(bodyStmt)))
534 return failure();
535 if (!isTerminated())
536 builder.create<cf::BranchOp>(loc, &checkBlock);
537
538 // If control never reaches the exit block, remove it and mark control flow
539 // as terminated. Otherwise we continue inserting ops in the exit block.
540 if (exitBlock.hasNoPredecessors()) {
541 exitBlock.erase();
542 setTerminated();
543 } else {
544 builder.setInsertionPointToEnd(&exitBlock);
545 }
546 return success();
547 }
548
549 LogicalResult visit(const slang::ast::WhileLoopStatement &stmt) {
550 return createWhileLoop(stmt.cond, stmt.body, false);
551 }
552
553 LogicalResult visit(const slang::ast::DoWhileLoopStatement &stmt) {
554 return createWhileLoop(stmt.cond, stmt.body, true);
555 }
556
557 // Handle `forever` loops.
558 LogicalResult visit(const slang::ast::ForeverLoopStatement &stmt) {
559 // Create the blocks for the loop body and exit.
560 auto &exitBlock = createBlock();
561 auto &bodyBlock = createBlock();
562 builder.create<cf::BranchOp>(loc, &bodyBlock);
563
564 // Push the blocks onto the loop stack such that we can continue and break.
565 context.loopStack.push_back({&bodyBlock, &exitBlock});
566 auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
567
568 // Generate the loop body.
569 builder.setInsertionPointToEnd(&bodyBlock);
570 if (failed(context.convertStatement(stmt.body)))
571 return failure();
572 if (!isTerminated())
573 builder.create<cf::BranchOp>(loc, &bodyBlock);
574
575 // If control never reaches the exit block, remove it and mark control flow
576 // as terminated. Otherwise we continue inserting ops in the exit block.
577 if (exitBlock.hasNoPredecessors()) {
578 exitBlock.erase();
579 setTerminated();
580 } else {
581 builder.setInsertionPointToEnd(&exitBlock);
582 }
583 return success();
584 }
585
586 // Handle timing control.
587 LogicalResult visit(const slang::ast::TimedStatement &stmt) {
588 return context.convertTimingControl(stmt.timing, stmt.stmt);
589 }
590
591 // Handle return statements.
592 LogicalResult visit(const slang::ast::ReturnStatement &stmt) {
593 if (stmt.expr) {
594 auto expr = context.convertRvalueExpression(*stmt.expr);
595 if (!expr)
596 return failure();
597 builder.create<mlir::func::ReturnOp>(loc, expr);
598 } else {
599 builder.create<mlir::func::ReturnOp>(loc);
600 }
601 setTerminated();
602 return success();
603 }
604
605 // Handle continue statements.
606 LogicalResult visit(const slang::ast::ContinueStatement &stmt) {
607 if (context.loopStack.empty())
608 return mlir::emitError(loc,
609 "cannot `continue` without a surrounding loop");
610 builder.create<cf::BranchOp>(loc, context.loopStack.back().continueBlock);
611 setTerminated();
612 return success();
613 }
614
615 // Handle break statements.
616 LogicalResult visit(const slang::ast::BreakStatement &stmt) {
617 if (context.loopStack.empty())
618 return mlir::emitError(loc, "cannot `break` without a surrounding loop");
619 builder.create<cf::BranchOp>(loc, context.loopStack.back().breakBlock);
620 setTerminated();
621 return success();
622 }
623
624 // Handle immediate assertion statements.
625 LogicalResult visit(const slang::ast::ImmediateAssertionStatement &stmt) {
626 auto cond = context.convertRvalueExpression(stmt.cond);
627 cond = context.convertToBool(cond);
628 if (!cond)
629 return failure();
630
631 // Handle assertion statements that don't have an action block.
632 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
633 auto defer = moore::DeferAssert::Immediate;
634 if (stmt.isFinal)
635 defer = moore::DeferAssert::Final;
636 else if (stmt.isDeferred)
637 defer = moore::DeferAssert::Observed;
638
639 switch (stmt.assertionKind) {
640 case slang::ast::AssertionKind::Assert:
641 builder.create<moore::AssertOp>(loc, defer, cond, StringAttr{});
642 return success();
643 case slang::ast::AssertionKind::Assume:
644 builder.create<moore::AssumeOp>(loc, defer, cond, StringAttr{});
645 return success();
646 case slang::ast::AssertionKind::CoverProperty:
647 builder.create<moore::CoverOp>(loc, defer, cond, StringAttr{});
648 return success();
649 default:
650 break;
651 }
652 mlir::emitError(loc) << "unsupported immediate assertion kind: "
653 << slang::ast::toString(stmt.assertionKind);
654 return failure();
655 }
656
657 // Regard assertion statements with an action block as the "if-else".
658 cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
659
660 // Create the blocks for the true and false branches, and the exit block.
661 Block &exitBlock = createBlock();
662 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
663 Block &trueBlock = createBlock();
664 builder.create<cf::CondBranchOp>(loc, cond, &trueBlock,
665 falseBlock ? falseBlock : &exitBlock);
666
667 // Generate the true branch.
668 builder.setInsertionPointToEnd(&trueBlock);
669 if (stmt.ifTrue && failed(context.convertStatement(*stmt.ifTrue)))
670 return failure();
671 if (!isTerminated())
672 builder.create<cf::BranchOp>(loc, &exitBlock);
673
674 if (stmt.ifFalse) {
675 // Generate the false branch if present.
676 builder.setInsertionPointToEnd(falseBlock);
677 if (failed(context.convertStatement(*stmt.ifFalse)))
678 return failure();
679 if (!isTerminated())
680 builder.create<cf::BranchOp>(loc, &exitBlock);
681 }
682
683 // If control never reaches the exit block, remove it and mark control flow
684 // as terminated. Otherwise we continue inserting ops in the exit block.
685 if (exitBlock.hasNoPredecessors()) {
686 exitBlock.erase();
687 setTerminated();
688 } else {
689 builder.setInsertionPointToEnd(&exitBlock);
690 }
691 return success();
692 }
693
694 // Handle concurrent assertion statements.
695 LogicalResult visit(const slang::ast::ConcurrentAssertionStatement &stmt) {
696 auto loc = context.convertLocation(stmt.sourceRange);
697 auto property = context.convertAssertionExpression(stmt.propertySpec, loc);
698 if (!property)
699 return failure();
700
701 // Handle assertion statements that don't have an action block.
702 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
703 switch (stmt.assertionKind) {
704 case slang::ast::AssertionKind::Assert:
705 builder.create<verif::AssertOp>(loc, property, Value(), StringAttr{});
706 return success();
707 case slang::ast::AssertionKind::Assume:
708 builder.create<verif::AssumeOp>(loc, property, Value(), StringAttr{});
709 return success();
710 default:
711 break;
712 }
713 mlir::emitError(loc) << "unsupported concurrent assertion kind: "
714 << slang::ast::toString(stmt.assertionKind);
715 return failure();
716 }
717
718 mlir::emitError(loc)
719 << "concurrent assertion statements with action blocks "
720 "are not supported yet";
721 return failure();
722 }
723
724 /// Handle the subset of system calls that return no result value. Return
725 /// true if the called system task could be handled, false otherwise. Return
726 /// failure if an error occurred.
727 FailureOr<bool>
728 visitSystemCall(const slang::ast::ExpressionStatement &stmt,
729 const slang::ast::CallExpression &expr,
730 const slang::ast::CallExpression::SystemCallInfo &info) {
731 const auto &subroutine = *info.subroutine;
732 auto args = expr.arguments();
733
734 // Simulation Control Tasks
735
736 if (subroutine.name == "$stop") {
737 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
738 builder.create<moore::StopBIOp>(loc);
739 return true;
740 }
741
742 if (subroutine.name == "$finish") {
743 createFinishMessage(args.size() >= 1 ? args[0] : nullptr);
744 builder.create<moore::FinishBIOp>(loc, 0);
745 builder.create<moore::UnreachableOp>(loc);
746 setTerminated();
747 return true;
748 }
749
750 if (subroutine.name == "$exit") {
751 // Calls to `$exit` from outside a `program` are ignored. Since we don't
752 // yet support programs, there is nothing to do here.
753 // TODO: Fix this once we support programs.
754 return true;
755 }
756
757 // Display and Write Tasks (`$display[boh]?` or `$write[boh]?`)
758
759 // Check for a `$display` or `$write` prefix.
760 bool isDisplay = false; // display or write
761 bool appendNewline = false; // display
762 StringRef remainingName = subroutine.name;
763 if (remainingName.consume_front("$display")) {
764 isDisplay = true;
765 appendNewline = true;
766 } else if (remainingName.consume_front("$write")) {
767 isDisplay = true;
768 }
769
770 // Check for optional `b`, `o`, or `h` suffix indicating default format.
771 using moore::IntFormat;
772 IntFormat defaultFormat = IntFormat::Decimal;
773 if (isDisplay && !remainingName.empty()) {
774 if (remainingName == "b")
775 defaultFormat = IntFormat::Binary;
776 else if (remainingName == "o")
777 defaultFormat = IntFormat::Octal;
778 else if (remainingName == "h")
779 defaultFormat = IntFormat::HexLower;
780 else
781 isDisplay = false;
782 }
783
784 if (isDisplay) {
785 auto message =
786 context.convertFormatString(args, loc, defaultFormat, appendNewline);
787 if (failed(message))
788 return failure();
789 if (*message == Value{})
790 return true;
791 builder.create<moore::DisplayBIOp>(loc, *message);
792 return true;
793 }
794
795 // Severity Tasks
796 using moore::Severity;
797 std::optional<Severity> severity;
798 if (subroutine.name == "$info")
799 severity = Severity::Info;
800 else if (subroutine.name == "$warning")
801 severity = Severity::Warning;
802 else if (subroutine.name == "$error")
803 severity = Severity::Error;
804 else if (subroutine.name == "$fatal")
805 severity = Severity::Fatal;
806
807 if (severity) {
808 // The `$fatal` task has an optional leading verbosity argument.
809 const slang::ast::Expression *verbosityExpr = nullptr;
810 if (severity == Severity::Fatal && args.size() >= 1) {
811 verbosityExpr = args[0];
812 args = args.subspan(1);
813 }
814
815 // Handle the string formatting.
816 auto message = context.convertFormatString(args, loc);
817 if (failed(message))
818 return failure();
819 if (*message == Value{})
820 *message = builder.create<moore::FormatLiteralOp>(loc, "");
821
822 builder.create<moore::SeverityBIOp>(loc, *severity, *message);
823
824 // Handle the `$fatal` case which behaves like a `$finish`.
825 if (severity == Severity::Fatal) {
826 createFinishMessage(verbosityExpr);
827 builder.create<moore::FinishBIOp>(loc, 1);
828 builder.create<moore::UnreachableOp>(loc);
829 setTerminated();
830 }
831 return true;
832 }
833
834 // Give up on any other system tasks. These will be tried again as an
835 // expression later.
836 return false;
837 }
838
839 /// Create the optional diagnostic message print for finish-like ops.
840 void createFinishMessage(const slang::ast::Expression *verbosityExpr) {
841 unsigned verbosity = 1;
842 if (verbosityExpr) {
843 auto value =
844 context.evaluateConstant(*verbosityExpr).integer().as<unsigned>();
845 assert(value && "Slang guarantees constant verbosity parameter");
846 verbosity = *value;
847 }
848 if (verbosity == 0)
849 return;
850 builder.create<moore::FinishMessageBIOp>(loc, verbosity > 1);
851 }
852
853 /// Emit an error for all other statements.
854 template <typename T>
855 LogicalResult visit(T &&stmt) {
856 mlir::emitError(loc, "unsupported statement: ")
857 << slang::ast::toString(stmt.kind);
858 return mlir::failure();
859 }
860
861 LogicalResult visitInvalid(const slang::ast::Statement &stmt) {
862 mlir::emitError(loc, "invalid statement: ")
863 << slang::ast::toString(stmt.kind);
864 return mlir::failure();
865 }
866};
867} // namespace
868
869LogicalResult Context::convertStatement(const slang::ast::Statement &stmt) {
870 assert(builder.getInsertionBlock());
871 auto loc = convertLocation(stmt.sourceRange);
872 return stmt.visit(StmtVisitor(*this, loc));
873}
874// NOLINTEND(misc-no-recursion)
assert(baseType &&"element must be base type")
static FVInt getZero(unsigned numBits)
Construct an FVInt with all bits set to 0.
Definition FVInt.h:61
This helps visit TypeOp nodes.
Definition HWVisitors.h:89
void info(Twine message)
Definition LSPUtils.cpp:20
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={})
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.