12#include "mlir/Dialect/Func/IR/FuncOps.h"
13#include "mlir/IR/Builders.h"
14#include "mlir/IR/Diagnostics.h"
15#include "slang/ast/Compilation.h"
16#include "slang/ast/SemanticFacts.h"
17#include "slang/ast/Statement.h"
18#include "slang/ast/SystemSubroutine.h"
19#include "slang/ast/expressions/MiscExpressions.h"
20#include "slang/ast/symbols/CompilationUnitSymbols.h"
21#include "slang/ast/symbols/InstanceSymbols.h"
22#include "llvm/ADT/ScopeExit.h"
23#include "llvm/Support/raw_ostream.h"
33 Context &
context, std::span<const slang::ast::Expression *const> args) {
34 auto timeScale =
context.timeScale;
38 if (
auto *expr = args[0]->as_if<slang::ast::ArbitrarySymbolExpression>()) {
39 const auto *symbol = expr->symbol.get();
40 if (
auto *instance = symbol->as_if<slang::ast::InstanceSymbol>()) {
41 timeScale = instance->body.getTimeScale().value_or(timeScale);
42 target = instance->getHierarchicalPath();
43 }
else if (
auto *unit =
44 symbol->as_if<slang::ast::CompilationUnitSymbol>()) {
45 timeScale = unit->getTimeScale().value_or(timeScale);
47 }
else if (symbol->kind == slang::ast::SymbolKind::Root) {
54 llvm::raw_string_ostream os(out);
57 os <<
" of " << target;
58 os <<
" is " << timeScale.base.toString() <<
" / "
59 << timeScale.precision.toString() <<
"\n";
66 auto i32Ty = moore::IntType::getInt(
context, 32);
68 auto unit = moore::ConstantOp::create(builder, loc, i32Ty, -15);
69 auto precision = moore::ConstantOp::create(builder, loc, i32Ty, 0);
70 auto emptyInt = moore::ConstantStringOp::create(
71 builder, loc, moore::IntType::getInt(
context, 0),
"");
72 auto suffix = moore::IntToStringOp::create(builder, loc, emptyInt);
73 auto minWidth = moore::ConstantOp::create(builder, loc, i32Ty, 20);
75 return {unit, precision, suffix, minWidth};
80static FailureOr<Value>
82 const slang::ast::ForeachLoopStatement &stmt,
83 uint32_t level,
const moore::IntType &idxType) {
84 auto &builder =
context.builder;
85 const auto &loopDim = stmt.loopDims[level];
88 Value array =
context.convertRvalueExpression(stmt.arrayRef);
89 for (uint32_t i = 0; i < level; ++i) {
90 const auto &dim = stmt.loopDims[i];
91 const auto &loopVar = dim.loopVar;
93 mlir::emitError(loc,
"unsupported foreach with missing loop variable");
96 llvm::TypeSwitch<Type, Type>(array.getType())
97 .Case<moore::OpenUnpackedArrayType, moore::UnpackedArrayType,
98 moore::ArrayType, moore::OpenArrayType, moore::QueueType,
99 moore::AssocArrayType>(
100 [](
auto ty) {
return ty.getElementType(); })
101 .Default([](Type) {
return Type(); });
103 auto curIdx = moore::ReadOp::create(builder, loc,
104 context.valueSymbols.lookup(loopVar));
105 if (dim.range.has_value()) {
108 moore::DynExtractOp::create(builder, loc, nestedType, array, offset);
111 moore::DynExtractOp::create(builder, loc, nestedType, array, curIdx);
116 if (loopDim.loopVar->arrayType.isQueue()) {
117 size = moore::QueueSizeBIOp::create(builder, loc, array);
118 }
else if (loopDim.loopVar->arrayType.getCanonicalType().kind ==
119 slang::ast::SymbolKind::DynamicArrayType) {
120 size = moore::OpenUArraySizeOp::create(builder, loc, array);
125 mlir::emitError(loc,
"unsupported foreach loop on type: ")
126 << loopDim.loopVar->arrayType.toString();
130 auto one = moore::ConstantOp::create(builder, loc, idxType, 1);
131 auto sizeMinusOne = moore::SubOp::create(builder, loc, size, one).getResult();
142 StmtVisitor(Context &context, Location loc)
143 : context(context), loc(loc), builder(context.builder) {}
145 bool isTerminated()
const {
return !builder.getInsertionBlock(); }
146 void setTerminated() { builder.clearInsertionPoint(); }
148 Block &createBlock() {
149 assert(builder.getInsertionBlock());
150 auto block = std::make_unique<Block>();
151 block->insertAfter(builder.getInsertionBlock());
152 return *block.release();
155 LogicalResult recursiveForeach(
const slang::ast::ForeachLoopStatement &stmt,
158 const auto &loopDim = stmt.loopDims[level];
159 auto &exitBlock = createBlock();
160 auto &stepBlock = createBlock();
161 auto &bodyBlock = createBlock();
162 auto &checkBlock = createBlock();
165 context.loopStack.push_back({&stepBlock, &exitBlock});
166 llvm::scope_exit done([&] { context.loopStack.pop_back(); });
169 const auto &iter = loopDim.loopVar;
170 auto idxType = context.convertType(*iter->getDeclaredType());
173 auto intIdxType = cast<moore::IntType>(idxType);
177 loopDim.range.has_value()
178 ? moore::ConstantOp::create(builder, loc, intIdxType,
179 loopDim.range->lower())
180 : moore::ConstantOp::create(builder, loc, intIdxType, 0);
183 Value varOp = moore::VariableOp::create(
184 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(idxType)),
185 builder.getStringAttr(iter->name), initial);
186 context.valueSymbols.insertIntoScope(context.valueSymbols.getCurScope(),
189 cf::BranchOp::create(builder, loc, &checkBlock);
190 builder.setInsertionPointToEnd(&checkBlock);
194 loopDim.range.has_value()
195 ? moore::ConstantOp::create(builder, loc, intIdxType,
196 loopDim.range->upper())
202 auto var = moore::ReadOp::create(builder, loc, varOp);
203 Value cond = moore::SleOp::create(builder, loc, var, upperBound);
206 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
207 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
209 cond = moore::LogicToIntOp::create(builder, loc, cond);
211 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
212 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
214 builder.setInsertionPointToEnd(&bodyBlock);
218 bool hasNext =
false;
219 for (uint32_t nextLevel = level + 1; nextLevel < stmt.loopDims.size();
221 if (stmt.loopDims[nextLevel].loopVar) {
222 if (failed(recursiveForeach(stmt, nextLevel)))
230 if (failed(context.convertStatement(stmt.body)))
234 cf::BranchOp::create(builder, loc, &stepBlock);
236 builder.setInsertionPointToEnd(&stepBlock);
239 var = moore::ReadOp::create(builder, loc, varOp);
240 auto one = moore::ConstantOp::create(builder, loc, intIdxType, 1);
241 auto postValue = moore::AddOp::create(builder, loc, var, one).getResult();
242 moore::BlockingAssignOp::create(builder, loc, varOp, postValue);
243 cf::BranchOp::create(builder, loc, &checkBlock);
245 if (exitBlock.hasNoPredecessors()) {
249 builder.setInsertionPointToEnd(&exitBlock);
255 LogicalResult visit(
const slang::ast::EmptyStatement &) {
return success(); }
264 LogicalResult visit(
const slang::ast::StatementList &stmts) {
265 for (
auto *stmt : stmts.list) {
266 if (isTerminated()) {
267 auto loc = context.convertLocation(stmt->sourceRange);
268 mlir::emitWarning(loc,
"unreachable code");
271 if (failed(context.convertStatement(*stmt)))
281 LogicalResult visit(
const slang::ast::BlockStatement &stmt) {
282 moore::JoinKind kind;
283 switch (stmt.blockKind) {
284 case slang::ast::StatementBlockKind::Sequential:
286 return context.convertStatement(stmt.body);
287 case slang::ast::StatementBlockKind::JoinAll:
288 kind = moore::JoinKind::Join;
290 case slang::ast::StatementBlockKind::JoinAny:
291 kind = moore::JoinKind::JoinAny;
293 case slang::ast::StatementBlockKind::JoinNone:
294 kind = moore::JoinKind::JoinNone;
308 SmallVector<const slang::ast::Statement *> items;
309 if (
auto *threadList = stmt.body.as_if<slang::ast::StatementList>())
310 items.append(threadList->list.begin(), threadList->list.end());
312 items.push_back(&stmt.body);
314 SmallVector<const slang::ast::Statement *> threads;
315 for (
auto *item : items) {
316 if (item->as_if<slang::ast::VariableDeclStatement>()) {
317 if (failed(context.convertStatement(*item)))
321 threads.push_back(item);
326 if (threads.empty() && !items.empty())
329 auto forkOp = moore::ForkJoinOp::create(builder, loc, kind, threads.size());
330 OpBuilder::InsertionGuard guard(builder);
332 for (
auto [i, thread] : llvm::enumerate(threads)) {
333 auto &tBlock = forkOp->getRegion(i).emplaceBlock();
334 builder.setInsertionPointToStart(&tBlock);
337 if (failed(context.convertStatement(*thread)))
339 moore::CompleteOp::create(builder, loc);
345 LogicalResult visit(
const slang::ast::ExpressionStatement &stmt) {
347 if (
const auto *call = stmt.expr.as_if<slang::ast::CallExpression>()) {
348 if (
const auto *info =
349 std::get_if<slang::ast::CallExpression::SystemCallInfo>(
350 &call->subroutine)) {
351 auto handled = visitSystemCall(stmt, *call, *info);
359 auto value = context.convertRvalueExpression(stmt.expr);
365 if (
auto *defOp = value.getDefiningOp())
366 if (isOpTriviallyDead(defOp))
373 LogicalResult visit(
const slang::ast::VariableDeclStatement &stmt) {
374 const auto &var = stmt.symbol;
375 auto type = context.convertType(*var.getDeclaredType());
380 if (
const auto *init = var.getInitializer()) {
381 initial = context.convertRvalueExpression(*init, type);
387 auto varOp = moore::VariableOp::create(
388 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
389 builder.getStringAttr(var.name), initial);
390 context.valueSymbols.insertIntoScope(context.valueSymbols.getCurScope(),
392 const auto &canonTy = var.getType().getCanonicalType();
393 if (
const auto *vi = canonTy.as_if<slang::ast::VirtualInterfaceType>())
394 if (failed(context.registerVirtualInterfaceMembers(var, *vi, loc)))
400 LogicalResult visit(
const slang::ast::ConditionalStatement &stmt) {
404 for (
const auto &condition : stmt.conditions) {
405 if (condition.pattern)
406 return mlir::emitError(loc,
407 "match patterns in if conditions not supported");
408 auto cond = context.convertRvalueExpression(*condition.expr);
411 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
413 allConds = moore::AndOp::create(builder, loc, allConds, cond);
417 assert(allConds &&
"slang guarantees at least one condition");
418 if (
auto ty = dyn_cast<moore::IntType>(allConds.getType());
420 allConds = moore::LogicToIntOp::create(builder, loc, allConds);
422 allConds = moore::ToBuiltinIntOp::create(builder, loc, allConds);
425 Block &exitBlock = createBlock();
426 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
427 Block &trueBlock = createBlock();
428 cf::CondBranchOp::create(builder, loc, allConds, &trueBlock,
429 falseBlock ? falseBlock : &exitBlock);
432 builder.setInsertionPointToEnd(&trueBlock);
433 if (failed(context.convertStatement(stmt.ifTrue)))
436 cf::BranchOp::create(builder, loc, &exitBlock);
440 builder.setInsertionPointToEnd(falseBlock);
441 if (failed(context.convertStatement(*stmt.ifFalse)))
444 cf::BranchOp::create(builder, loc, &exitBlock);
449 if (exitBlock.hasNoPredecessors()) {
453 builder.setInsertionPointToEnd(&exitBlock);
459 LogicalResult visit(
const slang::ast::CaseStatement &caseStmt) {
460 using slang::ast::AttributeSymbol;
461 using slang::ast::CaseStatementCondition;
463 caseStmt.expr.as_if<slang::ast::TypeReferenceExpression>()) {
464 if (caseStmt.condition != CaseStatementCondition::Normal)
465 return mlir::emitError(loc,
466 "unsupported type reference case condition");
468 const slang::ast::Statement *matchedStmt =
nullptr;
469 for (
const auto &item : caseStmt.items) {
470 for (
const auto *expr : item.expressions) {
471 auto *itemType = expr->as_if<slang::ast::TypeReferenceExpression>();
473 return mlir::emitError(
474 context.convertLocation(expr->sourceRange),
475 "unsupported non-type item in type reference case statement");
476 if (itemType->targetType.isMatching(caseType->targetType)) {
477 matchedStmt = item.stmt;
486 return context.convertStatement(*matchedStmt);
487 if (caseStmt.defaultCase)
488 return context.convertStatement(*caseStmt.defaultCase);
492 auto caseExpr = context.convertRvalueExpression(caseStmt.expr);
499 auto &exitBlock = createBlock();
500 Block *lastMatchBlock =
nullptr;
501 SmallVector<moore::FVIntegerAttr> itemConsts;
503 for (
const auto &item : caseStmt.items) {
506 auto &matchBlock = createBlock();
507 lastMatchBlock = &matchBlock;
512 for (
const auto *expr : item.expressions) {
516 if (caseStmt.condition == CaseStatementCondition::Inside) {
518 cond = context.convertInsideCheck(
519 context.convertToSimpleBitVector(caseExpr), itemLoc, *expr);
523 auto value = context.convertRvalueExpression(*expr);
526 itemLoc = value.getLoc();
529 auto maybeConst = value;
530 while (isa_and_nonnull<moore::IntToLogicOp, moore::LogicToIntOp>(
531 maybeConst.getDefiningOp()))
532 maybeConst = maybeConst.getDefiningOp()->getOperand(0);
533 if (
auto defOp = maybeConst.getDefiningOp<moore::ConstantOp>())
534 itemConsts.push_back(defOp.getValueAttr());
539 switch (caseStmt.condition) {
540 case CaseStatementCondition::Normal:
541 if (isa<moore::RealType>(caseExpr.getType()))
542 cond = moore::EqRealOp::create(builder, itemLoc, caseExpr, value);
544 cond = moore::CaseEqOp::create(builder, itemLoc, caseExpr, value);
546 case CaseStatementCondition::WildcardXOrZ:
547 cond = moore::CaseXZEqOp::create(builder, itemLoc, caseExpr, value);
549 case CaseStatementCondition::WildcardJustZ:
550 cond = moore::CaseZEqOp::create(builder, itemLoc, caseExpr, value);
552 case CaseStatementCondition::Inside:
553 llvm_unreachable(
"Inside condition has been handled already");
558 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
560 cond = moore::LogicToIntOp::create(builder, loc, cond);
562 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
566 auto &nextBlock = createBlock();
567 mlir::cf::CondBranchOp::create(builder, itemLoc, cond, &matchBlock,
569 builder.setInsertionPointToEnd(&nextBlock);
575 matchBlock.moveBefore(builder.getInsertionBlock());
578 OpBuilder::InsertionGuard guard(builder);
579 builder.setInsertionPointToEnd(&matchBlock);
580 if (failed(context.convertStatement(*item.stmt)))
582 if (!isTerminated()) {
583 auto loc = context.convertLocation(item.stmt->sourceRange);
584 mlir::cf::BranchOp::create(builder, loc, &exitBlock);
588 const auto caseStmtAttrs = context.compilation.getAttributes(caseStmt);
589 const bool hasFullCaseAttr =
590 llvm::find_if(caseStmtAttrs, [](
const AttributeSymbol *attr) {
591 return attr->name ==
"full_case";
592 }) != caseStmtAttrs.end();
603 auto twoStateExhaustive =
false;
604 if (
auto intType = dyn_cast<moore::IntType>(caseExpr.getType());
605 intType && intType.getWidth() < 32 &&
606 itemConsts.size() == (1 << intType.getWidth())) {
608 llvm::sort(itemConsts, [](
auto a,
auto b) {
609 return a.getValue().getRawValue().ult(b.getValue().getRawValue());
617 for (
auto value : itemConsts) {
618 if (value.getValue() != nextValue)
622 twoStateExhaustive = nextValue.isZero();
633 if ((twoStateExhaustive || (hasFullCaseAttr && !caseStmt.defaultCase)) &&
635 caseStmt.condition == CaseStatementCondition::Normal) {
636 mlir::cf::BranchOp::create(builder, loc, lastMatchBlock);
639 if (caseStmt.defaultCase)
640 if (failed(context.convertStatement(*caseStmt.defaultCase)))
643 mlir::cf::BranchOp::create(builder, loc, &exitBlock);
648 if (exitBlock.hasNoPredecessors()) {
652 builder.setInsertionPointToEnd(&exitBlock);
658 LogicalResult visit(
const slang::ast::ForLoopStatement &stmt) {
660 for (
auto *initExpr : stmt.initializers)
661 if (!context.convertRvalueExpression(*initExpr))
665 auto &exitBlock = createBlock();
666 auto &stepBlock = createBlock();
667 auto &bodyBlock = createBlock();
668 auto &checkBlock = createBlock();
669 cf::BranchOp::create(builder, loc, &checkBlock);
672 context.loopStack.push_back({&stepBlock, &exitBlock});
673 llvm::scope_exit done([&] { context.loopStack.pop_back(); });
676 builder.setInsertionPointToEnd(&checkBlock);
677 auto cond = context.convertRvalueExpression(*stmt.stopExpr);
680 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
681 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
683 cond = moore::LogicToIntOp::create(builder, loc, cond);
685 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
686 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
689 builder.setInsertionPointToEnd(&bodyBlock);
690 if (failed(context.convertStatement(stmt.body)))
693 cf::BranchOp::create(builder, loc, &stepBlock);
696 builder.setInsertionPointToEnd(&stepBlock);
697 for (
auto *stepExpr : stmt.steps)
698 if (!context.convertRvalueExpression(*stepExpr))
701 cf::BranchOp::create(builder, loc, &checkBlock);
705 if (exitBlock.hasNoPredecessors()) {
709 builder.setInsertionPointToEnd(&exitBlock);
714 LogicalResult visit(
const slang::ast::ForeachLoopStatement &stmt) {
715 for (uint32_t level = 0; level < stmt.loopDims.size(); level++) {
716 if (stmt.loopDims[level].loopVar)
717 return recursiveForeach(stmt, level);
723 LogicalResult visit(
const slang::ast::RepeatLoopStatement &stmt) {
724 auto intType = moore::IntType::getInt(context.getContext(), 32);
725 auto count = context.convertRvalueExpression(stmt.count, intType);
730 auto &exitBlock = createBlock();
731 auto &stepBlock = createBlock();
732 auto &bodyBlock = createBlock();
733 auto &checkBlock = createBlock();
734 auto currentCount = checkBlock.addArgument(count.getType(), count.getLoc());
735 cf::BranchOp::create(builder, loc, &checkBlock, count);
738 context.loopStack.push_back({&stepBlock, &exitBlock});
739 llvm::scope_exit done([&] { context.loopStack.pop_back(); });
742 builder.setInsertionPointToEnd(&checkBlock);
743 auto cond = builder.createOrFold<moore::BoolCastOp>(loc, currentCount);
744 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
746 cond = moore::LogicToIntOp::create(builder, loc, cond);
748 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
749 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
752 builder.setInsertionPointToEnd(&bodyBlock);
753 if (failed(context.convertStatement(stmt.body)))
756 cf::BranchOp::create(builder, loc, &stepBlock);
759 builder.setInsertionPointToEnd(&stepBlock);
760 auto one = moore::ConstantOp::create(
761 builder, count.getLoc(), cast<moore::IntType>(count.getType()), 1);
763 moore::SubOp::create(builder, count.getLoc(), currentCount, one);
764 cf::BranchOp::create(builder, loc, &checkBlock, nextCount);
768 if (exitBlock.hasNoPredecessors()) {
772 builder.setInsertionPointToEnd(&exitBlock);
778 LogicalResult createWhileLoop(
const slang::ast::Expression &condExpr,
779 const slang::ast::Statement &bodyStmt,
782 auto &exitBlock = createBlock();
783 auto &bodyBlock = createBlock();
784 auto &checkBlock = createBlock();
785 cf::BranchOp::create(builder, loc, atLeastOnce ? &bodyBlock : &checkBlock);
787 bodyBlock.moveBefore(&checkBlock);
790 context.loopStack.push_back({&checkBlock, &exitBlock});
791 llvm::scope_exit done([&] { context.loopStack.pop_back(); });
794 builder.setInsertionPointToEnd(&checkBlock);
795 auto cond = context.convertRvalueExpression(condExpr);
798 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
799 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
801 cond = moore::LogicToIntOp::create(builder, loc, cond);
803 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
804 cf::CondBranchOp::create(builder, loc, cond, &bodyBlock, &exitBlock);
807 builder.setInsertionPointToEnd(&bodyBlock);
808 if (failed(context.convertStatement(bodyStmt)))
811 cf::BranchOp::create(builder, loc, &checkBlock);
815 if (exitBlock.hasNoPredecessors()) {
819 builder.setInsertionPointToEnd(&exitBlock);
824 LogicalResult visit(
const slang::ast::WhileLoopStatement &stmt) {
825 return createWhileLoop(stmt.cond, stmt.body,
false);
828 LogicalResult visit(
const slang::ast::DoWhileLoopStatement &stmt) {
829 return createWhileLoop(stmt.cond, stmt.body,
true);
833 LogicalResult visit(
const slang::ast::ForeverLoopStatement &stmt) {
835 auto &exitBlock = createBlock();
836 auto &bodyBlock = createBlock();
837 cf::BranchOp::create(builder, loc, &bodyBlock);
840 context.loopStack.push_back({&bodyBlock, &exitBlock});
841 llvm::scope_exit done([&] { context.loopStack.pop_back(); });
844 builder.setInsertionPointToEnd(&bodyBlock);
845 if (failed(context.convertStatement(stmt.body)))
848 cf::BranchOp::create(builder, loc, &bodyBlock);
852 if (exitBlock.hasNoPredecessors()) {
856 builder.setInsertionPointToEnd(&exitBlock);
862 LogicalResult visit(
const slang::ast::TimedStatement &stmt) {
863 return context.convertTimingControl(stmt.timing, stmt.stmt);
867 LogicalResult visit(
const slang::ast::ReturnStatement &stmt) {
868 Operation *parentOp = builder.getInsertionBlock()
869 ? builder.getInsertionBlock()->getParentOp()
872 return mlir::emitError(loc) <<
"return statement is not within an op";
874 if (isa<moore::CoroutineOp, moore::ProcedureOp>(parentOp)) {
876 return mlir::emitError(loc)
877 <<
"unsupported `return <expr>` in a procedure or task";
878 moore::ReturnOp::create(builder, loc);
883 auto funcOp = dyn_cast<mlir::func::FuncOp>(parentOp);
885 return mlir::emitError(loc) <<
"unsupported return statement context";
888 auto resultTypes = funcOp.getFunctionType().getResults();
889 Type resultType = resultTypes.size() == 1 ? resultTypes[0] : Type();
890 auto expr = context.convertRvalueExpression(*stmt.expr, resultType);
893 mlir::func::ReturnOp::create(builder, loc, expr);
895 mlir::func::ReturnOp::create(builder, loc);
902 LogicalResult visit(
const slang::ast::ContinueStatement &stmt) {
903 if (context.loopStack.empty())
904 return mlir::emitError(loc,
905 "cannot `continue` without a surrounding loop");
906 cf::BranchOp::create(builder, loc, context.loopStack.back().continueBlock);
912 LogicalResult visit(
const slang::ast::BreakStatement &stmt) {
913 if (context.loopStack.empty())
914 return mlir::emitError(loc,
"cannot `break` without a surrounding loop");
915 cf::BranchOp::create(builder, loc, context.loopStack.back().breakBlock);
921 LogicalResult visit(
const slang::ast::ImmediateAssertionStatement &stmt) {
922 auto cond = context.convertRvalueExpression(stmt.cond);
923 cond = context.convertToBool(cond);
928 if (stmt.ifTrue && stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
929 auto defer = moore::DeferAssert::Immediate;
931 defer = moore::DeferAssert::Final;
932 else if (stmt.isDeferred)
933 defer = moore::DeferAssert::Observed;
935 switch (stmt.assertionKind) {
936 case slang::ast::AssertionKind::Assert:
937 moore::AssertOp::create(builder, loc, defer, cond, StringAttr{});
939 case slang::ast::AssertionKind::Assume:
940 moore::AssumeOp::create(builder, loc, defer, cond, StringAttr{});
942 case slang::ast::AssertionKind::CoverProperty:
943 moore::CoverOp::create(builder, loc, defer, cond, StringAttr{});
948 mlir::emitError(loc) <<
"unsupported immediate assertion kind: "
949 << slang::ast::toString(stmt.assertionKind);
954 if (
auto ty = dyn_cast<moore::IntType>(cond.getType());
956 cond = moore::LogicToIntOp::create(builder, loc, cond);
958 cond = moore::ToBuiltinIntOp::create(builder, loc, cond);
961 Block &exitBlock = createBlock();
962 Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
963 Block &trueBlock = createBlock();
964 cf::CondBranchOp::create(builder, loc, cond, &trueBlock,
965 falseBlock ? falseBlock : &exitBlock);
968 builder.setInsertionPointToEnd(&trueBlock);
969 if (stmt.ifTrue && failed(context.convertStatement(*stmt.ifTrue)))
972 cf::BranchOp::create(builder, loc, &exitBlock);
976 builder.setInsertionPointToEnd(falseBlock);
977 if (failed(context.convertStatement(*stmt.ifFalse)))
980 cf::BranchOp::create(builder, loc, &exitBlock);
985 if (exitBlock.hasNoPredecessors()) {
989 builder.setInsertionPointToEnd(&exitBlock);
995 LogicalResult visit(
const slang::ast::ConcurrentAssertionStatement &stmt) {
996 auto loc = context.convertLocation(stmt.sourceRange);
1007 const slang::ast::AssertionExpr *propertySpec;
1008 const slang::ast::ClockingAssertionExpr *clocking =
1009 stmt.propertySpec.as_if<slang::ast::ClockingAssertionExpr>();
1011 propertySpec = &(clocking->expr);
1013 propertySpec = &(stmt.propertySpec);
1015 if (
auto *disableIff =
1016 propertySpec->as_if<slang::ast::DisableIffAssertionExpr>()) {
1019 auto disableCond = context.convertRvalueExpression(disableIff->condition);
1020 auto enableCond = moore::NotOp::create(builder, loc, disableCond);
1022 enable = context.convertToI1(enableCond);
1026 auto clockingExpr = slang::ast::ClockingAssertionExpr(
1027 clocking->clocking, disableIff->expr);
1028 property = context.convertAssertionExpression(clockingExpr, loc);
1030 property = context.convertAssertionExpression(disableIff->expr, loc);
1033 property = context.convertAssertionExpression(stmt.propertySpec, loc);
1040 if (!stmt.ifTrue || stmt.ifTrue->as_if<slang::ast::EmptyStatement>()) {
1041 switch (stmt.assertionKind) {
1042 case slang::ast::AssertionKind::Assert:
1043 verif::AssertOp::create(builder, loc, property, enable, StringAttr{});
1045 case slang::ast::AssertionKind::Assume:
1046 verif::AssumeOp::create(builder, loc, property, enable, StringAttr{});
1048 case slang::ast::AssertionKind::CoverProperty:
1049 verif::CoverOp::create(builder, loc, property, enable, StringAttr{});
1054 mlir::emitError(loc) <<
"unsupported concurrent assertion kind: "
1055 << slang::ast::toString(stmt.assertionKind);
1059 mlir::emitError(loc)
1060 <<
"concurrent assertion statements with action blocks "
1061 "are not supported yet";
1075 getDisplayMessage(std::span<const slang::ast::Expression *const> args) {
1076 if (args.size() == 0)
1085 if (args[0]->as_if<slang::ast::StringLiteral>()) {
1086 return context.convertFormatString(args, loc);
1089 if (args.size() == 1) {
1090 return context.convertRvalueExpression(
1091 *args[0], builder.getType<moore::FormatStringType>());
1094 return emitError(loc) <<
"Failed to convert Display Message!";
1100 convertReadMemTask(std::span<const slang::ast::Expression *const> args,
1102 assert(args.size() >= 2 && args.size() <= 4 &&
1103 "$readmemh/$readmemb takes 2 to 4 arguments");
1105 auto i32Ty = moore::IntType::getInt(builder.getContext(), 32);
1106 auto filename = context.convertRvalueExpression(
1107 *args[0], moore::StringType::get(builder.getContext()));
1111 const auto *destExpr = args[1];
1115 if (
const auto *assign =
1116 destExpr->as_if<slang::ast::AssignmentExpression>())
1117 destExpr = &assign->left();
1122 Value sliceLeft, sliceRight;
1123 if (
const auto *rangeExpr =
1124 destExpr->as_if<slang::ast::RangeSelectExpression>()) {
1125 if (rangeExpr->getSelectionKind() !=
1126 slang::ast::RangeSelectionKind::Simple) {
1127 mlir::emitError(loc)
1128 <<
"unsupported: indexed part-select on $readmem memory";
1132 sliceLeft = context.convertRvalueExpression(rangeExpr->left(), i32Ty);
1133 sliceRight = context.convertRvalueExpression(rangeExpr->right(), i32Ty);
1135 if (!sliceLeft || !sliceRight)
1138 destExpr = &rangeExpr->value();
1141 auto dest = context.convertLvalueExpression(*destExpr);
1149 const auto *curTy = &destExpr->type->getCanonicalType();
1150 SmallVector<int64_t> dimLows;
1151 SmallVector<bool> dimDescs;
1152 const slang::ast::Type *elemSvTy = curTy;
1154 if (curTy->isAssociativeArray()) {
1155 mlir::emitError(loc) <<
"unsupported: $readmem into associative array";
1159 if (
const auto *queueTy = curTy->as_if<slang::ast::QueueType>()) {
1160 dimLows.push_back(0);
1161 dimDescs.push_back(
false);
1162 elemSvTy = &queueTy->elementType.getCanonicalType();
1163 }
else if (curTy->as_if<slang::ast::DynamicArrayType>()) {
1164 mlir::emitError(loc) <<
"unsupported: $readmem into dynamic array";
1167 while (
const auto *fixedArr =
1168 curTy->as_if<slang::ast::FixedSizeUnpackedArrayType>()) {
1169 dimLows.push_back(fixedArr->range.lower());
1170 dimDescs.push_back(fixedArr->range.isDescending());
1171 curTy = &fixedArr->elementType.getCanonicalType();
1176 if (dimLows.empty()) {
1177 mlir::emitError(loc) <<
"$readmem memory must be an unpacked array";
1183 if (!elemSvTy->isIntegral()) {
1184 mlir::emitError(loc) <<
"unsupported: $readmem element type "
1185 << elemSvTy->toString();
1192 DenseI64ArrayAttr enumValuesAttr;
1193 if (
const auto *enumTy = elemSvTy->as_if<slang::ast::EnumType>()) {
1194 if (enumTy->getBitWidth() > 64) {
1195 mlir::emitError(loc)
1196 <<
"unsupported: $readmem into enumeration wider than 64 bits";
1199 SmallVector<int64_t> vals;
1200 for (
const auto &ev : enumTy->values()) {
1201 auto v = ev.getValue().integer().as<int64_t>();
1203 mlir::emitError(loc)
1204 <<
"unsupported: $readmem enumeration value with unknown bits";
1209 enumValuesAttr = builder.getDenseI64ArrayAttr(vals);
1213 if (args.size() >= 3 &&
1214 args[2]->kind != slang::ast::ExpressionKind::EmptyArgument) {
1215 startAddr = context.convertRvalueExpression(*args[2], i32Ty);
1221 if (args.size() >= 4 &&
1222 args[3]->kind != slang::ast::ExpressionKind::EmptyArgument) {
1223 finishAddr = context.convertRvalueExpression(*args[3], i32Ty);
1228 auto base = isBinary ? moore::MemBase::Binary : moore::MemBase::Hex;
1229 moore::ReadMemBIOp::create(
1230 builder, loc, filename, dest,
1231 moore::MemBaseAttr::get(builder.getContext(), base), startAddr,
1232 finishAddr, sliceLeft, sliceRight,
1233 builder.getDenseI64ArrayAttr(dimLows),
1234 builder.getDenseBoolArrayAttr(dimDescs), enumValuesAttr);
1242 visitSystemCall(
const slang::ast::ExpressionStatement &stmt,
1243 const slang::ast::CallExpression &expr,
1244 const slang::ast::CallExpression::SystemCallInfo &info) {
1245 using ksn = slang::parsing::KnownSystemName;
1246 const auto &subroutine = *
info.subroutine;
1247 auto nameId = subroutine.knownNameId;
1248 auto args = expr.arguments();
1253 if (nameId == ksn::Cast) {
1259 if (nameId == ksn::Stop) {
1260 createFinishMessage(args.size() >= 1 ? args[0] :
nullptr);
1261 moore::StopBIOp::create(builder, loc);
1265 if (nameId == ksn::Finish) {
1266 createFinishMessage(args.size() >= 1 ? args[0] :
nullptr);
1267 moore::FinishBIOp::create(builder, loc, 0);
1268 moore::UnreachableOp::create(builder, loc);
1273 if (nameId == ksn::Exit) {
1282 if (nameId == ksn::PrintTimeScale) {
1283 auto message = moore::FormatLiteralOp::create(
1285 moore::DisplayBIOp::create(builder, loc, message);
1292 using moore::IntFormat;
1293 bool isDisplay =
false;
1294 bool isFDisplay =
false;
1295 bool isSWrite =
false;
1296 bool isSFormat =
false;
1297 bool appendNewline =
false;
1298 IntFormat defaultFormat = IntFormat::Decimal;
1302 appendNewline =
true;
1306 appendNewline =
true;
1307 defaultFormat = IntFormat::Binary;
1311 appendNewline =
true;
1312 defaultFormat = IntFormat::Octal;
1316 appendNewline =
true;
1317 defaultFormat = IntFormat::HexLower;
1324 defaultFormat = IntFormat::Binary;
1328 defaultFormat = IntFormat::Octal;
1332 defaultFormat = IntFormat::HexLower;
1336 appendNewline =
true;
1338 case ksn::FDisplayB:
1340 appendNewline =
true;
1341 defaultFormat = IntFormat::Binary;
1343 case ksn::FDisplayO:
1345 appendNewline =
true;
1346 defaultFormat = IntFormat::Octal;
1348 case ksn::FDisplayH:
1350 appendNewline =
true;
1351 defaultFormat = IntFormat::HexLower;
1358 defaultFormat = IntFormat::Binary;
1362 defaultFormat = IntFormat::Octal;
1366 defaultFormat = IntFormat::HexLower;
1376 defaultFormat = IntFormat::Binary;
1380 defaultFormat = IntFormat::Octal;
1384 defaultFormat = IntFormat::HexLower;
1392 context.convertFormatString(args, loc, defaultFormat, appendNewline);
1393 if (failed(message))
1395 if (*message == Value{})
1397 moore::DisplayBIOp::create(builder, loc, *message);
1402 assert(!args.empty() &&
"$fdisplay/$fwrite takes at least 1 argument");
1404 auto fd = context.convertRvalueExpression(
1405 *args[0], moore::IntType::getInt(builder.getContext(), 32));
1408 args = args.subspan(1);
1411 context.convertFormatString(args, loc, defaultFormat, appendNewline);
1412 if (failed(message))
1414 if (*message == Value{})
1416 moore::FDisplayBIOp::create(builder, loc, fd, *message);
1427 if (isSWrite || isSFormat) {
1428 if (isSFormat && args.size() < 2)
1429 return emitError(loc) <<
"$sformat requires at least 2 arguments";
1430 if (isSWrite && args.size() < 1)
1431 return emitError(loc) <<
"$swrite requires at least 1 argument";
1434 context.convertFormatString(args.subspan(1), loc, defaultFormat,
1436 if (failed(fmtValue))
1438 if (*fmtValue == Value{})
1441 moore::FormatStringToStringOp::create(builder, loc, *fmtValue);
1442 auto *lhsExpr = args[0];
1443 if (
auto *assignExpr =
1444 lhsExpr->as_if<slang::ast::AssignmentExpression>()) {
1445 auto lhs = context.convertLvalueExpression(assignExpr->left());
1448 auto convertedValue = context.materializeConversion(
1449 cast<moore::RefType>(lhs.getType()).getNestedType(), strValue,
1451 if (!convertedValue)
1453 moore::BlockingAssignOp::create(builder, loc, lhs, convertedValue);
1460 using moore::Severity;
1461 std::optional<Severity> severity;
1462 if (nameId == ksn::Info)
1463 severity = Severity::Info;
1464 else if (nameId == ksn::Warning)
1465 severity = Severity::Warning;
1466 else if (nameId == ksn::Error)
1467 severity = Severity::Error;
1468 else if (nameId == ksn::Fatal)
1469 severity = Severity::Fatal;
1473 const slang::ast::Expression *verbosityExpr =
nullptr;
1474 if (severity == Severity::Fatal && args.size() >= 1) {
1475 verbosityExpr = args[0];
1476 args = args.subspan(1);
1479 FailureOr<Value> maybeMessage = getDisplayMessage(args);
1480 if (failed(maybeMessage))
1482 auto message = maybeMessage.value();
1484 if (message == Value{})
1485 message = moore::FormatLiteralOp::create(builder, loc,
"");
1486 moore::SeverityBIOp::create(builder, loc, *severity, message);
1489 if (severity == Severity::Fatal) {
1490 createFinishMessage(verbosityExpr);
1491 moore::FinishBIOp::create(builder, loc, 1);
1492 moore::UnreachableOp::create(builder, loc);
1500 if (nameId == ksn::FClose) {
1501 assert(args.size() == 1 &&
"$fclose takes 1 argument");
1502 auto fd = context.convertRvalueExpression(
1503 *args[0], moore::IntType::getInt(builder.getContext(), 32));
1506 moore::FCloseBIOp::create(builder, loc, fd);
1510 if (nameId == ksn::FFlush) {
1511 assert(args.size() <= 1 &&
"$fflush takes at most 1 argument");
1513 if (args.size() == 1) {
1514 fd = context.convertRvalueExpression(
1515 *args[0], moore::IntType::getInt(builder.getContext(), 32));
1519 moore::FFlushBIOp::create(builder, loc, fd);
1523 if (nameId == ksn::ReadMemH || nameId == ksn::ReadMemB) {
1524 if (failed(convertReadMemTask(args, nameId == ksn::ReadMemB)))
1530 if (args.size() >= 1 && args[0]->type->isString()) {
1531 auto str = context.convertLvalueExpression(*args[0]);
1533 if (nameId == ksn::Putc) {
1535 assert(args.size() == 3 &&
"`putc` takes 3 arguments");
1536 auto index = context.convertRvalueExpression(*args[1]);
1537 auto character = context.convertRvalueExpression(*args[2]);
1538 moore::StringPutOp::create(builder, loc, str, index, character);
1542 if (nameId == ksn::IToA || nameId == ksn::HexToA ||
1543 nameId == ksn::OctToA || nameId == ksn::BinToA) {
1545 assert(args.size() == 2 &&
"`itoa/hex/oct/bin` takes 2 arguments");
1546 auto integerType = moore::IntType::getLogic(builder.getContext(), 32);
1547 auto input = context.convertRvalueExpression(*args[1], integerType);
1551 moore::StringItoaOp::create(builder, loc, str, input);
1554 moore::StringHextoaOp::create(builder, loc, str, input);
1557 moore::StringOcttoaOp::create(builder, loc, str, input);
1560 moore::StringBintoaOp::create(builder, loc, str, input);
1563 llvm_unreachable(
"unexpected ASCII integer to string conversion");
1569 if (nameId == ksn::RealToA) {
1571 assert(args.size() == 2 &&
"`realtoa` takes 2 arguments");
1574 auto input = context.convertRvalueExpression(*args[1], realType);
1575 moore::StringRealtoaOp::create(builder, loc, str, input);
1582 if (args.size() >= 1 && args[0]->type->isQueue()) {
1583 auto queue = context.convertLvalueExpression(*args[0]);
1587 if (nameId == ksn::Delete) {
1588 if (args.size() == 1) {
1589 moore::QueueClearOp::create(builder, loc, queue);
1592 if (args.size() == 2) {
1593 auto index = context.convertRvalueExpression(*args[1]);
1594 moore::QueueDeleteOp::create(builder, loc, queue, index);
1597 }
else if (nameId == ksn::Insert && args.size() == 3) {
1598 auto index = context.convertRvalueExpression(*args[1]);
1599 auto item = context.convertRvalueExpression(*args[2]);
1601 moore::QueueInsertOp::create(builder, loc, queue, index, item);
1603 }
else if (nameId == ksn::PushBack && args.size() == 2) {
1604 auto item = context.convertRvalueExpression(*args[1]);
1605 moore::QueuePushBackOp::create(builder, loc, queue, item);
1607 }
else if (nameId == ksn::PushFront && args.size() == 2) {
1608 auto item = context.convertRvalueExpression(*args[1]);
1609 moore::QueuePushFrontOp::create(builder, loc, queue, item);
1617 if (args.size() >= 1 && args[0]->type->isAssociativeArray()) {
1618 auto assocArray = context.convertLvalueExpression(*args[0]);
1623 if (nameId == ksn::Delete) {
1624 if (args.size() == 1) {
1625 moore::AssocArrayClearOp::create(builder, loc, assocArray);
1628 if (args.size() == 2) {
1629 auto index = context.convertRvalueExpression(*args[1]);
1630 moore::AssocArrayDeleteOp::create(builder, loc, assocArray, index);
1637 if (nameId == ksn::MonitorOn || nameId == ksn::MonitorOff) {
1638 context.ensureMonitorGlobals();
1639 bool enable = (nameId == ksn::MonitorOn);
1640 auto enabledRef = moore::GetGlobalVariableOp::create(
1641 context.builder, loc, context.monitorEnabledGlobal);
1642 auto value = moore::ConstantOp::create(context.builder, loc,
1644 moore::BlockingAssignOp::create(context.builder, loc, enabledRef, value);
1649 if (nameId == ksn::Monitor || nameId == ksn::MonitorB ||
1650 nameId == ksn::MonitorO || nameId == ksn::MonitorH) {
1651 context.ensureMonitorGlobals();
1654 unsigned myId = context.nextMonitorId++;
1657 auto i32Type = moore::IntType::getInt(context.getContext(), 32);
1659 moore::ConstantOp::create(context.builder, loc, i32Type, myId);
1660 auto activeRef = moore::GetGlobalVariableOp::create(
1661 context.builder, loc, context.monitorActiveIdGlobal);
1662 moore::BlockingAssignOp::create(context.builder, loc, activeRef, idConst);
1665 context.pendingMonitors.push_back({myId, loc, &expr});
1670 if (nameId == ksn::TimeFormat) {
1671 context.ensureTimeFormatGlobal();
1672 auto i32Ty = moore::IntType::getInt(context.getContext(), 32);
1673 auto strTy = moore::StringType::get(context.getContext());
1677 context.getContext());
1678 std::array<StringRef, 4> argNames = {
"unit",
"precision",
"suffix",
1680 for (
auto [name, value] : llvm::zip(argNames, defaults)) {
1681 auto base = moore::GetGlobalVariableOp::create(
1682 context.builder, loc, context.timeFormatGlobal);
1683 auto fieldRef = moore::StructExtractRefOp::create(
1684 context.builder, loc,
1685 moore::RefType::get(cast<moore::UnpackedType>(value.getType())),
1686 StringAttr::get(context.getContext(), name), base);
1687 moore::BlockingAssignOp::create(context.builder, loc, fieldRef,
1693 std::array<std::pair<StringRef, Type>, 4> argsTypes = {{
1695 {
"precision", i32Ty},
1697 {
"min_width", i32Ty},
1700 for (
auto [i, arg] : llvm::enumerate(argsTypes)) {
1701 if (args.size() <= i)
1703 auto value = context.convertRvalueExpression(*args[i], arg.second);
1707 auto base = moore::GetGlobalVariableOp::create(
1708 context.builder, loc, context.timeFormatGlobal);
1709 auto fieldRef = moore::StructExtractRefOp::create(
1710 context.builder, loc,
1711 moore::RefType::get(cast<moore::UnpackedType>(arg.second)),
1712 StringAttr::get(context.getContext(), arg.first), base);
1713 moore::BlockingAssignOp::create(context.builder, loc, fieldRef, value);
1724 void createFinishMessage(
const slang::ast::Expression *verbosityExpr) {
1725 unsigned verbosity = 1;
1726 if (verbosityExpr) {
1728 context.evaluateConstant(*verbosityExpr).integer().as<
unsigned>();
1729 assert(value &&
"Slang guarantees constant verbosity parameter");
1734 moore::FinishMessageBIOp::create(builder, loc, verbosity > 1);
1738 LogicalResult visit(
const slang::ast::EventTriggerStatement &stmt) {
1740 mlir::emitError(loc) <<
"unsupported delayed event trigger";
1746 auto target = context.convertLvalueExpression(stmt.target);
1752 Value inverted = moore::ReadOp::create(builder, loc, target);
1753 inverted = moore::NotOp::create(builder, loc, inverted);
1755 if (stmt.isNonBlocking)
1756 moore::NonBlockingAssignOp::create(builder, loc, target, inverted);
1758 moore::BlockingAssignOp::create(builder, loc, target, inverted);
1763 LogicalResult visit(
const slang::ast::WaitStatement &stmt) {
1764 auto waitOp = moore::WaitLevelOp::create(builder, loc);
1766 OpBuilder::InsertionGuard guard(builder);
1767 builder.setInsertionPointToStart(&waitOp.getBody().emplaceBlock());
1768 auto cond = context.convertRvalueExpression(stmt.cond);
1771 cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
1772 moore::DetectLevelOp::create(builder, loc, cond);
1775 if (failed(context.convertStatement(stmt.stmt)))
1781 LogicalResult visit(
const slang::ast::WaitForkStatement &stmt) {
1782 moore::WaitForkOp::create(builder, loc);
1787 template <
typename T>
1788 LogicalResult visit(T &&stmt) {
1789 mlir::emitError(loc,
"unsupported statement: ")
1790 << slang::ast::toString(stmt.kind);
1791 return mlir::failure();
1794 LogicalResult visitInvalid(
const slang::ast::Statement &stmt) {
1795 mlir::emitError(loc,
"invalid statement: ")
1796 << slang::ast::toString(stmt.kind);
1797 return mlir::failure();
1819 OpBuilder::InsertionGuard guard(
builder);
1823 auto i32Type = moore::IntType::getInt(
getContext(), 32);
1824 auto i1Type = moore::IntType::getInt(
getContext(), 1);
1829 builder, loc,
"__monitor_active_id", {}, i32Type);
1831 OpBuilder::InsertionGuard initGuard(
builder);
1832 builder.setInsertionPointToStart(
1834 auto zero = moore::ConstantOp::create(
builder, loc, i32Type, 0);
1835 moore::YieldOp::create(
builder, loc, zero);
1841 builder, loc,
"__monitor_enabled", {}, i1Type);
1843 OpBuilder::InsertionGuard initGuard(
builder);
1844 builder.setInsertionPointToStart(
1848 moore::YieldOp::create(
builder, loc, trueVal);
1854 using ksn = slang::parsing::KnownSystemName;
1856 auto &call = *pending.call;
1857 auto loc = pending.loc;
1861 std::get<slang::ast::CallExpression::SystemCallInfo>(call.subroutine);
1862 auto nameId = info.subroutine->knownNameId;
1865 auto defaultFormat = moore::IntFormat::Decimal;
1868 defaultFormat = moore::IntFormat::Binary;
1871 defaultFormat = moore::IntFormat::Octal;
1874 defaultFormat = moore::IntFormat::HexLower;
1883 auto alwaysProc = moore::ProcedureOp::create(
1884 builder, loc, moore::ProcedureKind::AlwaysComb);
1885 OpBuilder::InsertionGuard guard(
builder);
1886 builder.setInsertionPointToStart(&alwaysProc.getBody().emplaceBlock());
1891 if (failed(message))
1895 auto i32Type = moore::IntType::getInt(
getContext(), 32);
1896 auto myId = moore::ConstantOp::create(
builder, loc, i32Type, pending.id);
1899 isActive = moore::ReadOp::create(
builder, loc, isActive);
1900 isActive = moore::EqOp::create(
builder, loc, isActive, myId);
1904 enabled = moore::ReadOp::create(
builder, loc, enabled);
1905 enabled = moore::AndOp::create(
builder, loc, isActive, enabled);
1906 enabled = moore::ToBuiltinIntOp::create(
builder, loc, enabled);
1910 auto &printBlock = alwaysProc.getBody().emplaceBlock();
1911 auto &skipBlock = alwaysProc.getBody().emplaceBlock();
1912 cf::CondBranchOp::create(
builder, loc, enabled, &printBlock, &skipBlock);
1916 builder.setInsertionPointToStart(&printBlock);
1918 moore::DisplayBIOp::create(
builder, loc, *message);
1919 moore::ReturnOp::create(
builder, loc);
1922 builder.setInsertionPointToStart(&skipBlock);
1923 moore::ReturnOp::create(
builder, loc);
1937 OpBuilder::InsertionGuard guard(
builder);
1941 auto i32Ty = moore::IntType::getInt(
getContext(), 32);
1942 auto strTy = moore::StringType::get(
getContext());
1944 SmallVector<moore::StructLikeMember> members{
1945 {StringAttr::get(
getContext(),
"unit"), i32Ty},
1946 {StringAttr::get(
getContext(),
"precision"), i32Ty},
1947 {StringAttr::get(
getContext(),
"suffix"), strTy},
1948 {StringAttr::get(
getContext(),
"min_width"), i32Ty},
1950 auto structTy = moore::UnpackedStructType::get(
getContext(), members);
1953 builder, loc,
"__timeformat_state", {}, structTy);
1955 OpBuilder::InsertionGuard initGuard(
builder);
1956 builder.setInsertionPointToStart(
1959 auto init = moore::StructCreateOp::create(
builder, loc, structTy,
1960 ValueRange(defaults));
1961 moore::YieldOp::create(
builder, loc, init);
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
false return failure()
Parse an optional keyword or string and set instance into 'result'.
static FailureOr< Value > getRuntimeSizeAtLevel(Context &context, Location loc, const slang::ast::ForeachLoopStatement &stmt, uint32_t level, const moore::IntType &idxType)
static std::string buildPrintTimeScaleMessage(Context &context, std::span< const slang::ast::Expression *const > args)
Build the message printed by the $printtimescale system task.
static std::array< Value, 4 > getDefaultTimeFormatValues(OpBuilder &builder, Location loc, MLIRContext *context)
static FVInt getZero(unsigned numBits)
Construct an FVInt with all bits set to 0.
This helps visit TypeOp nodes.
Value getSelectIndex(Context &context, Location loc, Value index, const slang::ConstantRange &range)
Map an index into an array, with bounds range, to a bit offset of the underlying bit storage.
@ FourValued
Four-valued types such as logic or integer.
@ TwoValued
Two-valued types such as bit or int.
@ f64
A 64-bit double-precision floation point number ("double").
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< PendingMonitor > pendingMonitors
Pending $monitor calls that need to be converted at module level.
LogicalResult flushPendingMonitors()
Process any pending $monitor calls and generate the monitoring procedures at module level.
OpBuilder builder
The builder used to create IR operations.
void ensureTimeFormatGlobal()
Ensure that the global variable for $timeformat state exists.
void ensureMonitorGlobals()
Ensure that the global variables for $monitor state exist.
FailureOr< Value > convertFormatString(std::span< const slang::ast::Expression *const > arguments, Location loc, moore::IntFormat defaultFormat=moore::IntFormat::Decimal, bool appendNewline=false)
Convert a list of string literal arguments with formatting specifiers and arguments to be interpolate...
moore::GlobalVariableOp monitorActiveIdGlobal
Global variable ops for $monitor state management.
moore::GlobalVariableOp monitorEnabledGlobal
moore::GlobalVariableOp timeFormatGlobal
Global variable ops for $timeformat state management.
mlir::ModuleOp intoModuleOp
SymbolTable symbolTable
A symbol table of the MLIR module we are emitting into.
MLIRContext * getContext()
Return the MLIR context.
LogicalResult convertStatement(const slang::ast::Statement &stmt)
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.