[ImportVerilog] Switch from SCF to CF dialect for control flow (#7432)

Instead of using the SCF dialect and its `scf.if` and `scf.while`
operations for control flow, switch over to the CF dialect and its
`cf.br` and `cf.cond_br`. This allows us to support SystemVerilog's
`continue`, `break`, and `return` statements which don't map well to the
Structured Control Flow dialect.
This commit is contained in:
Fabian Schuiki 2024-08-05 17:10:19 -07:00 committed by GitHub
parent bec0deab4b
commit 5cc69bd6b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 646 additions and 359 deletions

View File

@ -131,8 +131,6 @@ def ProcedureKindAttr: I32EnumAttr<"ProcedureKind", "Procedure kind",
}
def ProcedureOp : MooreOp<"procedure", [
SingleBlock,
NoTerminator,
NoRegionArguments,
RecursiveMemoryEffects,
RecursivelySpeculatable
@ -172,15 +170,22 @@ def ProcedureOp : MooreOp<"procedure", [
See IEEE 1800-2017 § 9.2 "Structured procedures".
}];
let regions = (region SizedRegion<1>:$bodyRegion);
let arguments = (ins ProcedureKindAttr:$kind);
let results = (outs);
let regions = (region AnyRegion:$body);
let assemblyFormat = [{
$kind attr-dict-with-keyword $bodyRegion
$kind attr-dict-with-keyword $body
}];
}
def ReturnOp : MooreOp<"return", [
Pure, Terminator, HasParent<"ProcedureOp">
]> {
let summary = "Return from a procedure";
let assemblyFormat = [{ attr-dict }];
}
//===----------------------------------------------------------------------===//
// Declarations
//===----------------------------------------------------------------------===//
@ -812,6 +817,16 @@ class CaseEqOpBase<string mnemonic> : MooreOp<mnemonic, [
`case_ne` returns 1). `case_eq` corresponds to the `===` operator and
`case_ne` to the `!==` operator.
`casez_eq` treats Z bits in either operand as wildcards and skips them
during the comparison. `casexz_eq` treats X and Z bits as wildcards. These
are different from the `wildcard_eq` operation, which only considers X/Z in
the right-hand operand as wildcards.
Case statements use this operation to perform case comparisons:
- `case` statements use `case_eq`
- `casez` statements use `casez_eq`
- `casex` statements use `casexz_eq`
See IEEE 1800-2017 § 11.4.5 "Equality operators".
}];
let arguments = (ins SimpleBitVectorType:$lhs, SimpleBitVectorType:$rhs);
@ -823,6 +838,12 @@ class CaseEqOpBase<string mnemonic> : MooreOp<mnemonic, [
def CaseEqOp : CaseEqOpBase<"case_eq"> { let summary = "Case equality"; }
def CaseNeOp : CaseEqOpBase<"case_ne"> { let summary = "Case inequality"; }
def CaseZEqOp : CaseEqOpBase<"casez_eq"> {
let summary = "Case equality with Z as wildcard";
}
def CaseXZEqOp : CaseEqOpBase<"casexz_eq"> {
let summary = "Case equality with X and Z as wildcard";
}
class WildcardEqOpBase<string mnemonic> : MooreOp<mnemonic, [
Pure,
@ -1259,4 +1280,5 @@ def YieldOp : MooreOp<"yield", [
}];
let hasVerifier = 1;
}
#endif // CIRCT_DIALECT_MOORE_MOOREOPS

View File

