[Mlir-commits] [mlir] [mlir][emitc] Add 'emitc.while' and 'emitc.do' ops to the dialect (PR #143008)
Vlad Lazar
llvmlistbot at llvm.org
Thu Jun 5 10:05:48 PDT 2025
https://github.com/Vladislave0-0 created https://github.com/llvm/llvm-project/pull/143008
This MR adds:
- 'emitc::WhileOp' and 'emitc::DoOp' to the EmitC dialect
- Emission of the corresponding ops in the CppEmitter
- Conversion from the SCF dialect to the EmitC dialect for the ops
- Corresponding tests
>From 61c29758819091d030df875742f829dc949cca27 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Thu, 5 Jun 2025 19:41:52 +0300
Subject: [PATCH] [mlir][emitc] Add 'emitc.while' and 'emitc.do' ops to the
dialect
This MR adds:
- 'emitc::WhileOp' and 'emitc::DoOp' to the EmitC dialect
- Emission of the corresponding ops in the CppEmitter
- Conversion from the SCF dialect to the EmitC dialect for the ops
- Corresponding tests
---
mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 154 +++++++++++-
mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp | 238 +++++++++++++++++-
mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 116 ++++++++-
mlir/lib/Target/Cpp/TranslateToCpp.cpp | 122 ++++++++-
mlir/test/Conversion/SCFToEmitC/while.mlir | 235 +++++++++++++++++
mlir/test/Dialect/EmitC/invalid_ops.mlir | 196 ++++++++++++++-
mlir/test/Dialect/EmitC/ops.mlir | 32 +++
mlir/test/Target/Cpp/do.mlir | 69 +++++
mlir/test/Target/Cpp/while.mlir | 69 +++++
9 files changed, 1214 insertions(+), 17 deletions(-)
create mode 100644 mlir/test/Conversion/SCFToEmitC/while.mlir
create mode 100644 mlir/test/Target/Cpp/do.mlir
create mode 100644 mlir/test/Target/Cpp/while.mlir
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index d4aea52a0d485..2390c4ef24cbc 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1345,7 +1345,7 @@ def EmitC_AssignOp : EmitC_Op<"assign", []> {
}
def EmitC_YieldOp : EmitC_Op<"yield",
- [Pure, Terminator, ParentOneOf<["ExpressionOp", "IfOp", "ForOp", "SwitchOp"]>]> {
+ [Pure, Terminator, ParentOneOf<["DoOp", "ExpressionOp", "ForOp", "IfOp", "SwitchOp", "WhileOp"]>]> {
let summary = "Block termination operation";
let description = [{
The `emitc.yield` terminates its parent EmitC op's region, optionally yielding
@@ -1572,4 +1572,156 @@ def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects,
let hasVerifier = 1;
}
+def EmitC_WhileOp : EmitC_Op<"while",
+ [HasOnlyGraphRegion, RecursiveMemoryEffects, NoRegionArguments, OpAsmOpInterface, NoTerminator]> {
+ let summary = "While operation";
+ let description = [{
+ The `emitc.while` operation represents a C/C++ while loop construct that
+ repeatedly executes a body region as long as a condition region evaluates to
+ true. The operation has two regions:
+
+ 1. A condition region that must yield a boolean value (i1)
+ 2. A body region that contains the loop body
+
+ The condition region is evaluated before each iteration. If it yields true,
+ the body region is executed. The loop terminates when the condition yields
+ false. The condition region must contain exactly one block that terminates
+ with an `emitc.yield` operation producing an i1 value.
+
+ Example:
+
+ ```mlir
+ emitc.func @foo(%arg0 : !emitc.ptr<i32>) {
+ %var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+ %0 = emitc.literal "10" : i32
+ %1 = emitc.literal "1" : i32
+
+ emitc.while {
+ %var_load = load %var : <i32>
+ %res = emitc.cmp le, %var_load, %0 : (i32, i32) -> i1
+ emitc.yield %res : i1
+ } do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ %var_load = load %var : <i32>
+ %tmp_add = add %var_load, %1 : (i32, i32) -> i32
+ "emitc.assign"(%var, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
+ }
+
+ return
+ }
+ ```
+
+ ```c++
+ // Code emitted for the operation above.
+ void foo(int32_t* v1) {
+ int32_t v2 = 0;
+ while (v2 <= 10) {
+ printf("%d", *v1);
+ int32_t v3 = v2;
+ int32_t v4 = v3 + 1;
+ v2 = v4;
+ }
+ return;
+ }
+ ```
+ }];
+
+ let arguments = (ins);
+ let results = (outs);
+ let regions = (region MaxSizedRegion<1>:$conditionRegion,
+ MaxSizedRegion<1>:$bodyRegion);
+
+ let hasCustomAssemblyFormat = 1;
+ let hasVerifier = 1;
+
+ let extraClassDeclaration = [{
+ Operation *getRootOp();
+
+ //===------------------------------------------------------------------===//
+ // OpAsmOpInterface Methods
+ //===------------------------------------------------------------------===//
+
+ /// EmitC ops in the body can omit their 'emitc.' prefix in the assembly.
+ static ::llvm::StringRef getDefaultDialect() {
+ return "emitc";
+ }
+ }];
+}
+
+def EmitC_DoOp : EmitC_Op<"do",
+ [RecursiveMemoryEffects, NoRegionArguments, OpAsmOpInterface, NoTerminator]> {
+ let summary = "Do-while operation";
+ let description = [{
+ The `emitc.do` operation represents a C/C++ do-while loop construct that
+ executes a body region first and then repeatedly executes it as long as a
+ condition region evaluates to true. The operation has two regions:
+
+ 1. A body region that contains the loop body
+ 2. A condition region that must yield a boolean value (i1)
+
+ Unlike a while loop, the body region is executed before the first evaluation
+ of the condition. The loop terminates when the condition yields false. The
+ condition region must contain exactly one block that terminates with an
+ `emitc.yield` operation producing an i1 value.
+
+ Example:
+
+ ```mlir
+ emitc.func @foo(%arg0 : !emitc.ptr<i32>) {
+ %var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+ %0 = emitc.literal "10" : i32
+ %1 = emitc.literal "1" : i32
+
+ emitc.do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ %var_load = load %var : <i32>
+ %tmp_add = add %var_load, %1 : (i32, i32) -> i32
+ "emitc.assign"(%var, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
+ } while {
+ %var_load = load %var : <i32>
+ %res = emitc.cmp le, %var_load, %0 : (i32, i32) -> i1
+ emitc.yield %res : i1
+ }
+
+ return
+ }
+ ```
+
+ ```c++
+ // Code emitted for the operation above.
+ void foo(int32_t* v1) {
+ int32_t v2 = 0;
+ do {
+ printf("%d", *v1);
+ int32_t v3 = v2;
+ int32_t v4 = v3 + 1;
+ v2 = v4;
+ } while (v2 <= 10);
+ return;
+ }
+ ```
+ }];
+
+ let arguments = (ins);
+ let results = (outs);
+ let regions = (region MaxSizedRegion<1>:$bodyRegion,
+ MaxSizedRegion<1>:$conditionRegion);
+
+ let hasCustomAssemblyFormat = 1;
+ let hasVerifier = 1;
+
+ let extraClassDeclaration = [{
+ Operation *getRootOp();
+
+ //===------------------------------------------------------------------===//
+ // OpAsmOpInterface Methods
+ //===------------------------------------------------------------------===//
+
+ /// EmitC ops in the body can omit their 'emitc.' prefix in the assembly.
+ static ::llvm::StringRef getDefaultDialect() {
+ return "emitc";
+ }
+ }];
+}
+
#endif // MLIR_DIALECT_EMITC_IR_EMITC
diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index 345e8494194eb..2e454070cb185 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -333,11 +333,246 @@ LogicalResult IndexSwitchOpLowering::matchAndRewrite(
return success();
}
+//==============================================================================
+
+// Lower scf::while to either emitc::while or emitc::do based on argument usage
+// patterns. Uses mutable variables to maintain loop state across iterations.
+struct WhileLowering : public OpConversionPattern<WhileOp> {
+ using OpConversionPattern::OpConversionPattern;
+
+ LogicalResult
+ matchAndRewrite(WhileOp whileOp, OpAdaptor adaptor,
+ ConversionPatternRewriter &rewriter) const override {
+ Location loc = whileOp.getLoc();
+ MLIRContext *context = loc.getContext();
+
+ // Create variable storage for loop-carried values to enable imperative
+ // updates while maintaining SSA semantics at conversion boundaries.
+ SmallVector<Value> variables;
+ if (failed(
+ createInitVariables(whileOp, rewriter, variables, loc, context))) {
+ return failure();
+ }
+
+ // Select lowering strategy based on condition argument usage:
+ // - emitc.while when condition args match region inputs (direct mapping);
+ // - emitc.do when condition args differ (requires state synchronization).
+ Region &beforeRegion = adaptor.getBefore();
+ Block &beforeBlock = beforeRegion.front();
+ auto condOp = cast<scf::ConditionOp>(beforeRegion.back().getTerminator());
+
+ bool isDoOp = !llvm::equal(beforeBlock.getArguments(), condOp.getArgs());
+
+ LogicalResult result =
+ isDoOp ? lowerDoWhile(whileOp, variables, context, rewriter, loc)
+ : lowerWhile(whileOp, variables, context, rewriter, loc);
+
+ if (failed(result))
+ return failure();
+
+ // Create an emitc::variable op for each result. These variables will be
+ // assigned to by emitc::assign ops within the loop body.
+ SmallVector<Value> resultVariables;
+ if (failed(createVariablesForResults(whileOp, getTypeConverter(), rewriter,
+ resultVariables))) {
+ return rewriter.notifyMatchFailure(whileOp,
+ "Failed to create result variables");
+ }
+
+ rewriter.setInsertionPointAfter(whileOp);
+
+ // Transfer final loop state to result variables and get final SSA results.
+ SmallVector<Value> finalResults =
+ finalizeLoopResults(resultVariables, variables, rewriter, loc);
+
+ rewriter.replaceOp(whileOp, finalResults);
+ return success();
+ }
+
+private:
+ // Initialize variables for loop-carried values to enable state updates
+ // across iterations without SSA argument passing.
+ static LogicalResult createInitVariables(WhileOp whileOp,
+ ConversionPatternRewriter &rewriter,
+ SmallVectorImpl<Value> &outVars,
+ Location loc, MLIRContext *context) {
+ emitc::OpaqueAttr noInit = emitc::OpaqueAttr::get(context, "");
+
+ for (Value init : whileOp.getInits()) {
+ emitc::VariableOp var = rewriter.create<emitc::VariableOp>(
+ loc, emitc::LValueType::get(init.getType()), noInit);
+ rewriter.create<emitc::AssignOp>(loc, var.getResult(), init);
+ outVars.push_back(var.getResult());
+ }
+
+ return success();
+ }
+
+ // Transition from SSA block arguments to variable-based state management by
+ // replacing argument uses with variable loads and cleaning up block
+ // interface.
+ void replaceBlockArgsWithVarLoads(Block *block, ArrayRef<Value> vars,
+ ConversionPatternRewriter &rewriter,
+ Location loc) const {
+ rewriter.setInsertionPointToStart(block);
+
+ for (auto [arg, var] : llvm::zip(block->getArguments(), vars)) {
+ Type loadedType = cast<emitc::LValueType>(var.getType()).getValueType();
+ Value load = rewriter.create<emitc::LoadOp>(loc, loadedType, var);
+ arg.replaceAllUsesWith(load);
+ }
+
+ // Remove arguments after replacement to simplify block structure.
+ block->eraseArguments(0, block->getNumArguments());
+ }
+
+ // Convert SCF yield terminators to imperative assignments to update loop
+ // variables, maintaining loop semantics while transitioning to emitc model.
+ void processYieldTerminator(Operation *terminator, ArrayRef<Value> vars,
+ ConversionPatternRewriter &rewriter,
+ Location loc) const {
+ auto yieldOp = cast<scf::YieldOp>(terminator);
+ SmallVector<Value> yields(yieldOp.getOperands());
+ rewriter.eraseOp(yieldOp);
+
+ rewriter.setInsertionPointToEnd(yieldOp->getBlock());
+ for (auto [var, val] : llvm::zip(vars, yields))
+ rewriter.create<emitc::AssignOp>(loc, var, val);
+ }
+
+ // Transfers final loop state from mutable variables to result variables,
+ // then returns the final SSA values to replace the original scf::while
+ // results.
+ static SmallVector<Value>
+ finalizeLoopResults(ArrayRef<Value> resultVariables,
+ ArrayRef<Value> loopVariables,
+ ConversionPatternRewriter &rewriter, Location loc) {
+ // Transfer final loop state to result variables to bridge imperative loop
+ // variables with SSA result expectations of the original op.
+ for (auto [resultVar, var] : llvm::zip(resultVariables, loopVariables)) {
+ Type loadedType = cast<emitc::LValueType>(var.getType()).getValueType();
+ Value load = rewriter.create<emitc::LoadOp>(loc, loadedType, var);
+ rewriter.create<emitc::AssignOp>(loc, resultVar, load);
+ }
+
+ // Replace op with loaded values to integrate with converted SSA graph.
+ SmallVector<Value> finalResults;
+ for (Value resultVar : resultVariables) {
+ Type loadedType =
+ cast<emitc::LValueType>(resultVar.getType()).getValueType();
+ finalResults.push_back(
+ rewriter.create<emitc::LoadOp>(loc, loadedType, resultVar));
+ }
+
+ return finalResults;
+ }
+
+ // Direct lowering to emitc.while when condition arguments match region
+ // inputs.
+ LogicalResult lowerWhile(WhileOp whileOp, ArrayRef<Value> vars,
+ MLIRContext *context,
+ ConversionPatternRewriter &rewriter,
+ Location loc) const {
+ auto loweredWhile = rewriter.create<emitc::WhileOp>(loc);
+
+ // Lower before region to condition region.
+ rewriter.inlineRegionBefore(whileOp.getBefore(),
+ loweredWhile.getConditionRegion(),
+ loweredWhile.getConditionRegion().end());
+
+ Block *condBlock = &loweredWhile.getConditionRegion().front();
+ replaceBlockArgsWithVarLoads(condBlock, vars, rewriter, loc);
+
+ Operation *condTerminator =
+ loweredWhile.getConditionRegion().back().getTerminator();
+ auto condOp = cast<scf::ConditionOp>(condTerminator);
+ rewriter.setInsertionPoint(condOp);
+ Value condition = rewriter.getRemappedValue(condOp.getCondition());
+ rewriter.create<emitc::YieldOp>(condOp.getLoc(), condition);
+ rewriter.eraseOp(condOp);
+
+ // Lower after region to body region.
+ rewriter.inlineRegionBefore(whileOp.getAfter(),
+ loweredWhile.getBodyRegion(),
+ loweredWhile.getBodyRegion().end());
+
+ Block *bodyBlock = &loweredWhile.getBodyRegion().front();
+ replaceBlockArgsWithVarLoads(bodyBlock, vars, rewriter, loc);
+
+ // Convert scf.yield to variable assignments for state updates.
+ processYieldTerminator(bodyBlock->getTerminator(), vars, rewriter, loc);
+
+ return success();
+ }
+
+ // Lower to emitc.do when condition arguments differ from region inputs.
+ LogicalResult lowerDoWhile(WhileOp whileOp, ArrayRef<Value> vars,
+ MLIRContext *context,
+ ConversionPatternRewriter &rewriter,
+ Location loc) const {
+ Type i1Type = IntegerType::get(context, 1);
+ auto globalCondition =
+ rewriter.create<emitc::VariableOp>(loc, emitc::LValueType::get(i1Type),
+ emitc::OpaqueAttr::get(context, ""));
+ Value conditionVal = globalCondition.getResult();
+
+ auto loweredDo = rewriter.create<emitc::DoOp>(loc);
+
+ // Lower before region as body.
+ rewriter.inlineRegionBefore(whileOp.getBefore(), loweredDo.getBodyRegion(),
+ loweredDo.getBodyRegion().end());
+
+ Block *bodyBlock = &loweredDo.getBodyRegion().front();
+ replaceBlockArgsWithVarLoads(bodyBlock, vars, rewriter, loc);
+
+ // Convert scf.condition to condition variable assignment.
+ Operation *condTerminator =
+ loweredDo.getBodyRegion().back().getTerminator();
+ scf::ConditionOp condOp = cast<scf::ConditionOp>(condTerminator);
+ rewriter.setInsertionPoint(condOp);
+ Value condition = rewriter.getRemappedValue(condOp.getCondition());
+ rewriter.create<emitc::AssignOp>(loc, conditionVal, condition);
+
+ // Wrap body region in conditional to preserve scf semantics.
+ auto ifOp = rewriter.create<emitc::IfOp>(loc, condition, false, false);
+
+ // Lower after region as then-block of conditional.
+ rewriter.inlineRegionBefore(whileOp.getAfter(), ifOp.getBodyRegion(),
+ ifOp.getBodyRegion().begin());
+
+ if (!ifOp.getBodyRegion().empty()) {
+ Block *ifBlock = &ifOp.getBodyRegion().front();
+
+ // Handle argument mapping from condition op to body region.
+ auto args = condOp.getArgs();
+ for (auto [arg, val] : llvm::zip(ifBlock->getArguments(), args))
+ arg.replaceAllUsesWith(rewriter.getRemappedValue(val));
+
+ ifBlock->eraseArguments(0, ifBlock->getNumArguments());
+
+ // Convert scf.yield to variable assignments for state updates.
+ processYieldTerminator(ifBlock->getTerminator(), vars, rewriter, loc);
+ rewriter.create<emitc::YieldOp>(loc);
+ }
+
+ rewriter.eraseOp(condOp);
+
+ // Create condition region that loads from the flag variable.
+ Block *condBlock = rewriter.createBlock(&loweredDo.getConditionRegion());
+ rewriter.setInsertionPointToStart(condBlock);
+ Value cond = rewriter.create<emitc::LoadOp>(loc, i1Type, conditionVal);
+ rewriter.create<emitc::YieldOp>(loc, cond);
+
+ return success();
+ }
+};
+
void mlir::populateSCFToEmitCConversionPatterns(RewritePatternSet &patterns,
TypeConverter &typeConverter) {
patterns.add<ForLowering>(typeConverter, patterns.getContext());
patterns.add<IfLowering>(typeConverter, patterns.getContext());
patterns.add<IndexSwitchOpLowering>(typeConverter, patterns.getContext());
+ patterns.add<WhileLowering>(typeConverter, patterns.getContext());
}
void SCFToEmitCPass::runOnOperation() {
@@ -354,7 +589,8 @@ void SCFToEmitCPass::runOnOperation() {
// Configure conversion to lower out SCF operations.
ConversionTarget target(getContext());
- target.addIllegalOp<scf::ForOp, scf::IfOp, scf::IndexSwitchOp>();
+ target
+ .addIllegalOp<scf::ForOp, scf::IfOp, scf::IndexSwitchOp, scf::WhileOp>();
target.markUnknownOpDynamicallyLegal([](Operation *) { return true; });
if (failed(
applyPartialConversion(getOperation(), target, std::move(patterns))))
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 1709654b90138..70a7150b818ff 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -900,10 +900,12 @@ LogicalResult emitc::YieldOp::verify() {
Value result = getResult();
Operation *containingOp = getOperation()->getParentOp();
- if (result && containingOp->getNumResults() != 1)
+ if (result && containingOp->getNumResults() != 1 &&
+ !isa<WhileOp, DoOp>(containingOp))
return emitOpError() << "yields a value not returned by parent";
- if (!result && containingOp->getNumResults() != 0)
+ if (!result && containingOp->getNumResults() != 0 &&
+ !isa<WhileOp, DoOp>(containingOp))
return emitOpError() << "does not yield a value to be returned by parent";
return success();
@@ -1394,6 +1396,116 @@ void FileOp::build(OpBuilder &builder, OperationState &state, StringRef id) {
builder.getNamedAttr("id", builder.getStringAttr(id)));
}
+//===----------------------------------------------------------------------===//
+// Common functions for WhileOp and DoOp
+//===----------------------------------------------------------------------===//
+
+static Operation *getRootOpFromLoopCondition(Region &condRegion) {
+ auto yieldOp = cast<emitc::YieldOp>(condRegion.front().getTerminator());
+ return yieldOp.getResult().getDefiningOp();
+}
+
+static LogicalResult verifyLoopRegions(Operation &op, Region &condition,
+ Region &body) {
+ if (condition.empty())
+ return op.emitOpError("condition region cannot be empty");
+
+ Block &condBlock = condition.front();
+ for (Operation &inner : condBlock.without_terminator()) {
+ if (!inner.hasTrait<OpTrait::emitc::CExpression>())
+ return op.emitOpError(
+ "expected all operations in condition region must implement "
+ "CExpression trait, but ")
+ << inner.getName() << " does not";
+ }
+
+ auto condYield = dyn_cast<emitc::YieldOp>(condBlock.back());
+ if (!condYield)
+ return op.emitOpError(
+ "expected condition region to end with emitc.yield, but got ")
+ << condBlock.back().getName();
+
+ if (condYield.getNumOperands() != 1 ||
+ !condYield.getOperand(0).getType().isInteger(1))
+ return op.emitOpError("condition region must yield a single i1 value");
+
+ if (body.empty())
+ return op.emitOpError("body region cannot be empty");
+
+ Block &bodyBlock = body.front();
+ if (auto bodyYield = dyn_cast<emitc::YieldOp>(bodyBlock.back()))
+ if (bodyYield.getNumOperands() != 0)
+ return op.emitOpError(
+ "expected body region to return 0 values, but body returns ")
+ << bodyYield.getNumOperands();
+
+ return success();
+}
+
+static void printLoop(OpAsmPrinter &p, Operation *self, Region &first,
+ StringRef midKeyword, Region &second) {
+ p << ' ';
+ p.printRegion(first, /*printEntryBlockArgs=*/false);
+ p << ' ' << midKeyword << ' ';
+ p.printRegion(second);
+ p.printOptionalAttrDictWithKeyword(self->getAttrs());
+}
+
+static ParseResult parseLoop(OpAsmParser &parser, OperationState &res,
+ StringRef midKeyword) {
+ Region *firstRegion = res.addRegion();
+ Region *secondRegion = res.addRegion();
+
+ if (parser.parseRegion(*firstRegion))
+ return failure();
+ if (parser.parseKeyword(midKeyword) || parser.parseRegion(*secondRegion))
+ return failure();
+
+ return parser.parseOptionalAttrDictWithKeyword(res.attributes);
+}
+
+//===----------------------------------------------------------------------===//
+// WhileOp
+//===----------------------------------------------------------------------===//
+
+Operation *WhileOp::getRootOp() {
+ return getRootOpFromLoopCondition(getConditionRegion());
+}
+
+void WhileOp::print(OpAsmPrinter &p) {
+ printLoop(p, getOperation(), getConditionRegion(), "do", getBodyRegion());
+}
+
+LogicalResult emitc::WhileOp::verify() {
+ return verifyLoopRegions(*getOperation(), getConditionRegion(),
+ getBodyRegion());
+}
+
+ParseResult WhileOp::parse(OpAsmParser &parser, OperationState &result) {
+ return parseLoop(parser, result, "do");
+}
+
+//===----------------------------------------------------------------------===//
+// DoOp
+//===----------------------------------------------------------------------===//
+
+Operation *DoOp::getRootOp() {
+ return getRootOpFromLoopCondition(getConditionRegion());
+}
+
+void DoOp::print(OpAsmPrinter &p) {
+ printLoop(p, getOperation(), getBodyRegion(), "while", getConditionRegion());
+}
+
+LogicalResult emitc::DoOp::verify() {
+ return verifyLoopRegions(*getOperation(), getConditionRegion(),
+ getBodyRegion());
+}
+
+ParseResult DoOp::parse(OpAsmParser &parser, OperationState &result) {
+ return parseLoop(parser, result, "while");
+}
+
//===----------------------------------------------------------------------===//
// TableGen'd op method definitions
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 5abc112ab8c7a..18f25303a34b4 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -180,6 +180,12 @@ struct CppEmitter {
/// Emit an expression as a C expression.
LogicalResult emitExpression(ExpressionOp expressionOp);
+ /// Emit while as a C while.
+ LogicalResult emitWhile(WhileOp expressionOp);
+
+ /// Emit do-while as a C do-while.
+ LogicalResult emitDo(DoOp expressionOp);
+
/// Insert the expression representing the operation into the value cache.
void cacheDeferredOpResult(Value value, StringRef str);
@@ -198,6 +204,8 @@ struct CppEmitter {
/// Return the existing or a new label of a Block.
StringRef getOrCreateName(Block &block);
+ LogicalResult emitInlinedExpression(Value value);
+
/// Whether to map an mlir integer to a unsigned integer in C++.
bool shouldMapToUnsigned(IntegerType::SignednessSemantics val);
@@ -239,7 +247,7 @@ struct CppEmitter {
}
/// Get expression currently being emitted.
- ExpressionOp getEmittedExpression() { return emittedExpression; }
+ Operation *getEmittedExpression() { return emittedExpression; }
/// Determine whether given value is part of the expression potentially being
/// emitted.
@@ -249,7 +257,8 @@ struct CppEmitter {
Operation *def = value.getDefiningOp();
if (!def)
return false;
- auto operandExpression = dyn_cast<ExpressionOp>(def->getParentOp());
+
+ Operation *operandExpression = def->getParentOp();
return operandExpression == emittedExpression;
};
@@ -280,7 +289,7 @@ struct CppEmitter {
std::stack<int64_t> labelInScopeCount;
/// State of the current expression being emitted.
- ExpressionOp emittedExpression;
+ Operation *emittedExpression = nullptr;
SmallVector<int> emittedExpressionPrecedence;
void pushExpressionPrecedence(int precedence) {
@@ -510,6 +519,16 @@ static LogicalResult printOperation(CppEmitter &emitter,
return success();
}
+static LogicalResult printOperation(CppEmitter &emitter,
+ emitc::WhileOp whileOp) {
+ return emitter.emitWhile(whileOp);
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+ emitc::DoOp doWhileOp) {
+ return emitter.emitDo(doWhileOp);
+}
+
static LogicalResult printOperation(CppEmitter &emitter, emitc::CmpOp cmpOp) {
Operation *operation = cmpOp.getOperation();
@@ -1424,6 +1443,84 @@ LogicalResult CppEmitter::emitExpression(ExpressionOp expressionOp) {
return success();
}
+LogicalResult CppEmitter::emitWhile(WhileOp whileOp) {
+ assert(emittedExpressionPrecedence.empty() &&
+ "Expected precedence stack to be empty");
+ Operation *rootOp = whileOp.getRootOp();
+
+ emittedExpression = whileOp;
+ FailureOr<int> precedence = getOperatorPrecedence(rootOp);
+ if (failed(precedence))
+ return failure();
+ pushExpressionPrecedence(precedence.value());
+
+ os << "while (";
+ if (failed(emitOperation(*rootOp, /*trailingSemicolon=*/false)))
+ return failure();
+ os << ") {\n";
+
+ popExpressionPrecedence();
+ assert(emittedExpressionPrecedence.empty() &&
+ "Expected precedence stack to be empty");
+ emittedExpression = nullptr;
+
+ os.indent();
+
+ Region &bodyRegion = whileOp.getBodyRegion();
+ auto regionOps = bodyRegion.getOps();
+
+ for (Operation &op : regionOps) {
+ if (isa<emitc::YieldOp>(op))
+ continue;
+
+ if (failed(emitOperation(op, /*trailingSemicolon=*/true)))
+ return failure();
+ }
+
+ os.unindent() << "}";
+
+ return success();
+}
+
+LogicalResult CppEmitter::emitDo(DoOp doWhileOp) {
+ os << "do {\n";
+ os.indent();
+
+ Region &bodyRegion = doWhileOp.getBodyRegion();
+ auto regionOps = bodyRegion.getOps();
+
+ for (Operation &op : regionOps) {
+ if (isa<emitc::YieldOp>(op))
+ continue;
+
+ if (failed(emitOperation(op, /*trailingSemicolon=*/true)))
+ return failure();
+ }
+
+ os.unindent() << "} while (";
+
+ assert(emittedExpressionPrecedence.empty() &&
+ "Expected precedence stack to be empty");
+ Operation *rootOp = doWhileOp.getRootOp();
+
+ emittedExpression = doWhileOp;
+ FailureOr<int> precedence = getOperatorPrecedence(rootOp);
+ if (failed(precedence))
+ return failure();
+ pushExpressionPrecedence(precedence.value());
+
+ if (failed(emitOperation(*rootOp, /*trailingSemicolon=*/false)))
+ return failure();
+ os << ");";
+
+ popExpressionPrecedence();
+ assert(emittedExpressionPrecedence.empty() &&
+ "Expected precedence stack to be empty");
+ emittedExpression = nullptr;
+
+ return success();
+}
+
LogicalResult CppEmitter::emitOperand(Value value) {
if (isPartOfCurrentExpression(value)) {
Operation *def = value.getDefiningOp();
@@ -1607,12 +1704,13 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
emitc::CallOpaqueOp, emitc::CastOp, emitc::CmpOp,
emitc::ConditionalOp, emitc::ConstantOp, emitc::DeclareFuncOp,
- emitc::DivOp, emitc::ExpressionOp, emitc::FileOp, emitc::ForOp,
- emitc::FuncOp, emitc::GlobalOp, emitc::IfOp, emitc::IncludeOp,
- emitc::LoadOp, emitc::LogicalAndOp, emitc::LogicalNotOp,
- emitc::LogicalOrOp, emitc::MulOp, emitc::RemOp, emitc::ReturnOp,
- emitc::SubOp, emitc::SwitchOp, emitc::UnaryMinusOp,
- emitc::UnaryPlusOp, emitc::VariableOp, emitc::VerbatimOp>(
+ emitc::DivOp, emitc::DoOp, emitc::ExpressionOp, emitc::FileOp,
+ emitc::ForOp, emitc::FuncOp, emitc::GlobalOp, emitc::IfOp,
+ emitc::IncludeOp, emitc::LoadOp, emitc::LogicalAndOp,
+ emitc::LogicalNotOp, emitc::LogicalOrOp, emitc::MulOp,
+ emitc::RemOp, emitc::ReturnOp, emitc::SubOp, emitc::SwitchOp,
+ emitc::UnaryMinusOp, emitc::UnaryPlusOp, emitc::VariableOp,
+ emitc::VerbatimOp, emitc::WhileOp>(
[&](auto op) { return printOperation(*this, op); })
// Func ops.
@@ -1656,9 +1754,9 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
// Never emit a semicolon for some operations, especially if endening with
// `}`.
trailingSemicolon &=
- !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::FileOp, emitc::ForOp,
- emitc::IfOp, emitc::IncludeOp, emitc::SwitchOp, emitc::VerbatimOp>(
- op);
+ !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::DoOp, emitc::FileOp,
+ emitc::ForOp, emitc::IfOp, emitc::IncludeOp, emitc::SwitchOp,
+ emitc::VerbatimOp, emitc::WhileOp>(op);
os << (trailingSemicolon ? ";\n" : "\n");
diff --git a/mlir/test/Conversion/SCFToEmitC/while.mlir b/mlir/test/Conversion/SCFToEmitC/while.mlir
new file mode 100644
index 0000000000000..d4d64fac29280
--- /dev/null
+++ b/mlir/test/Conversion/SCFToEmitC/while.mlir
@@ -0,0 +1,235 @@
+// RUN: mlir-opt -allow-unregistered-dialect -convert-scf-to-emitc %s | FileCheck %s
+// RUN: mlir-opt -allow-unregistered-dialect -convert-to-emitc="filter-dialects=scf" %s | FileCheck %s
+
+emitc.func @payload_whileOp(%arg: i32) -> i32 {
+ %result = emitc.add %arg, %arg : (i32, i32) -> i32
+ return %result : i32
+}
+
+func.func @whileOp() -> i32 {
+ %init = emitc.literal "1.0" : i32
+ %var = emitc.literal "1.0" : i32
+ %exit = emitc.literal "10.0" : i32
+
+ %res = scf.while (%arg1 = %init) : (i32) -> i32 {
+ %sum = emitc.add %arg1, %var : (i32, i32) -> i32
+ %condition = emitc.cmp lt, %sum, %exit : (i32, i32) -> i1
+ scf.condition(%condition) %arg1 : i32
+ } do {
+ ^bb0(%arg2: i32):
+ %next_arg1 = emitc.call @payload_whileOp(%arg2) : (i32) -> i32
+ scf.yield %next_arg1 : i32
+ }
+
+ return %res : i32
+}
+// CHECK-LABEL: emitc.func @payload_whileOp(
+// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
+// CHECK: %[[VAL_0:.*]] = add %[[ARG0]], %[[ARG0]] : (i32, i32) -> i32
+// CHECK: return %[[VAL_0]] : i32
+// CHECK: }
+
+// CHECK-LABEL: func.func @whileOp() -> i32 {
+// CHECK: %[[VAL_0:.*]] = emitc.literal "1.0" : i32
+// CHECK: %[[VAL_1:.*]] = emitc.literal "1.0" : i32
+// CHECK: %[[VAL_2:.*]] = emitc.literal "10.0" : i32
+// CHECK: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: emitc.while {
+// CHECK: %[[VAL_4:.*]] = load %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_5:.*]] = add %[[VAL_4]], %[[VAL_1]] : (i32, i32) -> i32
+// CHECK: %[[VAL_6:.*]] = cmp lt, %[[VAL_5]], %[[VAL_2]] : (i32, i32) -> i1
+// CHECK: yield %[[VAL_6]] : i1
+// CHECK: } do {
+// CHECK: %[[VAL_7:.*]] = load %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_8:.*]] = call @payload_whileOp(%[[VAL_7]]) : (i32) -> i32
+// CHECK: assign %[[VAL_8]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: }
+// CHECK: %[[VAL_9:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: %[[VAL_10:.*]] = emitc.load %[[VAL_3]] : <i32>
+// CHECK: emitc.assign %[[VAL_10]] : i32 to %[[VAL_9]] : <i32>
+// CHECK: %[[VAL_11:.*]] = emitc.load %[[VAL_9]] : <i32>
+// CHECK: return %[[VAL_11]] : i32
+// CHECK: }
+
+emitc.func @payload_doOp(%arg: i32) -> i32 {
+ %result = emitc.add %arg, %arg : (i32, i32) -> i32
+ return %result : i32
+}
+
+func.func @doOp() -> i32 {
+ %init = emitc.literal "1.0" : i32
+ %var = emitc.literal "1.0" : i32
+ %exit = emitc.literal "10.0" : i32
+
+ %res = scf.while (%arg1 = %init) : (i32) -> i32 {
+ %sum = emitc.add %arg1, %var : (i32, i32) -> i32
+ %condition = emitc.cmp lt, %sum, %exit : (i32, i32) -> i1
+ %next = emitc.add %arg1, %arg1 : (i32, i32) -> i32
+ scf.condition(%condition) %next : i32
+ } do {
+ ^bb0(%arg2: i32):
+ %next_arg1 = emitc.call @payload_doOp(%arg2) : (i32) -> i32
+ scf.yield %next_arg1 : i32
+ }
+
+ return %res : i32
+}
+// CHECK-LABEL: emitc.func @payload_doOp(
+// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
+// CHECK: %[[VAL_0:.*]] = add %[[ARG0]], %[[ARG0]] : (i32, i32) -> i32
+// CHECK: return %[[VAL_0]] : i32
+// CHECK: }
+
+// CHECK-LABEL: func.func @doOp() -> i32 {
+// CHECK: %[[VAL_0:.*]] = emitc.literal "1.0" : i32
+// CHECK: %[[VAL_1:.*]] = emitc.literal "1.0" : i32
+// CHECK: %[[VAL_2:.*]] = emitc.literal "10.0" : i32
+// CHECK: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_4:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i1>
+// CHECK: emitc.do {
+// CHECK: %[[VAL_5:.*]] = load %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_6:.*]] = add %[[VAL_5]], %[[VAL_1]] : (i32, i32) -> i32
+// CHECK: %[[VAL_7:.*]] = cmp lt, %[[VAL_6]], %[[VAL_2]] : (i32, i32) -> i1
+// CHECK: %[[VAL_8:.*]] = add %[[VAL_5]], %[[VAL_5]] : (i32, i32) -> i32
+// CHECK: assign %[[VAL_7]] : i1 to %[[VAL_4]] : <i1>
+// CHECK: if %[[VAL_7]] {
+// CHECK: %[[VAL_9:.*]] = call @payload_doOp(%[[VAL_8]]) : (i32) -> i32
+// CHECK: assign %[[VAL_9]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: }
+// CHECK: } while {
+// CHECK: %[[VAL_10:.*]] = load %[[VAL_4]] : <i1>
+// CHECK: yield %[[VAL_10]] : i1
+// CHECK: }
+// CHECK: %[[VAL_11:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: %[[VAL_12:.*]] = emitc.load %[[VAL_3]] : <i32>
+// CHECK: emitc.assign %[[VAL_12]] : i32 to %[[VAL_11]] : <i32>
+// CHECK: %[[VAL_13:.*]] = emitc.load %[[VAL_11]] : <i32>
+// CHECK: return %[[VAL_13]] : i32
+// CHECK: }
+
+emitc.func @payload_whileOp_two_results(%arg: i32) -> i32 {
+ %result = emitc.add %arg, %arg : (i32, i32) -> i32
+ return %result : i32
+}
+
+func.func @whileOp_two_results() -> i32 {
+ %init = emitc.literal "1.0" : i32
+ %exit = emitc.literal "10.0" : i32
+
+ %res1, %res2 = scf.while (%arg1_1 = %init, %arg1_2 = %init) : (i32, i32) -> (i32, i32) {
+ %sum = emitc.add %arg1_1, %arg1_2 : (i32, i32) -> i32
+ %condition = emitc.cmp lt, %sum, %exit : (i32, i32) -> i1
+ scf.condition(%condition) %arg1_1, %arg1_2 : i32, i32
+ } do {
+ ^bb0(%arg2_1 : i32, %arg2_2 : i32):
+ %next1 = emitc.call @payload_whileOp_two_results(%arg2_1) : (i32) -> i32
+ %next2 = emitc.call @payload_whileOp_two_results(%arg2_2) : (i32) -> i32
+ scf.yield %next1, %next2 : i32, i32
+ }
+
+ return %res1 : i32
+}
+
+// CHECK-LABEL: emitc.func @payload_whileOp_two_results(
+// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
+// CHECK: %[[VAL_0:.*]] = add %[[ARG0]], %[[ARG0]] : (i32, i32) -> i32
+// CHECK: return %[[VAL_0]] : i32
+// CHECK: }
+
+// CHECK-LABEL: func.func @whileOp_two_results() -> i32 {
+// CHECK: %[[VAL_0:.*]] = emitc.literal "1.0" : i32
+// CHECK: %[[VAL_1:.*]] = emitc.literal "10.0" : i32
+// CHECK: %[[VAL_2:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_2]] : <i32>
+// CHECK: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: emitc.while {
+// CHECK: %[[VAL_4:.*]] = load %[[VAL_2]] : <i32>
+// CHECK: %[[VAL_5:.*]] = load %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_6:.*]] = add %[[VAL_4]], %[[VAL_5]] : (i32, i32) -> i32
+// CHECK: %[[VAL_7:.*]] = cmp lt, %[[VAL_6]], %[[VAL_1]] : (i32, i32) -> i1
+// CHECK: yield %[[VAL_7]] : i1
+// CHECK: } do {
+// CHECK: %[[VAL_8:.*]] = load %[[VAL_2]] : <i32>
+// CHECK: %[[VAL_9:.*]] = load %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_10:.*]] = call @payload_whileOp_two_results(%[[VAL_8]]) : (i32) -> i32
+// CHECK: %[[VAL_11:.*]] = call @payload_whileOp_two_results(%[[VAL_9]]) : (i32) -> i32
+// CHECK: assign %[[VAL_10]] : i32 to %[[VAL_2]] : <i32>
+// CHECK: assign %[[VAL_11]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: }
+// CHECK: %[[VAL_12:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: %[[VAL_13:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: %[[VAL_14:.*]] = emitc.load %[[VAL_2]] : <i32>
+// CHECK: emitc.assign %[[VAL_14]] : i32 to %[[VAL_12]] : <i32>
+// CHECK: %[[VAL_15:.*]] = emitc.load %[[VAL_3]] : <i32>
+// CHECK: emitc.assign %[[VAL_15]] : i32 to %[[VAL_13]] : <i32>
+// CHECK: %[[VAL_16:.*]] = emitc.load %[[VAL_12]] : <i32>
+// CHECK: %[[VAL_17:.*]] = emitc.load %[[VAL_13]] : <i32>
+// CHECK: return %[[VAL_16]] : i32
+// CHECK: }
+
+emitc.func @payload_doOp_two_results(%arg: i32) -> i32 {
+ %result = emitc.add %arg, %arg : (i32, i32) -> i32
+ return %result : i32
+}
+
+func.func @doOp_two_results() -> i32 {
+ %init = emitc.literal "1.0" : i32
+ %exit = emitc.literal "10.0" : i32
+
+ %res1, %res2 = scf.while (%arg1_1 = %init, %arg1_2 = %init) : (i32, i32) -> (i32, i32) {
+ %sum = emitc.add %arg1_1, %arg1_2 : (i32, i32) -> i32
+ %condition = emitc.cmp lt, %sum, %exit : (i32, i32) -> i1
+ scf.condition(%condition) %init, %arg1_2 : i32, i32
+ } do {
+ ^bb0(%arg2_1 : i32, %arg2_2 : i32):
+ %next1 = emitc.call @payload_doOp_two_results(%arg2_1) : (i32) -> i32
+ %next2 = emitc.call @payload_doOp_two_results(%arg2_2) : (i32) -> i32
+ scf.yield %next1, %next2 : i32, i32
+ }
+
+ return %res1 : i32
+}
+
+// CHECK-LABEL: emitc.func @payload_doOp_two_results(
+// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
+// CHECK: %[[VAL_0:.*]] = add %[[ARG0]], %[[ARG0]] : (i32, i32) -> i32
+// CHECK: return %[[VAL_0]] : i32
+// CHECK: }
+
+// CHECK-LABEL: func.func @doOp_two_results() -> i32 {
+// CHECK: %[[VAL_0:.*]] = emitc.literal "1.0" : i32
+// CHECK: %[[VAL_1:.*]] = emitc.literal "10.0" : i32
+// CHECK: %[[VAL_2:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_2]] : <i32>
+// CHECK: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_4:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i1>
+// CHECK: emitc.do {
+// CHECK: %[[VAL_5:.*]] = load %[[VAL_2]] : <i32>
+// CHECK: %[[VAL_6:.*]] = load %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_7:.*]] = add %[[VAL_5]], %[[VAL_6]] : (i32, i32) -> i32
+// CHECK: %[[VAL_8:.*]] = cmp lt, %[[VAL_7]], %[[VAL_1]] : (i32, i32) -> i1
+// CHECK: assign %[[VAL_8]] : i1 to %[[VAL_4]] : <i1>
+// CHECK: if %[[VAL_8]] {
+// CHECK: %[[VAL_9:.*]] = call @payload_doOp_two_results(%[[VAL_0]]) : (i32) -> i32
+// CHECK: %[[VAL_10:.*]] = call @payload_doOp_two_results(%[[VAL_6]]) : (i32) -> i32
+// CHECK: assign %[[VAL_9]] : i32 to %[[VAL_2]] : <i32>
+// CHECK: assign %[[VAL_10]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: }
+// CHECK: } while {
+// CHECK: %[[VAL_11:.*]] = load %[[VAL_4]] : <i1>
+// CHECK: yield %[[VAL_11]] : i1
+// CHECK: }
+// CHECK: %[[VAL_12:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: %[[VAL_13:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: %[[VAL_14:.*]] = emitc.load %[[VAL_2]] : <i32>
+// CHECK: emitc.assign %[[VAL_14]] : i32 to %[[VAL_12]] : <i32>
+// CHECK: %[[VAL_15:.*]] = emitc.load %[[VAL_3]] : <i32>
+// CHECK: emitc.assign %[[VAL_15]] : i32 to %[[VAL_13]] : <i32>
+// CHECK: %[[VAL_16:.*]] = emitc.load %[[VAL_12]] : <i32>
+// CHECK: %[[VAL_17:.*]] = emitc.load %[[VAL_13]] : <i32>
+// CHECK: return %[[VAL_16]] : i32
+// CHECK: }
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index 3793dfe3f173b..acc8059658e34 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -252,7 +252,7 @@ func.func @sub_pointer_pointer(%arg0: !emitc.ptr<f32>, %arg1: !emitc.ptr<f32>) {
// -----
func.func @test_misplaced_yield() {
- // expected-error @+1 {{'emitc.yield' op expects parent op to be one of 'emitc.expression, emitc.if, emitc.for, emitc.switch'}}
+ // expected-error @+1 {{'emitc.yield' op expects parent op to be one of 'emitc.do, emitc.expression, emitc.for, emitc.if, emitc.switch, emitc.while'}}
emitc.yield
return
}
@@ -654,3 +654,197 @@ func.func @test_verbatim(%arg0 : !emitc.ptr<i32>, %arg1 : i32) {
emitc.verbatim "{a} " args %arg0, %arg1 : !emitc.ptr<i32>, i32
return
}
+
+// -----
+
+func.func @test_while(%arg0 : !emitc.ptr<i32>) {
+ // expected-error @+1 {{'emitc.while' op condition region cannot be empty}}
+ emitc.while {
+ } do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ }
+
+ return
+}
+
+// -----
+
+emitc.func @test_while(%arg0 : !emitc.ptr<i32>) {
+ %1 = emitc.literal "1" : i32
+
+ // expected-error @+1 {{'emitc.while' op expected all operations in condition region must implement CExpression trait, but emitc.literal does not}}
+ emitc.while {
+ %2 = emitc.literal "2" : i32
+ %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %3 : i1
+ } do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ }
+
+ return
+}
+
+// -----
+
+func.func @test_while(%arg0 : !emitc.ptr<i32>) {
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+
+ // expected-error @+1 {{'emitc.while' op expected condition region to end with emitc.yield, but got emitc.cmp}}
+ emitc.while {
+ %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ } do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ }
+
+ return
+}
+
+// -----
+
+func.func @test_while(%arg0 : !emitc.ptr<i32>) {
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+
+ // expected-error @+1 {{'emitc.while' op condition region must yield a single i1 value}}
+ emitc.while {
+ %3 = emitc.add %1, %2 : (i32, i32) -> i32
+ emitc.yield %3 : i32
+ } do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ }
+
+ return
+}
+
+// -----
+
+func.func @test_while() {
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+
+ // expected-error @+1 {{'emitc.while' op body region cannot be empty}}
+ emitc.while {
+ %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %3 : i1
+ } do {
+ }
+
+ return
+}
+
+// -----
+
+emitc.func @test_while(%arg0 : !emitc.ptr<i32>) {
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+
+ // expected-error @+1 {{'emitc.while' op expected body region to return 0 values, but body returns 1}}
+ emitc.while {
+ %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %3 : i1
+ } do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ %4 = emitc.add %1, %2 : (i32, i32) -> i32
+ emitc.yield %4 : i32
+ }
+
+ return
+}
+
+// -----
+
+func.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
+ // expected-error @+1 {{'emitc.do' op condition region cannot be empty}}
+ emitc.do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ } while {
+ }
+
+ return
+}
+
+// -----
+
+emitc.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
+ %1 = emitc.literal "1" : i32
+
+ // expected-error @+1 {{'emitc.do' op expected all operations in condition region must implement CExpression trait, but emitc.literal does not}}
+ emitc.do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ } while {
+ %2 = emitc.literal "2" : i32
+ %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %3 : i1
+ }
+
+ return
+}
+
+// -----
+
+func.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+
+ // expected-error @+1 {{'emitc.do' op expected condition region to end with emitc.yield, but got emitc.cmp}}
+ emitc.do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ } while {
+ %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ }
+
+ return
+}
+
+// -----
+
+func.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+
+ // expected-error @+1 {{'emitc.do' op condition region must yield a single i1 value}}
+ emitc.do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ } while {
+ %3 = emitc.add %1, %2 : (i32, i32) -> i32
+ emitc.yield %3 : i32
+ }
+
+ return
+}
+
+// -----
+
+func.func @test_do_while() {
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+
+ // expected-error @+1 {{'emitc.do' op body region cannot be empty}}
+ emitc.do {
+ } while {
+ %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %3 : i1
+ }
+
+ return
+}
+
+// -----
+
+emitc.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+
+ // expected-error @+1 {{'emitc.do' op expected body region to return 0 values, but body returns 1}}
+ emitc.do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ %4 = emitc.add %1, %2 : (i32, i32) -> i32
+ emitc.yield %4 : i32
+ } while {
+ %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %3 : i1
+ }
+
+ return
+}
diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir
index 36d12e763afc7..739340f566dc4 100644
--- a/mlir/test/Dialect/EmitC/ops.mlir
+++ b/mlir/test/Dialect/EmitC/ops.mlir
@@ -302,3 +302,35 @@ func.func @switch() {
return
}
+
+func.func @while(%arg0 : !emitc.ptr<i32>) {
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+ %3 = emitc.literal "3" : i32
+
+ emitc.while {
+ %add = emitc.add %1, %2 : (i32, i32) -> i32
+ %5 = emitc.cmp eq, %add, %3 : (i32, i32) -> i1
+ emitc.yield %5 : i1
+ } do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ }
+
+ return
+}
+
+func.func @do(%arg0 : !emitc.ptr<i32>) {
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+ %3 = emitc.literal "3" : i32
+
+ emitc.do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ } while {
+ %add = emitc.add %1, %2 : (i32, i32) -> i32
+ %5 = emitc.cmp eq, %add, %3 : (i32, i32) -> i1
+ emitc.yield %5 : i1
+ }
+
+ return
+}
diff --git a/mlir/test/Target/Cpp/do.mlir b/mlir/test/Target/Cpp/do.mlir
new file mode 100644
index 0000000000000..034fe77ed22c4
--- /dev/null
+++ b/mlir/test/Target/Cpp/do.mlir
@@ -0,0 +1,69 @@
+// RUN: mlir-translate -mlir-to-cpp %s | FileCheck --match-full-lines %s -check-prefix=CPP-DEFAULT
+
+
+// CPP-DEFAULT-LABEL: void emitc_do(int32_t* v1) {
+// CPP-DEFAULT: int32_t v2 = 0;
+// CPP-DEFAULT: do {
+// CPP-DEFAULT: printf("%d", *v1);
+// CPP-DEFAULT: int32_t v3 = v2;
+// CPP-DEFAULT: int32_t v4 = v3 + 1;
+// CPP-DEFAULT: v2 = v4;
+// CPP-DEFAULT: } while (v2 <= 10);
+// CPP-DEFAULT: return;
+// CPP-DEFAULT: }
+
+emitc.func @emitc_do(%arg0 : !emitc.ptr<i32>) {
+ %var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+ %0 = emitc.literal "10" : i32
+ %1 = emitc.literal "1" : i32
+
+ emitc.do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ %var_load = load %var : <i32>
+ %tmp_add = add %var_load, %1 : (i32, i32) -> i32
+ "emitc.assign"(%var, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
+ } while {
+ %var_load = load %var : <i32>
+ %res = emitc.cmp le, %var_load, %0 : (i32, i32) -> i1
+ emitc.yield %res : i1
+ }
+
+ return
+}
+
+
+// CPP-DEFAULT-LABEL: void emitc_do_with_expression(int32_t* v1) {
+// CPP-DEFAULT: int32_t v2 = 0;
+// CPP-DEFAULT: int32_t v3 = 10 + 1;
+// CPP-DEFAULT: do {
+// CPP-DEFAULT: printf("%d", *v1);
+// CPP-DEFAULT: int32_t v4 = v2;
+// CPP-DEFAULT: int32_t v5 = v4 + 1;
+// CPP-DEFAULT: v2 = v5;
+// CPP-DEFAULT: } while (v2 <= v3);
+// CPP-DEFAULT: return;
+// CPP-DEFAULT: }
+
+emitc.func @emitc_do_with_expression(%arg0 : !emitc.ptr<i32>) {
+ %var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+ %0 = emitc.literal "10" : i32
+ %1 = emitc.literal "1" : i32
+
+ %add = emitc.expression : i32 {
+ %add = add %0, %1 : (i32, i32) -> i32
+ yield %add : i32
+ }
+
+ emitc.do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ %var_load = load %var : <i32>
+ %tmp_add = add %var_load, %1 : (i32, i32) -> i32
+ "emitc.assign"(%var, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
+ } while {
+ %var_load = load %var : <i32>
+ %res = emitc.cmp le, %var_load, %add : (i32, i32) -> i1
+ emitc.yield %res : i1
+ }
+
+ return
+}
diff --git a/mlir/test/Target/Cpp/while.mlir b/mlir/test/Target/Cpp/while.mlir
new file mode 100644
index 0000000000000..76cf7c2f752cf
--- /dev/null
+++ b/mlir/test/Target/Cpp/while.mlir
@@ -0,0 +1,69 @@
+// RUN: mlir-translate -mlir-to-cpp %s | FileCheck --match-full-lines %s -check-prefix=CPP-DEFAULT
+
+
+// CPP-DEFAULT-LABEL: void emitc_while(int32_t* v1) {
+// CPP-DEFAULT: int32_t v2 = 0;
+// CPP-DEFAULT: while (v2 <= 10) {
+// CPP-DEFAULT: printf("%d", *v1);
+// CPP-DEFAULT: int32_t v3 = v2;
+// CPP-DEFAULT: int32_t v4 = v3 + 1;
+// CPP-DEFAULT: v2 = v4;
+// CPP-DEFAULT: }
+// CPP-DEFAULT: return;
+// CPP-DEFAULT: }
+
+emitc.func @emitc_while(%arg0 : !emitc.ptr<i32>) {
+ %var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+ %0 = emitc.literal "10" : i32
+ %1 = emitc.literal "1" : i32
+
+ emitc.while {
+ %var_load = load %var : <i32>
+ %res = emitc.cmp le, %var_load, %0 : (i32, i32) -> i1
+ emitc.yield %res : i1
+ } do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ %var_load = load %var : <i32>
+ %tmp_add = add %var_load, %1 : (i32, i32) -> i32
+ "emitc.assign"(%var, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
+ }
+
+ return
+}
+
+
+// CPP-DEFAULT-LABEL: void emitc_while_with_expression(int32_t* v1) {
+// CPP-DEFAULT: int32_t v2 = 0;
+// CPP-DEFAULT: int32_t v3 = 10 + 1;
+// CPP-DEFAULT: while (v2 <= v3) {
+// CPP-DEFAULT: printf("%d", *v1);
+// CPP-DEFAULT: int32_t v4 = v2;
+// CPP-DEFAULT: int32_t v5 = v4 + 1;
+// CPP-DEFAULT: v2 = v5;
+// CPP-DEFAULT: }
+// CPP-DEFAULT: return;
+// CPP-DEFAULT: }
+
+emitc.func @emitc_while_with_expression(%arg0 : !emitc.ptr<i32>) {
+ %var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+ %0 = emitc.literal "10" : i32
+ %1 = emitc.literal "1" : i32
+
+ %add = emitc.expression : i32 {
+ %add = add %0, %1 : (i32, i32) -> i32
+ yield %add : i32
+ }
+
+ emitc.while {
+ %var_load = load %var : <i32>
+ %res = emitc.cmp le, %var_load, %add : (i32, i32) -> i1
+ emitc.yield %res : i1
+ } do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ %var_load = load %var : <i32>
+ %tmp_add = add %var_load, %1 : (i32, i32) -> i32
+ "emitc.assign"(%var, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
+ }
+
+ return
+}
More information about the Mlir-commits
mailing list