@ -264,8 +264,8 @@ LogicalResult ImportDriver::importVerilog(ModuleOp module) {
return success();
// Traverse the parsed Verilog AST and map it to the equivalent CIRCT ops.
mlirContext->loadDialect<moore::MooreDialect, hw::HWDialect, scf::SCFDialect,
func::FuncDialect>();
mlirContext->loadDialect<moore::MooreDialect, hw::HWDialect,
cf::ControlFlowDialect, func::FuncDialect>();
auto conversionTimer = ts.nest("Verilog to dialect mapping");
Context context(*compilation, module, driver.sourceManager, bufferFilePaths);
if (failed(context.convertCompilation()))

View File

@ -13,8 +13,8 @@
#include "circt/Conversion/ImportVerilog.h"
#include "circt/Dialect/HW/HWOps.h"
#include "circt/Dialect/Moore/MooreOps.h"
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
#include "slang/ast/ASTVisitor.h"
#include "llvm/ADT/ScopedHashTable.h"
#include "llvm/Support/Debug.h"
@ -46,6 +46,15 @@ struct FunctionLowering {
mlir::func::FuncOp op;
};
/// Information about a loops continuation and exit blocks relevant while
/// lowering the loop's body statements.
struct LoopFrame {
/// The block to jump to from a `continue` statement.
Block *continueBlock;
/// The block to jump to from a `break` statement.
Block *breakBlock;
};
/// A helper class to facilitate the conversion from a Slang AST to MLIR
/// operations. Keeps track of the destination MLIR module, builders, and
/// various worklists and utilities needed for conversion.
@ -136,6 +145,13 @@ struct Context {
/// side. This allows expressions to resolve the opaque
/// `LValueReferenceExpression`s in the AST.
SmallVector<Value> lvalueStack;
/// A stack of loop continuation and exit blocks. Each loop will push the
/// relevant info onto this stack, lower its loop body statements, and pop the
/// info off the stack again. Continue and break statements encountered as
/// part of the loop body statements will use this information to branch to
/// the correct block.
SmallVector<LoopFrame> loopStack;
};
} // namespace ImportVerilog

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "ImportVerilogInternals.h"
#include "llvm/ADT/ScopeExit.h"
using namespace mlir;
using namespace circt;
@ -22,6 +23,16 @@ struct StmtVisitor {
StmtVisitor(Context &context, Location loc)
: context(context), loc(loc), builder(context.builder) {}
bool isTerminated() const { return !builder.getInsertionBlock(); }
void setTerminated() { builder.clearInsertionPoint(); }
Block &createBlock() {
assert(builder.getInsertionBlock());
auto block = std::make_unique<Block>();
block->insertAfter(builder.getInsertionBlock());
return *block.release();
}
// Skip empty statements (stray semicolons).
LogicalResult visit(const slang::ast::EmptyStatement &) { return success(); }
@ -33,9 +44,15 @@ struct StmtVisitor {
// which in turn has a single body statement, which then commonly is a list of
// statements.
LogicalResult visit(const slang::ast::StatementList &stmts) {
for (auto *stmt : stmts.list)
for (auto *stmt : stmts.list) {
if (isTerminated()) {
auto loc = context.convertLocation(stmt->sourceRange);
mlir::emitWarning(loc, "unreachable code");
break;
}
if (failed(context.convertStatement(*stmt)))
return failure();
}
return success();
}
@ -104,81 +121,123 @@ struct StmtVisitor {
allConds =
builder.create<moore::ConversionOp>(loc, builder.getI1Type(), allConds);
// Generate the if operation.
auto ifOp =
builder.create<scf::IfOp>(loc, allConds, stmt.ifFalse != nullptr);
OpBuilder::InsertionGuard guard(builder);
// Create the blocks for the true and false branches, and the exit block.
Block &exitBlock = createBlock();
Block *falseBlock = stmt.ifFalse ? &createBlock() : nullptr;
Block &trueBlock = createBlock();
builder.create<cf::CondBranchOp>(loc, allConds, &trueBlock,
falseBlock ? falseBlock : &exitBlock);
// Generate the "then" body.
builder.setInsertionPoint(ifOp.thenYield());
// Generate the true branch.
builder.setInsertionPointToEnd(&trueBlock);
if (failed(context.convertStatement(stmt.ifTrue)))
return failure();
if (!isTerminated())
builder.create<cf::BranchOp>(loc, &exitBlock);
// Generate the "else" body if present.
// Generate the false branch if present.
if (stmt.ifFalse) {
builder.setInsertionPoint(ifOp.elseYield());
builder.setInsertionPointToEnd(falseBlock);
if (failed(context.convertStatement(*stmt.ifFalse)))
return failure();
if (!isTerminated())
builder.create<cf::BranchOp>(loc, &exitBlock);
}
// If control never reaches the exit block, remove it and mark control flow
// as terminated. Otherwise we continue inserting ops in the exit block.
if (exitBlock.hasNoPredecessors()) {
exitBlock.erase();
setTerminated();
} else {
builder.setInsertionPointToEnd(&exitBlock);
}
return success();
}
// Handle case statements.
LogicalResult visit(const slang::ast::CaseStatement &caseStmt) {
using slang::ast::CaseStatementCondition;
auto caseExpr = context.convertRvalueExpression(caseStmt.expr);
if (!caseExpr)
return failure();
auto items = caseStmt.items;
// Used to generate the condition of the default case statement.
SmallVector<Value> defaultConds;
// Traverse the case items.
for (auto item : items) {
// One statement will be matched with multi-conditions.
// Like case(cond) 0, 1 : y = x; endcase.
SmallVector<Value> allConds;
for (const auto *expr : item.expressions) {
auto itemExpr = context.convertRvalueExpression(*expr);
if (!itemExpr)
return failure();
// Check each case individually. This currently ignores the `unique`,
// `unique0`, and `priority` modifiers which would allow for additional
// optimizations.
auto &exitBlock = createBlock();
auto newEqOp = builder.create<moore::EqOp>(loc, caseExpr, itemExpr);
allConds.push_back(newEqOp);
for (const auto &item : caseStmt.items) {
// Create the block that will contain the main body of the expression.
// This is where any of the comparisons will branch to if they match.
auto &matchBlock = createBlock();
// The SV standard requires expressions to be checked in the order
// specified by the user, and for the evaluation to stop as soon as the
// first matching expression is encountered.
for (const auto *expr : item.expressions) {
auto value = context.convertRvalueExpression(*expr);
if (!value)
return failure();
auto itemLoc = value.getLoc();
// Generate the appropriate equality operator.
Value cond;
switch (caseStmt.condition) {
case CaseStatementCondition::Normal:
cond = builder.create<moore::CaseEqOp>(itemLoc, caseExpr, value);
break;
case CaseStatementCondition::WildcardXOrZ:
cond = builder.create<moore::CaseXZEqOp>(itemLoc, caseExpr, value);
break;
case CaseStatementCondition::WildcardJustZ:
cond = builder.create<moore::CaseZEqOp>(itemLoc, caseExpr, value);
break;
case CaseStatementCondition::Inside:
mlir::emitError(loc, "unsupported set membership case statement");
return failure();
}
cond = builder.create<moore::ConversionOp>(itemLoc, builder.getI1Type(),
cond);
// If the condition matches, branch to the match block. Otherwise
// continue checking the next expression in a new block.
auto &nextBlock = createBlock();
builder.create<mlir::cf::CondBranchOp>(itemLoc, cond, &matchBlock,
&nextBlock);
builder.setInsertionPointToEnd(&nextBlock);
}
// Bound all conditions of an item into one.
auto cond = allConds.back();
allConds.pop_back();
while (!allConds.empty()) {
cond = builder.create<moore::OrOp>(loc, allConds.back(), cond);
allConds.pop_back();
}
// Gather all items' conditions.
defaultConds.push_back(cond);
cond =
builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
auto ifOp = builder.create<mlir::scf::IfOp>(loc, cond);
// The current block is the fall-through after all conditions have been
// checked and nothing matched. Move the match block up before this point
// to make the IR easier to read.
matchBlock.moveBefore(builder.getInsertionBlock());
// Generate the code for this item's statement in the match block.
OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPoint(ifOp.thenYield());
builder.setInsertionPointToEnd(&matchBlock);
if (failed(context.convertStatement(*item.stmt)))
return failure();
}
// Handle the 'default case' statement if it exists.
if (caseStmt.defaultCase) {
auto cond = defaultConds.back();
defaultConds.pop_back();
while (!defaultConds.empty()) {
cond = builder.create<moore::OrOp>(loc, defaultConds.back(), cond);
defaultConds.pop_back();
if (!isTerminated()) {
auto loc = context.convertLocation(item.stmt->sourceRange);
builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
}
cond = builder.create<moore::NotOp>(loc, cond);
cond =
builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
auto ifOp = builder.create<mlir::scf::IfOp>(loc, cond);
OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPoint(ifOp.thenYield());
}
// Generate the default case if present.
if (caseStmt.defaultCase)
if (failed(context.convertStatement(*caseStmt.defaultCase)))
return failure();
if (!isTerminated())
builder.create<mlir::cf::BranchOp>(loc, &exitBlock);
// If control never reaches the exit block, remove it and mark control flow
// as terminated. Otherwise we continue inserting ops in the exit block.
if (exitBlock.hasNoPredecessors()) {
exitBlock.erase();
setTerminated();
} else {
builder.setInsertionPointToEnd(&exitBlock);
}
return success();
}
@ -190,127 +249,179 @@ struct StmtVisitor {
if (!context.convertRvalueExpression(*initExpr))
return failure();
// Create the while op.
auto whileOp = builder.create<scf::WhileOp>(loc, TypeRange{}, ValueRange{});
OpBuilder::InsertionGuard guard(builder);
// Create the blocks for the loop condition, body, step, and exit.
auto &exitBlock = createBlock();
auto &stepBlock = createBlock();
auto &bodyBlock = createBlock();
auto &checkBlock = createBlock();
builder.create<cf::BranchOp>(loc, &checkBlock);
// In the "before" region, check that the condition holds.
builder.createBlock(&whileOp.getBefore());
// Push the blocks onto the loop stack such that we can continue and break.
context.loopStack.push_back({&stepBlock, &exitBlock});
auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
// Generate the loop condition check.
builder.setInsertionPointToEnd(&checkBlock);
auto cond = context.convertRvalueExpression(*stmt.stopExpr);
if (!cond)
return failure();
cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
builder.create<mlir::scf::ConditionOp>(loc, cond, ValueRange{});
builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
// In the "after" region, generate the loop body and step expressions.
builder.createBlock(&whileOp.getAfter());
// Generate the loop body.
builder.setInsertionPointToEnd(&bodyBlock);
if (failed(context.convertStatement(stmt.body)))
return failure();
if (!isTerminated())
builder.create<cf::BranchOp>(loc, &stepBlock);
// Generate the step expressions.
builder.setInsertionPointToEnd(&stepBlock);
for (auto *stepExpr : stmt.steps)
if (!context.convertRvalueExpression(*stepExpr))
return failure();
builder.create<mlir::scf::YieldOp>(loc);
if (!isTerminated())
builder.create<cf::BranchOp>(loc, &checkBlock);
// If control never reaches the exit block, remove it and mark control flow
// as terminated. Otherwise we continue inserting ops in the exit block.
if (exitBlock.hasNoPredecessors()) {
exitBlock.erase();
setTerminated();
} else {
builder.setInsertionPointToEnd(&exitBlock);
}
return success();
}
// Handle `repeat` loops.
LogicalResult visit(const slang::ast::RepeatLoopStatement &stmt) {
// Create the while op and feed in the repeat count as the initial counter
// value.
auto count = context.convertRvalueExpression(stmt.count);
if (!count)
return failure();
auto type = cast<moore::IntType>(count.getType());
auto whileOp = builder.create<scf::WhileOp>(loc, type, count);
OpBuilder::InsertionGuard guard(builder);
// In the "before" region, check that the counter is non-zero.
auto *block = builder.createBlock(&whileOp.getBefore(), {}, type, loc);
auto counterArg = block->getArgument(0);
auto cond = builder.createOrFold<moore::BoolCastOp>(loc, counterArg);
// Create the blocks for the loop condition, body, step, and exit.
auto &exitBlock = createBlock();
auto &stepBlock = createBlock();
auto &bodyBlock = createBlock();
auto &checkBlock = createBlock();
auto currentCount = checkBlock.addArgument(count.getType(), count.getLoc());
builder.create<cf::BranchOp>(loc, &checkBlock, count);
// Push the blocks onto the loop stack such that we can continue and break.
context.loopStack.push_back({&stepBlock, &exitBlock});
auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
// Generate the loop condition check.
builder.setInsertionPointToEnd(&checkBlock);
auto cond = builder.createOrFold<moore::BoolCastOp>(loc, currentCount);
cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
builder.create<scf::ConditionOp>(loc, cond, counterArg);
builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
// In the "after" region, generate the loop body and decrement the counter.
block = builder.createBlock(&whileOp.getAfter(), {}, type, loc);
// Generate the loop body.
builder.setInsertionPointToEnd(&bodyBlock);
if (failed(context.convertStatement(stmt.body)))
return failure();
counterArg = block->getArgument(0);
auto constOne = builder.create<moore::ConstantOp>(loc, type, 1);
auto subOp = builder.create<moore::SubOp>(loc, counterArg, constOne);
builder.create<scf::YieldOp>(loc, ValueRange{subOp});
if (!isTerminated())
builder.create<cf::BranchOp>(loc, &stepBlock);
// Decrement the current count and branch back to the check block.
builder.setInsertionPointToEnd(&stepBlock);
auto one = builder.create<moore::ConstantOp>(
count.getLoc(), cast<moore::IntType>(count.getType()), 1);
Value nextCount =
builder.create<moore::SubOp>(count.getLoc(), currentCount, one);
builder.create<cf::BranchOp>(loc, &checkBlock, nextCount);
// If control never reaches the exit block, remove it and mark control flow
// as terminated. Otherwise we continue inserting ops in the exit block.
if (exitBlock.hasNoPredecessors()) {
exitBlock.erase();
setTerminated();
} else {
builder.setInsertionPointToEnd(&exitBlock);
}
return success();
}
// Handle `while` and `do-while` loops.
LogicalResult createWhileLoop(const slang::ast::Expression &condExpr,
const slang::ast::Statement &bodyStmt,
bool atLeastOnce) {
// Create the blocks for the loop condition, body, and exit.
auto &exitBlock = createBlock();
auto &bodyBlock = createBlock();
auto &checkBlock = createBlock();
builder.create<cf::BranchOp>(loc, atLeastOnce ? &bodyBlock : &checkBlock);
if (atLeastOnce)
bodyBlock.moveBefore(&checkBlock);
// Push the blocks onto the loop stack such that we can continue and break.
context.loopStack.push_back({&checkBlock, &exitBlock});
auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
// Generate the loop condition check.
builder.setInsertionPointToEnd(&checkBlock);
auto cond = context.convertRvalueExpression(condExpr);
if (!cond)
return failure();
cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
builder.create<cf::CondBranchOp>(loc, cond, &bodyBlock, &exitBlock);
// Generate the loop body.
builder.setInsertionPointToEnd(&bodyBlock);
if (failed(context.convertStatement(bodyStmt)))
return failure();
if (!isTerminated())
builder.create<cf::BranchOp>(loc, &checkBlock);
// If control never reaches the exit block, remove it and mark control flow
// as terminated. Otherwise we continue inserting ops in the exit block.
if (exitBlock.hasNoPredecessors()) {
exitBlock.erase();
setTerminated();
} else {
builder.setInsertionPointToEnd(&exitBlock);
}
return success();
}
// Handle `while` loops.
LogicalResult visit(const slang::ast::WhileLoopStatement &stmt) {
// Create the while op.
auto whileOp = builder.create<scf::WhileOp>(loc, TypeRange{}, ValueRange{});
OpBuilder::InsertionGuard guard(builder);
// In the "before" region, check that the condition holds.
builder.createBlock(&whileOp.getBefore());
auto cond = context.convertRvalueExpression(stmt.cond);
if (!cond)
return failure();
cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
builder.create<mlir::scf::ConditionOp>(loc, cond, ValueRange{});
// In the "after" region, generate the loop body.
builder.createBlock(&whileOp.getAfter());
if (failed(context.convertStatement(stmt.body)))
return failure();
builder.create<mlir::scf::YieldOp>(loc);
return success();
return createWhileLoop(stmt.cond, stmt.body, false);
}
// Handle `do ... while` loops.
LogicalResult visit(const slang::ast::DoWhileLoopStatement &stmt) {
// Create the while op.
auto whileOp = builder.create<scf::WhileOp>(loc, TypeRange{}, ValueRange{});
OpBuilder::InsertionGuard guard(builder);
// In the "before" region, generate the loop body and check that the
// condition holds.
builder.createBlock(&whileOp.getBefore());
if (failed(context.convertStatement(stmt.body)))
return failure();
auto cond = context.convertRvalueExpression(stmt.cond);
if (!cond)
return failure();
cond = builder.createOrFold<moore::BoolCastOp>(loc, cond);
cond = builder.create<moore::ConversionOp>(loc, builder.getI1Type(), cond);
builder.create<mlir::scf::ConditionOp>(loc, cond, ValueRange{});
// Generate an empty "after" region.
builder.createBlock(&whileOp.getAfter());
builder.create<mlir::scf::YieldOp>(loc);
return success();
return createWhileLoop(stmt.cond, stmt.body, true);
}
// Handle `forever` loops.
LogicalResult visit(const slang::ast::ForeverLoopStatement &stmt) {
// Create the while op.
auto whileOp = builder.create<scf::WhileOp>(loc, TypeRange{}, ValueRange{});
OpBuilder::InsertionGuard guard(builder);
// Create the blocks for the loop body and exit.
auto &exitBlock = createBlock();
auto &bodyBlock = createBlock();
builder.create<cf::BranchOp>(loc, &bodyBlock);
// In the "before" region, return true for the condition.
builder.createBlock(&whileOp.getBefore());
auto cond = builder.create<hw::ConstantOp>(loc, builder.getI1Type(), 1);
builder.create<mlir::scf::ConditionOp>(loc, cond, ValueRange{});
// Push the blocks onto the loop stack such that we can continue and break.
context.loopStack.push_back({&bodyBlock, &exitBlock});
auto done = llvm::make_scope_exit([&] { context.loopStack.pop_back(); });
// In the "after" region, generate the loop body.
builder.createBlock(&whileOp.getAfter());
// Generate the loop body.
builder.setInsertionPointToEnd(&bodyBlock);
if (failed(context.convertStatement(stmt.body)))
return failure();
builder.create<mlir::scf::YieldOp>(loc);
if (!isTerminated())
builder.create<cf::BranchOp>(loc, &bodyBlock);
// If control never reaches the exit block, remove it and mark control flow
// as terminated. Otherwise we continue inserting ops in the exit block.
if (exitBlock.hasNoPredecessors()) {
exitBlock.erase();
setTerminated();
} else {
builder.setInsertionPointToEnd(&exitBlock);
}
return success();
}
@ -325,13 +436,34 @@ struct StmtVisitor {
// Handle return statements.
LogicalResult visit(const slang::ast::ReturnStatement &stmt) {
Value expr;
if (stmt.expr) {
expr = context.convertRvalueExpression(*stmt.expr);
auto expr = context.convertRvalueExpression(*stmt.expr);
if (!expr)
return failure();
builder.create<mlir::func::ReturnOp>(loc, expr);
} else {
builder.create<mlir::func::ReturnOp>(loc);
}
builder.create<mlir::func::ReturnOp>(loc, expr);
setTerminated();
return success();
}
// Handle continue statements.
LogicalResult visit(const slang::ast::ContinueStatement &stmt) {
if (context.loopStack.empty())
return mlir::emitError(loc,
"cannot `continue` without a surrounding loop");
builder.create<cf::BranchOp>(loc, context.loopStack.back().continueBlock);
setTerminated();
return success();
}
// Handle break statements.
LogicalResult visit(const slang::ast::BreakStatement &stmt) {
if (context.loopStack.empty())
return mlir::emitError(loc, "cannot `break` without a surrounding loop");
builder.create<cf::BranchOp>(loc, context.loopStack.back().breakBlock);
setTerminated();
return success();
}
@ -352,6 +484,7 @@ struct StmtVisitor {
} // namespace
LogicalResult Context::convertStatement(const slang::ast::Statement &stmt) {
assert(builder.getInsertionBlock());
auto loc = convertLocation(stmt.sourceRange);
return stmt.visit(StmtVisitor(*this, loc));
}

View File

@ -440,11 +440,14 @@ struct ModuleVisitor : public BaseVisitor {
LogicalResult visit(const slang::ast::ProceduralBlockSymbol &procNode) {
auto procOp = builder.create<moore::ProcedureOp>(
loc, convertProcedureKind(procNode.procedureKind));
procOp.getBodyRegion().emplaceBlock();
OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToEnd(procOp.getBody());
builder.setInsertionPointToEnd(&procOp.getBody().emplaceBlock());
Context::ValueSymbolScope scope(context.valueSymbols);
return context.convertStatement(procNode.getBody());
if (failed(context.convertStatement(procNode.getBody())))
return failure();
if (builder.getBlock())
builder.create<moore::ReturnOp>(loc);
return success();
}
// Handle parameters.
@ -913,15 +916,15 @@ Context::convertFunction(const slang::ast::SubroutineSymbol &subroutine) {
// If there was no explicit return statement provided by the user, insert a
// default one.
if (block.empty() || !block.back().hasTrait<OpTrait::IsTerminator>()) {
if (builder.getBlock()) {
if (returnVar && !subroutine.getReturnType().isVoid()) {
returnVar = builder.create<moore::ReadOp>(returnVar.getLoc(), returnVar);
builder.create<mlir::func::ReturnOp>(lowering->op.getLoc(), returnVar);
Value read = builder.create<moore::ReadOp>(returnVar.getLoc(), returnVar);
builder.create<mlir::func::ReturnOp>(lowering->op.getLoc(), read);
} else {
builder.create<mlir::func::ReturnOp>(lowering->op.getLoc(), ValueRange{});
}
}
if (returnVar.use_empty())
if (returnVar && returnVar.use_empty())
returnVar.getDefiningOp()->erase();
return success();
}

View File

@ -44,7 +44,8 @@ void SimplifyProceduresPass::runOnOperation() {
// Use to collect blocking assignments that have been replaced by a "shadow"
// variable.
DenseSet<Operation *> assignOps;
for (auto &nestedOp : procedureOp) {
procedureOp.walk([&](Operation *op) {
auto &nestedOp = *op;
// Only create a "shadow" varaible for the global variable used by other
// operations in the procedure body.
if (isa<ReadOp>(nestedOp) &&
@ -54,14 +55,14 @@ void SimplifyProceduresPass::runOnOperation() {
DenseSet<Operation *> users;
for (auto *user : nestedOp.getOperand(0).getUsers())
// Ensuring don't handle the users existing in another procedure body.
if (user->getBlock() == procedureOp.getBody())
if (procedureOp->isAncestor(user))
users.insert(user);
// Because the operand of moore.event_wait is net.
if (auto varOp = llvm::dyn_cast_or_null<VariableOp>(
nestedOp.getOperand(0).getDefiningOp())) {
auto resultType = varOp.getResult().getType();
builder.setInsertionPointToStart(procedureOp.getBody());
builder.setInsertionPointToStart(&procedureOp.getBody().front());
auto readOp = builder.create<ReadOp>(
nestedOp.getLoc(), cast<RefType>(resultType).getNestedType(),
varOp.getResult());
@ -96,6 +97,6 @@ void SimplifyProceduresPass::runOnOperation() {
builder.clearInsertionPoint();
assignOps.erase(assignOp);
}
}
});
});
}

View File

@ -231,220 +231,324 @@ module Basic;
MyStruct s3 = ~s2;
endmodule
// CHECK-LABEL: func.func private @dummyA(
// CHECK-LABEL: func.func private @dummyB(
// CHECK-LABEL: func.func private @dummyC(
function void dummyA(); endfunction
function void dummyB(); endfunction
function void dummyC(); endfunction
// CHECK-LABEL: func.func private @ConditionalStatements(
// CHECK-SAME: %arg0: !moore.i1
// CHECK-SAME: %arg1: !moore.i1
function void ConditionalStatements(bit x, bit y);
// CHECK: [[COND:%.+]] = moore.conversion %arg0 : !moore.i1 -> i1
// CHECK: cf.cond_br [[COND]], ^[[BB1:.+]], ^[[BB2:.+]]
// CHECK: ^[[BB1]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB2]]
// CHECK: ^[[BB2]]:
if (x) dummyA();
// CHECK: [[COND1:%.+]] = moore.and %arg0, %arg1
// CHECK: [[COND2:%.+]] = moore.conversion [[COND1]] : !moore.i1 -> i1
// CHECK: cf.cond_br [[COND2]], ^[[BB1:.+]], ^[[BB2:.+]]
// CHECK: ^[[BB1]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB2]]
// CHECK: ^[[BB2]]:
if (x &&& y) dummyA();
// CHECK: [[COND:%.+]] = moore.conversion %arg0 : !moore.i1 -> i1
// CHECK: cf.cond_br [[COND]], ^[[BB1:.+]], ^[[BB2:.+]]
// CHECK: ^[[BB1]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB3:.+]]
// CHECK: ^[[BB2]]:
// CHECK: call @dummyB()
// CHECK: cf.br ^[[BB3]]
// CHECK: ^[[BB3]]:
if (x)
dummyA();
else
dummyB();
// CHECK: [[COND:%.+]] = moore.conversion %arg0 : !moore.i1 -> i1
// CHECK: cf.cond_br [[COND]], ^[[BB1:.+]], ^[[BB2:.+]]
// CHECK: ^[[BB1]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB6:.+]]
// CHECK: ^[[BB2]]:
// CHECK: [[COND:%.+]] = moore.conversion %arg1 : !moore.i1 -> i1
// CHECK: cf.cond_br [[COND]], ^[[BB3:.+]], ^[[BB4:.+]]
// CHECK: ^[[BB3]]:
// CHECK: call @dummyB()
// CHECK: cf.br ^[[BB5:.+]]
// CHECK: ^[[BB4]]:
// CHECK: call @dummyC()
// CHECK: cf.br ^[[BB5]]
// CHECK: ^[[BB5]]:
// CHECK: cf.br ^[[BB6]]
// CHECK: ^[[BB6]]:
if (x)
dummyA();
else if (y)
dummyB();
else
dummyC();
// CHECK: [[COND:%.+]] = moore.conversion %arg0 : !moore.i1 -> i1
// CHECK: cf.cond_br [[COND]], ^[[BB1:.+]], ^[[BB2:.+]]
// CHECK: ^[[BB1]]:
// CHECK: return
// CHECK: ^[[BB2]]:
if (x) return;
endfunction
// CHECK-LABEL: func.func private @CaseStatements(
// CHECK-SAME: %arg0: !moore.i32
// CHECK-SAME: %arg1: !moore.i32
// CHECK-SAME: %arg2: !moore.i32
// CHECK-SAME: %arg3: !moore.i32
function void CaseStatements(int x, int a, int b, int c);
// CHECK: [[FLAG:%.+]] = moore.add %arg0, %arg0
case (x + x)
// CHECK: [[COND1:%.+]] = moore.case_eq [[FLAG]], %arg1
// CHECK: [[COND2:%.+]] = moore.conversion [[COND1]] : !moore.i1 -> i1
// CHECK: cf.cond_br [[COND2]], ^[[BB1:.+]], ^[[BB2:.+]]
// CHECK: ^[[BB1]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB3:.+]]
a: dummyA();
// CHECK: ^[[BB2]]:
// CHECK: call @dummyB()
// CHECK: cf.br ^[[BB3]]
default: dummyB();
// CHECK: ^[[BB3]]:
endcase
// CHECK: [[COND1:%.+]] = moore.case_eq %arg0, %arg1
// CHECK: [[COND2:%.+]] = moore.conversion [[COND1]] : !moore.i1 -> i1
// CHECK: cf.cond_br [[COND2]], ^[[BB_MATCH:.+]], ^[[BB1:.+]]
// CHECK: ^[[BB1]]:
// CHECK: [[TMP:%.+]] = moore.add %arg2, %arg3
// CHECK: [[COND1:%.+]] = moore.case_eq %arg0, [[TMP]]
// CHECK: [[COND2:%.+]] = moore.conversion [[COND1]] : !moore.i1 -> i1
// CHECK: cf.cond_br [[COND2]], ^[[BB_MATCH:.+]], ^[[BB2:.+]]
// CHECK: ^[[BB_MATCH]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB_EXIT:.+]]
// CHECK: ^[[BB2]]:
// CHECK: call @dummyB()
// CHECK: cf.br ^[[BB_EXIT]]
// CHECK: ^[[BB_EXIT]]:
case (x)
a, (b+c): dummyA();
default: dummyB();
endcase
// CHECK: [[COND1:%.+]] = moore.casez_eq %arg0, %arg1
// CHECK: [[COND2:%.+]] = moore.conversion [[COND1]] : !moore.i1 -> i1
// CHECK: cf.cond_br [[COND2]], ^[[BB1:.+]], ^[[BB2:.+]]
// CHECK: ^[[BB1]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB3:.+]]
// CHECK: ^[[BB2]]:
// CHECK: cf.br ^[[BB3]]
// CHECK: ^[[BB3]]:
casez (x)
a: dummyA();
endcase
// CHECK: [[COND1:%.+]] = moore.casexz_eq %arg0, %arg1
// CHECK: [[COND2:%.+]] = moore.conversion [[COND1]] : !moore.i1 -> i1
// CHECK: cf.cond_br [[COND2]], ^[[BB1:.+]], ^[[BB2:.+]]
// CHECK: ^[[BB1]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB3:.+]]
// CHECK: ^[[BB2]]:
// CHECK: cf.br ^[[BB3]]
// CHECK: ^[[BB3]]:
casex (x)
a: dummyA();
endcase
endfunction
// CHECK-LABEL: func.func private @ForLoopStatements(
// CHECK-SAME: %arg0: !moore.i32
// CHECK-SAME: %arg1: !moore.i32
// CHECK-SAME: %arg2: !moore.i1
function void ForLoopStatements(int a, int b, bit c);
int x;
// CHECK: moore.blocking_assign %x, %arg0
// CHECK: cf.br ^[[BB_CHECK:.+]]
// CHECK: ^[[BB_CHECK]]:
// CHECK: [[TMP1:%.+]] = moore.read %x
// CHECK: [[TMP2:%.+]] = moore.slt [[TMP1]], %arg1
// CHECK: [[TMP3:%.+]] = moore.conversion [[TMP2]] : !moore.i1 -> i1
// CHECK: cf.cond_br [[TMP3]], ^[[BB_BODY:.+]], ^[[BB_EXIT:.+]]
// CHECK: ^[[BB_BODY]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB_STEP:.+]]
// CHECK: ^[[BB_STEP]]:
// CHECK: call @dummyB()
// CHECK: cf.br ^[[BB_CHECK]]
// CHECK: ^[[BB_EXIT]]:
for (x = a; x < b; dummyB()) dummyA();
// CHECK: %y = moore.variable %arg0 : <i32>
// CHECK: cf.br ^[[BB_CHECK:.+]]
// CHECK: ^[[BB_CHECK]]:
// CHECK: [[TMP1:%.+]] = moore.read %y
// CHECK: [[TMP2:%.+]] = moore.slt [[TMP1]], %arg1
// CHECK: [[TMP3:%.+]] = moore.conversion [[TMP2]] : !moore.i1 -> i1
// CHECK: cf.cond_br [[TMP3]], ^[[BB_BODY:.+]], ^[[BB_EXIT:.+]]
// CHECK: ^[[BB_BODY]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB_STEP:.+]]
// CHECK: ^[[BB_STEP]]:
// CHECK: call @dummyB()
// CHECK: cf.br ^[[BB_CHECK]]
// CHECK: ^[[BB_EXIT]]:
for (int y = a; y < b; dummyB()) dummyA();
// CHECK: cf.br ^[[BB_CHECK:.+]]
// CHECK: ^[[BB_CHECK]]:
// CHECK: [[TMP:%.+]] = moore.conversion %arg2 : !moore.i1 -> i1
// CHECK: cf.cond_br [[TMP]], ^[[BB_BODY:.+]], ^[[BB_EXIT:.+]]
// CHECK: ^[[BB_BODY]]:
// CHECK: [[TMP:%.+]] = moore.conversion %arg2 : !moore.i1 -> i1
// CHECK: cf.cond_br [[TMP]], ^[[BB_TRUE:.+]], ^[[BB_FALSE:.+]]
// CHECK: ^[[BB_TRUE]]:
// CHECK: cf.br ^[[BB_STEP:.+]]
// CHECK: ^[[BB_FALSE]]:
// CHECK: cf.br ^[[BB_EXIT]]
// CHECK: ^[[BB_STEP]]:
// CHECK: call @dummyB()
// CHECK: cf.br ^[[BB_CHECK]]
// CHECK: ^[[BB_EXIT]]:
for (; c; dummyB())
if (c)
continue;
else
break;
endfunction
// CHECK-LABEL: func.func private @ForeverLoopStatements(
// CHECK-SAME: %arg0: !moore.i1
// CHECK-SAME: %arg1: !moore.i1
function void ForeverLoopStatements(bit x, bit y);
// CHECK: cf.br ^[[BB_BODY:.+]]
// CHECK: ^[[BB_BODY]]:
forever begin
if (x) begin
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB_EXIT:.+]]
dummyA();
break;
end else begin
// CHECK: call @dummyB()
// CHECK: cf.br ^[[BB_BODY]]
dummyB();
continue;
end
end
// CHECK: ^[[BB_EXIT]]:
// CHECK: cf.br ^[[BB_BODY:.+]]
// CHECK: ^[[BB_BODY]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB_BODY]]
forever dummyA();
endfunction
// CHECK-LABEL: func.func private @WhileLoopStatements(
// CHECK-SAME: %arg0: !moore.i1
// CHECK-SAME: %arg1: !moore.i1
function void WhileLoopStatements(bit x, bit y);
// CHECK: cf.br ^[[BB_CHECK:.+]]
// CHECK: ^[[BB_CHECK]]:
// CHECK: [[TMP:%.+]] = moore.conversion %arg0 : !moore.i1 -> i1
// CHECK: cf.cond_br [[TMP]], ^[[BB_BODY:.+]], ^[[BB_EXIT:.+]]
// CHECK: ^[[BB_BODY]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB_CHECK]]
// CHECK: ^[[BB_EXIT]]:
while (x) dummyA();
// CHECK: cf.br ^[[BB_BODY:.+]]
// CHECK: ^[[BB_BODY]]:
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB_CHECK:.+]]
// CHECK: ^[[BB_CHECK]]:
// CHECK: [[TMP:%.+]] = moore.conversion %arg0 : !moore.i1 -> i1
// CHECK: cf.cond_br [[TMP]], ^[[BB_BODY]], ^[[BB_EXIT:.+]]
// CHECK: ^[[BB_EXIT]]:
do dummyA(); while (x);
// CHECK: cf.br ^[[BB_CHECK:.+]]
// CHECK: ^[[BB_CHECK]]:
// CHECK: [[TMP:%.+]] = moore.conversion %arg0 : !moore.i1 -> i1
// CHECK: cf.cond_br [[TMP]], ^[[BB_BODY:.+]], ^[[BB_EXIT:.+]]
// CHECK: ^[[BB_BODY]]:
while (x) begin
if (y) begin
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB_EXIT]]
dummyA();
break;
end else begin
// CHECK: call @dummyB()
// CHECK: cf.br ^[[BB_CHECK]]
dummyB();
continue;
end
end
// CHECK: ^[[BB_EXIT]]:
endfunction
// CHECK-LABEL: func.func private @RepeatLoopStatements(
// CHECK-SAME: %arg0: !moore.i32
// CHECK-SAME: %arg1: !moore.i1
function void RepeatLoopStatements(int x, bit y);
// CHECK: cf.br ^[[BB_CHECK:.+]](%arg0 : !moore.i32)
repeat (x) begin
// CHECK: ^[[BB_CHECK]]([[COUNT:%.+]]: !moore.i32):
// CHECK: [[TMP1:%.+]] = moore.bool_cast [[COUNT]] : i32 -> i1
// CHECK: [[TMP2:%.+]] = moore.conversion [[TMP1]] : !moore.i1 -> i1
// CHECK: cf.cond_br [[TMP2]], ^[[BB_BODY:.+]], ^[[BB_EXIT:.+]]
// CHECK: ^[[BB_BODY]]:
if (y) begin
// CHECK: call @dummyA()
// CHECK: cf.br ^[[BB_EXIT]]
dummyA();
break;
end else begin
// CHECK: call @dummyB()
// CHECK: cf.br ^[[BB_STEP:.+]]
dummyB();
continue;
end
// CHECK: ^[[BB_STEP]]:
// CHECK: [[TMP1:%.+]] = moore.constant 1 : i32
// CHECK: [[TMP2:%.+]] = moore.sub [[COUNT]], [[TMP1]] : i32
// CHECK: cf.br ^[[BB_CHECK]]([[TMP2]] : !moore.i32)
end
// CHECK: ^[[BB_EXIT]]:
endfunction
// CHECK-LABEL: moore.module @Statements
module Statements;
bit x, y, z;
int i;
initial begin
// CHECK: %a = moore.variable : <i32>
// CHECK: %a = moore.variable : <i32>
automatic int a;
// CHECK: [[TMP1:%.+]] = moore.read %a
// CHECK moore.blocking_assign %i, [[TMP1]] : i32
i = a;
//===------------------------------------------------------------------===//
// Conditional statements
// CHECK: [[TMP1:%.+]] = moore.read %x
// CHECK: [[COND:%.+]] = moore.conversion [[TMP1]] : !moore.i1 -> i1
// CHECK: scf.if [[COND]] {
// CHECK: [[TMP2:%.+]] = moore.read %y :
// CHECK: moore.blocking_assign %x, [[TMP2]] : i1
// CHECK: }
if (x) x = y;
// CHECK: [[TMP1:%.+]] = moore.read %x
// CHECK: [[TMP2:%.+]] = moore.read %y
// CHECK: [[COND0:%.+]] = moore.and [[TMP1]], [[TMP2]]
// CHECK: [[COND1:%.+]] = moore.conversion [[COND0]] : !moore.i1 -> i1
// CHECK: scf.if [[COND1]] {
// CHECK: [[TMP3:%.+]] = moore.read %y
// CHECK: moore.blocking_assign %x, [[TMP3]]
// CHECK: }
if (x &&& y) x = y;
// CHECK: [[TMP1:%.+]] = moore.read %x
// CHECK: [[COND:%.+]] = moore.conversion [[TMP1]] : !moore.i1 -> i1
// CHECK: scf.if [[COND]] {
// CHECK: [[TMP2:%.+]] = moore.read %z
// CHECK: moore.blocking_assign %x, [[TMP2]]
// CHECK: } else {
// CHECK: [[TMP3:%.+]] = moore.read %y
// CHECK: moore.blocking_assign %x, [[TMP3]]
// CHECK: }
if (x) x = z; else x = y;
// CHECK: [[TMP1:%.+]] = moore.read %x
// CHECK: [[COND:%.+]] = moore.conversion [[TMP1]] : !moore.i1 -> i1
// CHECK: scf.if [[COND]] {
// CHECK: [[TMP2:%.+]] = moore.read %x
// CHECK: moore.blocking_assign %x, [[TMP2]]
// CHECK: } else {
// CHECK: [[TMP3:%.+]] = moore.read %y
// CHECK: [[COND:%.+]] = moore.conversion [[TMP3]] : !moore.i1 -> i1
// CHECK: scf.if [[COND]] {
// CHECK: [[TMP4:%.+]] = moore.read %y
// CHECK: moore.blocking_assign %x, [[TMP4]]
// CHECK: } else {
// CHECK: [[TMP5:%.+]] = moore.read %z
// CHECK: moore.blocking_assign %x, [[TMP5]]
// CHECK: }
// CHECK: }
if (x) begin
x = x;
end else if (y) begin
x = y;
end else begin
x = z;
end
//===------------------------------------------------------------------===//
// Case statements
// CHECK: [[TMP1:%.+]] = moore.read %x
// CHECK: [[TMP2:%.+]] = moore.read %x
// CHECK: [[TMP3:%.+]] = moore.eq [[TMP1]], [[TMP2]] : i1 -> i1
// CHECK: [[TMP4:%.+]] = moore.conversion [[TMP3]] : !moore.i1 -> i1
// CHECK: scf.if [[TMP4]] {
// CHECK: [[TMP5:%.+]] = moore.read %x
// CHECK: moore.blocking_assign %x, [[TMP5]] : i1
// CHECK: }
// CHECK: [[TMP6:%.+]] = moore.read %x
// CHECK: [[TMP7:%.+]] = moore.eq [[TMP1]], [[TMP6]] : i1 -> i1
// CHECK: [[TMP8:%.+]] = moore.read %y
// CHECK: [[TMP9:%.+]] = moore.eq [[TMP1]], [[TMP8]] : i1 -> i1
// CHECK: [[TMP10:%.+]] = moore.or [[TMP7]], [[TMP9]] : i1
// CHECK: [[TMP11:%.+]] = moore.conversion [[TMP10]] : !moore.i1 -> i1
// CHECK: scf.if [[TMP11]] {
// CHECK: [[TMP12:%.+]] = moore.read %y
// CHECK: moore.blocking_assign %x, [[TMP12]] : i1
// CHECK: }
case (x)
x: x = x;
x, y: x = y;
endcase
// CHECK: [[TMP1:%.+]] = moore.read %x
// CHECK: [[TMP2:%.+]] = moore.read %x
// CHECK: [[TMP3:%.+]] = moore.eq [[TMP1]], [[TMP2]] : i1 -> i1
// CHECK: [[TMP4:%.+]] = moore.conversion [[TMP3]] : !moore.i1 -> i1
// CHECK: scf.if [[TMP4]] {
// CHECK: [[TMP5:%.+]] = moore.read %x
// CHECK: moore.blocking_assign %x, [[TMP5]] : i1
// CHECK: }
// CHECK: [[TMP6:%.+]] = moore.read %x
// CHECK: [[TMP7:%.+]] = moore.eq [[TMP1]], [[TMP6]] : i1 -> i1
// CHECK: [[TMP8:%.+]] = moore.read %y
// CHECK: [[TMP9:%.+]] = moore.eq [[TMP1]], [[TMP8]] : i1 -> i1
// CHECK: [[TMP10:%.+]] = moore.or [[TMP7]], [[TMP9]] : i1
// CHECK: [[TMP11:%.+]] = moore.conversion [[TMP10]] : !moore.i1 -> i1
// CHECK: scf.if [[TMP11]] {
// CHECK: [[TMP12:%.+]] = moore.read %y
// CHECK: moore.blocking_assign %x, [[TMP12]] : i1
// CHECK: }
// CHECK: [[TMP13:%.+]] = moore.read %z
// CHECK: [[TMP14:%.+]] = moore.eq [[TMP1]], [[TMP13]] : i1 -> i1
// CHECK: [[TMP15:%.+]] = moore.conversion [[TMP14]] : !moore.i1 -> i1
// CHECK: scf.if [[TMP15]] {
// CHECK: [[TMP16:%.+]] = moore.read %z
// CHECK: moore.blocking_assign %x, [[TMP16]] : i1
// CHECK: }
// CHECK: [[TMP17:%.+]] = moore.or [[TMP10]], [[TMP14]] : i1
// CHECK: [[TMP18:%.+]] = moore.or [[TMP3]], [[TMP17]] : i1
// CHECK: [[TMP19:%.+]] = moore.not [[TMP18]] : i1
// CHECK: [[TMP20:%.+]] = moore.conversion [[TMP19]] : !moore.i1 -> i1
// CHECK: scf.if [[TMP20]] {
// CHECK: [[TMP21:%.+]] = moore.read %x
// CHECK: moore.blocking_assign %x, [[TMP21]] : i1
// CHECK: }
case (x)
x: x = x;
x, y: x = y;
z: x = z;
default x = x;
endcase
//===------------------------------------------------------------------===//
// Loop statements
// CHECK: [[TMP1:%.+]] = moore.read %x
// CHECK: moore.blocking_assign %y, [[TMP1]] : i1
// CHECK: scf.while : () -> () {
// CHECK: [[TMP2:%.+]] = moore.read %x
// CHECK: [[COND:%.+]] = moore.conversion [[TMP2]] : !moore.i1 -> i1
// CHECK: scf.condition([[COND]])
// CHECK: } do {
// CHECK: [[TMP3:%.+]] = moore.read %y
// CHECK: moore.blocking_assign %x, [[TMP3]] : i1
// CHECK: [[TMP4:%.+]] = moore.read %z
// CHECK: moore.blocking_assign %x, [[TMP4]] : i1
// CHECK: scf.yield
// CHECK: }
for (y = x; x; x = z) x = y;
// CHECK: [[TMP:%.+]] = moore.constant true : i1
// CHECK: %iv = moore.variable [[TMP]] : <i1>
// CHECK: scf.while : () -> () {
// CHECK: [[TMP:%.+]] = moore.read %iv
// CHECK: [[COND:%.+]] = moore.conversion [[TMP]] : !moore.i1 -> i1
// CHECK: scf.condition([[COND]])
// CHECK: } do {
// CHECK: [[TMP:%.+]] = moore.read %iv
// CHECK: moore.blocking_assign %y, [[TMP]] : i1
// CHECK: [[TMP:%.+]] = moore.read %iv
// CHECK: moore.blocking_assign %x, [[TMP]] : i1
// CHECK: scf.yield
// CHECK: }
for (bit iv = '1; iv; x = iv) y = iv;
// CHECK: [[TMP1:%.+]] = moore.read %i
// CHECK: scf.while (%arg0 = [[TMP1]]) : (!moore.i32) -> !moore.i32 {
// CHECK: [[TMP2:%.+]] = moore.bool_cast %arg0 : i32 -> i1
// CHECK: [[TMP3:%.+]] = moore.conversion [[TMP2]] : !moore.i1 -> i1
// CHECK: scf.condition([[TMP3]]) %arg0 : !moore.i32
// CHECK: } do {
// CHECK: ^bb0(%arg0: !moore.i32):
// CHECK: [[TMP4:%.+]] = moore.read %y
// CHECK: moore.blocking_assign %x, [[TMP4]] : i1
// CHECK: [[TMP5:%.+]] = moore.constant 1 : i32
// CHECK: [[TMP6:%.+]] = moore.sub %arg0, [[TMP5]] : i32
// CHECK: scf.yield [[TMP6]] : !moore.i32
// CHECK: }
repeat (i) x = y;
// CHECK: scf.while : () -> () {
// CHECK: [[TMP1:%.+]] = moore.read %x
// CHECK: [[COND:%.+]] = moore.conversion [[TMP1]] : !moore.i1 -> i1
// CHECK: scf.condition([[COND]])
// CHECK: } do {
// CHECK: [[TMP2:%.+]] = moore.read %y
// CHECK: moore.blocking_assign %x, [[TMP2]] : i1
// CHECK: scf.yield
// CHECK: }
while (x) x = y;
// CHECK: scf.while : () -> () {
// CHECK: [[TMP1:%.+]] = moore.read %y
// CHECK: moore.blocking_assign %x, [[TMP1]] : i1
// CHECK: [[TMP2:%.+]] = moore.read %x
// CHECK: [[COND:%.+]] = moore.conversion [[TMP2]] : !moore.i1 -> i1
// CHECK: scf.condition([[COND]])
// CHECK: } do {
// CHECK: scf.yield
// CHECK: }
do x = y; while (x);
// CHECK: scf.while : () -> () {
// CHECK: %true = hw.constant true
// CHECK: scf.condition(%true)
// CHECK: } do {
// CHECK: [[TMP1:%.+]] = moore.read %y
// CHECK: moore.blocking_assign %x, [[TMP1]] : i1
// CHECK: scf.yield
// CHECK: }
forever x = y;
//===------------------------------------------------------------------===//
// Assignments

View File

@ -77,12 +77,12 @@ moore.module @Module() {
// CHECK: moore.procedure always_comb {
// CHECK: moore.procedure always_latch {
// CHECK: moore.procedure always_ff {
moore.procedure initial {}
moore.procedure final {}
moore.procedure always {}
moore.procedure always_comb {}
moore.procedure always_latch {}
moore.procedure always_ff {}
moore.procedure initial { moore.return }
moore.procedure final { moore.return }
moore.procedure always { moore.return }
moore.procedure always_comb { moore.return }
moore.procedure always_latch { moore.return }
moore.procedure always_ff { moore.return }
// CHECK: %[[TMP1:.+]] = moore.read %v2
// CHECK: moore.assign %v1, %[[TMP1]] : i1
@ -100,6 +100,7 @@ moore.module @Module() {
moore.nonblocking_assign %v1, %4 : i1
// CHECK: %a = moore.variable : <i32>
%a = moore.variable : <i32>
moore.return
}
}

View File

@ -43,6 +43,7 @@
// CHECK: moore.nonblocking_assign %v, %[[TMP2]] : l42
moore.nonblocking_assign %7, %8 : l9002
}
moore.return
}
moore.output
}
@ -73,6 +74,7 @@ moore.module @Nested() {
// CHECK: %[[TMP5:.+]] = moore.extract %[[TMP2]] from 0 : i96 -> i32
// CHECK: moore.blocking_assign %y, %[[TMP5]] : i32
moore.blocking_assign %3, %6 : i96
moore.return
}
moore.output
}

View File

@ -29,5 +29,6 @@ moore.module @LocalVar() {
moore.blocking_assign %a, %6 : i32
%7 = moore.read %a : <i32>
moore.blocking_assign %y, %7 : i32
moore.return
}
}

View File

@ -33,6 +33,8 @@ moore.module @Foo() {
%3 = moore.constant 1 : i32
%4 = moore.add %2, %3 : i32
moore.blocking_assign %a, %4 : i32
moore.return
}
// CHECK: moore.procedure always_comb
@ -44,5 +46,7 @@ moore.module @Foo() {
// CHECK: moore.blocking_assign %y, [[TMP]]
%0 = moore.read %a : <i32>
moore.blocking_assign %y, %0 : i32
moore.return
}
}