[Mlir-commits] [mlir] [mlir][emitc] Add 'emitc.while' and 'emitc.do' ops to the dialect (PR #143008)
Vlad Lazar
llvmlistbot at llvm.org
Fri Sep 26 04:57:26 PDT 2025
https://github.com/Vladislave0-0 updated https://github.com/llvm/llvm-project/pull/143008
>From c7f754e8aca15d3612922d0dbe8cb2343b2533a9 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 01/13] [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 | 236 +++++++++++++++++-
mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 116 ++++++++-
mlir/lib/Target/Cpp/TranslateToCpp.cpp | 119 ++++++++-
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, 1210 insertions(+), 16 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 721f9f6b320ad..133aefccd59ad 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1393,7 +1393,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
@@ -1727,4 +1727,156 @@ def EmitC_GetFieldOp
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 1f239aa5d1aa3..6aff0ce98ab91 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -336,11 +336,244 @@ 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() {
@@ -357,7 +590,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 5c8564bca6f86..9b3ea203cad07 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -974,10 +974,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();
@@ -1561,6 +1563,116 @@ LogicalResult GetFieldOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
return success();
}
+//===----------------------------------------------------------------------===//
+// 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 a5bd80e9d6b8b..dcc2bd6e3f07b 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -179,6 +179,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);
@@ -201,6 +207,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);
@@ -258,7 +266,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.
@@ -322,7 +330,7 @@ struct CppEmitter {
unsigned int valueCount{0};
/// State of the current expression being emitted.
- ExpressionOp emittedExpression;
+ Operation *emittedExpression = nullptr;
SmallVector<int> emittedExpressionPrecedence;
void pushExpressionPrecedence(int precedence) {
@@ -557,6 +565,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();
@@ -1514,6 +1532,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();
@@ -1711,13 +1807,14 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
emitc::CallOpaqueOp, emitc::CastOp, emitc::ClassOp,
emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp,
- emitc::DeclareFuncOp, emitc::DivOp, emitc::ExpressionOp,
+ emitc::DeclareFuncOp, emitc::DivOp, emitc::DoOp, emitc::ExpressionOp,
emitc::FieldOp, 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::GetFieldOp, 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.
@@ -1765,9 +1862,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 f4c15f50053a8..4644e71a5101e 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
}
@@ -729,3 +729,197 @@ emitc.class @testClass {
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 84c9b65d775d2..6a41c839399f4 100644
--- a/mlir/test/Dialect/EmitC/ops.mlir
+++ b/mlir/test/Dialect/EmitC/ops.mlir
@@ -335,3 +335,35 @@ emitc.class final @finalClass {
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
+}
>From 78712dbdef1c01a4687e6690c9b947acc23f13b1 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Wed, 23 Jul 2025 15:58:05 +0300
Subject: [PATCH 02/13] [mlir][emitc] Change conditional region structure
Change the canonical structure of a conditional region:
- The condition region must contain exactly one block with:
1. An `emitc.expression` operation producing an i1 value
2. An `emitc.yield` passing through the expression result
- The body region must not yield any values
---
mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 172 ++++++++-----
mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp | 48 ++--
mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 61 +++--
mlir/lib/Target/Cpp/TranslateToCpp.cpp | 147 ++++-------
mlir/test/Conversion/SCFToEmitC/while.mlir | 154 ++++++------
mlir/test/Dialect/EmitC/invalid_ops.mlir | 229 ++++++++++++++----
mlir/test/Dialect/EmitC/ops.mlir | 20 +-
mlir/test/Target/Cpp/do.mlir | 97 ++++++--
mlir/test/Target/Cpp/while.mlir | 97 ++++++--
9 files changed, 680 insertions(+), 345 deletions(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 133aefccd59ad..8f58d671e20bf 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1728,63 +1728,88 @@ def EmitC_GetFieldOp
}
def EmitC_WhileOp : EmitC_Op<"while",
- [HasOnlyGraphRegion, RecursiveMemoryEffects, NoRegionArguments, OpAsmOpInterface, NoTerminator]> {
+ [NoTerminator, OpAsmOpInterface, RecursiveMemoryEffects]> {
let summary = "While operation";
- let description = [{
+ 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)
+ 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.
+ The condition is evaluated before each iteration as follows:
+ - The condition region must contain exactly one block with:
+ 1. An `emitc.expression` operation producing an i1 value
+ 2. An `emitc.yield` passing through the expression result
+ - The expression's body contains the actual condition logic
+
+ If the condition yields true, the body region is executed. The loop terminates
+ when the condition yields false. The body region must not yield any values.
+
+ The canonical structure of `emitc.while` is:
+
+ ```mlir
+ emitc.while {
+ // Condition region (must yield i1)
+ %condition = emitc.expression : i1 {
+ // Condition computation...
+ %result = ... : i1 // Last operation must produce i1
+ emitc.yield %result : i1
+ }
+ // Forward expression result
+ emitc.yield %condition : i1
+ } do {
+ // Body region (no terminator required).
+ // Loop body operations...
+ }
+ ```
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.func @while_example() {
+ %counter = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+ %end = emitc.literal "10" : i32
+ %step = 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
+ %condition = emitc.expression : i1 {
+ %current = emitc.load %counter : !emitc.lvalue<i32>
+ %cmp_res = emitc.cmp lt, %current, %end : (i32, i32) -> i1
+ emitc.yield %cmp_res : i1
+ }
+ emitc.yield %condition : 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) -> ()
- }
+ // Print current value
+ %val = emitc.load %counter : !emitc.lvalue<i32>
+ emitc.verbatim "printf(\"%d\\n\", {});" args %val : i32
+ // Increment counter
+ %new_val = emitc.add %val, %step : (i32, i32) -> i32
+ "emitc.assign"(%counter, %new_val) : (!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;
+ void while_example() {
+ int32_t v1 = 0;
+ while (v1 < 10) {
+ int32_t v2 = v1;
+ printf("%d\n", v2);
+ int32_t v3 = v2 + 1;
+ v1 = v3;
}
return;
}
```
}];
- let arguments = (ins);
- let results = (outs);
- let regions = (region MaxSizedRegion<1>:$conditionRegion,
- MaxSizedRegion<1>:$bodyRegion);
+ let results = (outs);
+ let regions = (region SizedRegion<1>:$conditionRegion,
+ SizedRegion<1>:$bodyRegion);
let hasCustomAssemblyFormat = 1;
let hasVerifier = 1;
@@ -1804,54 +1829,81 @@ def EmitC_WhileOp : EmitC_Op<"while",
}
def EmitC_DoOp : EmitC_Op<"do",
- [RecursiveMemoryEffects, NoRegionArguments, OpAsmOpInterface, NoTerminator]> {
+ [NoTerminator, OpAsmOpInterface, RecursiveMemoryEffects]> {
let summary = "Do-while operation";
- let description = [{
+ 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:
+ repeatedly executes a body region 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)
+ The condition is evaluated before each iteration as follows:
+ - The condition region must contain exactly one block with:
+ 1. An `emitc.expression` operation producing an i1 value
+ 2. An `emitc.yield` passing through the expression result
+ - The expression's body contains the actual condition logic
+
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.
+ of the condition. Thus, there is a guarantee that the loop will be executed
+ at least once. The loop terminates when the condition yields false.
+
+ The canonical structure of `emitc.do` is:
+
+ ```mlir
+ emitc.do {
+ // Body region (no terminator required).
+ // Loop body operations...
+ } while {
+ // Condition region (must yield i1)
+ %condition = emitc.expression : i1 {
+ // Condition computation...
+ %result = ... : i1 // Last operation must produce i1
+ emitc.yield %result : i1
+ }
+ // Forward expression result
+ emitc.yield %condition : i1
+ }
+ ```
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.func @do_example() {
+ %counter = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+ %end = emitc.literal "10" : i32
+ %step = 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) -> ()
+ // Print current value
+ %val = emitc.load %counter : !emitc.lvalue<i32>
+ emitc.verbatim "printf(\"%d\\n\", {});" args %val : i32
+
+ // Increment counter
+ %new_val = emitc.add %val, %step : (i32, i32) -> i32
+ "emitc.assign"(%counter, %new_val) : (!emitc.lvalue<i32>, i32) -> ()
} while {
- %var_load = load %var : <i32>
- %res = emitc.cmp le, %var_load, %0 : (i32, i32) -> i1
- emitc.yield %res : i1
+ %condition = emitc.expression : i1 {
+ %current = emitc.load %counter : !emitc.lvalue<i32>
+ %cmp_res = emitc.cmp lt, %current, %end : (i32, i32) -> i1
+ emitc.yield %cmp_res : i1
+ }
+ emitc.yield %condition : i1
}
-
return
}
```
-
```c++
// Code emitted for the operation above.
- void foo(int32_t* v1) {
- int32_t v2 = 0;
+ void do_example() {
+ int32_t v1 = 0;
do {
- printf("%d", *v1);
- int32_t v3 = v2;
- int32_t v4 = v3 + 1;
- v2 = v4;
- } while (v2 <= 10);
+ int32_t v2 = v1;
+ printf("%d\n", v2);
+ int32_t v3 = v2 + 1;
+ v1 = v3;
+ } while (v1 < 10);
return;
}
```
@@ -1859,8 +1911,8 @@ def EmitC_DoOp : EmitC_Op<"do",
let arguments = (ins);
let results = (outs);
- let regions = (region MaxSizedRegion<1>:$bodyRegion,
- MaxSizedRegion<1>:$conditionRegion);
+ let regions = (region SizedRegion<1>:$bodyRegion,
+ SizedRegion<1>:$conditionRegion);
let hasCustomAssemblyFormat = 1;
let hasVerifier = 1;
diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index 6aff0ce98ab91..c98609915b942 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -477,27 +477,34 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
auto loweredWhile = rewriter.create<emitc::WhileOp>(loc);
// Lower before region to condition region.
- rewriter.inlineRegionBefore(whileOp.getBefore(),
- loweredWhile.getConditionRegion(),
- loweredWhile.getConditionRegion().end());
+ Region &condRegion = loweredWhile.getConditionRegion();
+ Block *condBlock = rewriter.createBlock(&condRegion);
+ rewriter.setInsertionPointToStart(condBlock);
- Block *condBlock = &loweredWhile.getConditionRegion().front();
- replaceBlockArgsWithVarLoads(condBlock, vars, rewriter, loc);
+ Type i1Type = IntegerType::get(context, 1);
+ auto exprOp = rewriter.create<emitc::ExpressionOp>(loc, TypeRange{i1Type});
+ Region &exprRegion = exprOp.getBodyRegion();
- Operation *condTerminator =
- loweredWhile.getConditionRegion().back().getTerminator();
- auto condOp = cast<scf::ConditionOp>(condTerminator);
- rewriter.setInsertionPoint(condOp);
+ rewriter.inlineRegionBefore(whileOp.getBefore(), exprRegion,
+ exprRegion.begin());
+
+ Block *exprBlock = &exprRegion.front();
+ replaceBlockArgsWithVarLoads(exprBlock, vars, rewriter, loc);
+
+ auto condOp = cast<scf::ConditionOp>(exprBlock->getTerminator());
Value condition = rewriter.getRemappedValue(condOp.getCondition());
- rewriter.create<emitc::YieldOp>(condOp.getLoc(), condition);
- rewriter.eraseOp(condOp);
+ rewriter.setInsertionPointAfter(condOp);
+ rewriter.replaceOpWithNewOp<emitc::YieldOp>(condOp, condition);
+
+ rewriter.setInsertionPointToEnd(condBlock);
+ rewriter.create<emitc::YieldOp>(loc, exprOp);
// Lower after region to body region.
- rewriter.inlineRegionBefore(whileOp.getAfter(),
- loweredWhile.getBodyRegion(),
- loweredWhile.getBodyRegion().end());
+ Region &bodyRegion = loweredWhile.getBodyRegion();
+ rewriter.inlineRegionBefore(whileOp.getAfter(), bodyRegion,
+ bodyRegion.end());
- Block *bodyBlock = &loweredWhile.getBodyRegion().front();
+ Block *bodyBlock = &bodyRegion.front();
replaceBlockArgsWithVarLoads(bodyBlock, vars, rewriter, loc);
// Convert scf.yield to variable assignments for state updates.
@@ -559,11 +566,20 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
rewriter.eraseOp(condOp);
// Create condition region that loads from the flag variable.
- Block *condBlock = rewriter.createBlock(&loweredDo.getConditionRegion());
+ Region &condRegion = loweredDo.getConditionRegion();
+ Block *condBlock = rewriter.createBlock(&condRegion);
rewriter.setInsertionPointToStart(condBlock);
+
+ auto exprOp = rewriter.create<emitc::ExpressionOp>(loc, TypeRange{i1Type});
+ Block *exprBlock = rewriter.createBlock(&exprOp.getBodyRegion());
+ rewriter.setInsertionPointToStart(exprBlock);
+
Value cond = rewriter.create<emitc::LoadOp>(loc, i1Type, conditionVal);
rewriter.create<emitc::YieldOp>(loc, cond);
+ rewriter.setInsertionPointToEnd(condBlock);
+ rewriter.create<emitc::YieldOp>(loc, exprOp);
+
return success();
}
};
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 9b3ea203cad07..f23d076752d5d 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -1574,37 +1574,50 @@ static Operation *getRootOpFromLoopCondition(Region &condRegion) {
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)
+ if (condBlock.getOperations().size() != 2)
return op.emitOpError(
- "expected condition region to end with emitc.yield, but got ")
- << condBlock.back().getName();
+ "condition region must contain exactly two operations: "
+ "'emitc.expression' followed by 'emitc.yield', but found ")
+ << condBlock.getOperations().size() << " operations";
+
+ Operation &first = condBlock.front();
+ auto exprOp = dyn_cast<emitc::ExpressionOp>(first);
+ if (!exprOp)
+ return op.emitOpError("expected first op in condition region to be "
+ "'emitc.expression', "
+ "but got ")
+ << first.getName();
+
+ if (!exprOp.getResult().getType().isInteger(1))
+ return op.emitOpError("emitc.expression in condition region must return "
+ "'i1', but returns ")
+ << exprOp.getResult().getType();
+
+ Operation &last = *std::next(condBlock.begin());
+ auto condYield = dyn_cast<emitc::YieldOp>(last);
+ if (!condYield)
+ return op.emitOpError("expected last op in condition region to be "
+ "'emitc.yield', but got ")
+ << last.getName();
+
+ if (condYield.getNumOperands() != 1) {
+ return op.emitOpError("expected condition region to return 1 value, but "
+ "it returns ")
+ << condYield.getNumOperands() << " values";
+ }
- if (condYield.getNumOperands() != 1 ||
- !condYield.getOperand(0).getType().isInteger(1))
- return op.emitOpError("condition region must yield a single i1 value");
+ if (condYield.getOperand(0) != exprOp.getResult())
+ return op.emitError("'emitc.yield' must return result of "
+ "'emitc.expression' from this condition region");
- if (body.empty())
+ Block &bodyBlock = body.front();
+ if (bodyBlock.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();
+ if (bodyBlock.mightHaveTerminator())
+ return op.emitOpError("body region must not contain terminator");
return success();
}
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index dcc2bd6e3f07b..787997191bc77 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -179,12 +179,6 @@ 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);
@@ -567,12 +561,51 @@ static LogicalResult printOperation(CppEmitter &emitter,
static LogicalResult printOperation(CppEmitter &emitter,
emitc::WhileOp whileOp) {
- return emitter.emitWhile(whileOp);
+ raw_indented_ostream &os = emitter.ostream();
+
+ os << "while (";
+
+ Block &condBlock = whileOp.getConditionRegion().front();
+ auto condYield = cast<emitc::YieldOp>(condBlock.back());
+ if (failed(emitter.emitExpression(
+ cast<emitc::ExpressionOp>(condYield.getOperand(0).getDefiningOp()))))
+ return failure();
+
+ os << ") {\n";
+ os.indent();
+
+ Block &bodyBlock = whileOp.getBodyRegion().front();
+ for (Operation &op : bodyBlock) {
+ if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/true)))
+ return failure();
+ }
+
+ os.unindent() << "}";
+ return success();
}
-static LogicalResult printOperation(CppEmitter &emitter,
- emitc::DoOp doWhileOp) {
- return emitter.emitDo(doWhileOp);
+static LogicalResult printOperation(CppEmitter &emitter, emitc::DoOp doOp) {
+ raw_indented_ostream &os = emitter.ostream();
+
+ os << "do {\n";
+ os.indent();
+
+ Block &bodyBlock = doOp.getBodyRegion().front();
+ for (Operation &op : bodyBlock) {
+ if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/true)))
+ return failure();
+ }
+
+ os.unindent() << "} while (";
+
+ Block &condBlock = doOp.getConditionRegion().front();
+ auto condYield = cast<emitc::YieldOp>(condBlock.back());
+ if (failed(emitter.emitExpression(
+ cast<emitc::ExpressionOp>(condYield.getOperand(0).getDefiningOp()))))
+ return failure();
+
+ os << ");";
+ return success();
}
static LogicalResult printOperation(CppEmitter &emitter, emitc::CmpOp cmpOp) {
@@ -1532,84 +1565,6 @@ 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();
@@ -1807,14 +1762,14 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
emitc::CallOpaqueOp, emitc::CastOp, emitc::ClassOp,
emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp,
- emitc::DeclareFuncOp, emitc::DivOp, emitc::DoOp, emitc::ExpressionOp,
- emitc::FieldOp, emitc::FileOp, emitc::ForOp, emitc::FuncOp,
- emitc::GetFieldOp, 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>(
+ emitc::DeclareFuncOp, emitc::DivOp, emitc::DoOp,
+ emitc::ExpressionOp, emitc::FieldOp, emitc::FileOp,
+ emitc::ForOp, emitc::FuncOp, emitc::GetFieldOp, 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.
diff --git a/mlir/test/Conversion/SCFToEmitC/while.mlir b/mlir/test/Conversion/SCFToEmitC/while.mlir
index d4d64fac29280..9ded0123e3cca 100644
--- a/mlir/test/Conversion/SCFToEmitC/while.mlir
+++ b/mlir/test/Conversion/SCFToEmitC/while.mlir
@@ -1,12 +1,12 @@
// 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
+emitc.func @payload_while(%arg: i32) -> i32 {
+ %result = add %arg, %arg : (i32, i32) -> i32
return %result : i32
}
-func.func @whileOp() -> i32 {
+func.func @while() -> i32 {
%init = emitc.literal "1.0" : i32
%var = emitc.literal "1.0" : i32
%exit = emitc.literal "10.0" : i32
@@ -17,47 +17,50 @@ func.func @whileOp() -> i32 {
scf.condition(%condition) %arg1 : i32
} do {
^bb0(%arg2: i32):
- %next_arg1 = emitc.call @payload_whileOp(%arg2) : (i32) -> i32
+ %next_arg1 = emitc.call @payload_while(%arg2) : (i32) -> i32
scf.yield %next_arg1 : i32
}
return %res : i32
}
-// CHECK-LABEL: emitc.func @payload_whileOp(
+// CHECK-LABEL: emitc.func @payload_while(
// 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-LABEL: func.func @while() -> 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: %[[VAL_4:.*]] = expression : i1 {
+// 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: yield %[[VAL_7]] : i1
+// CHECK: }
+// CHECK: yield %[[VAL_4]] : 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: %[[VAL_8:.*]] = load %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_9:.*]] = call @payload_while(%[[VAL_8]]) : (i32) -> i32
+// CHECK: assign %[[VAL_9]] : 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: %[[VAL_10:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: %[[VAL_11:.*]] = emitc.load %[[VAL_3]] : <i32>
+// CHECK: emitc.assign %[[VAL_11]] : i32 to %[[VAL_10]] : <i32>
+// CHECK: %[[VAL_12:.*]] = emitc.load %[[VAL_10]] : <i32>
+// CHECK: return %[[VAL_12]] : i32
// CHECK: }
-emitc.func @payload_doOp(%arg: i32) -> i32 {
- %result = emitc.add %arg, %arg : (i32, i32) -> i32
+emitc.func @payload_do(%arg: i32) -> i32 {
+ %result = add %arg, %arg : (i32, i32) -> i32
return %result : i32
}
-func.func @doOp() -> i32 {
+func.func @do() -> i32 {
%init = emitc.literal "1.0" : i32
%var = emitc.literal "1.0" : i32
%exit = emitc.literal "10.0" : i32
@@ -69,19 +72,19 @@ func.func @doOp() -> i32 {
scf.condition(%condition) %next : i32
} do {
^bb0(%arg2: i32):
- %next_arg1 = emitc.call @payload_doOp(%arg2) : (i32) -> i32
+ %next_arg1 = emitc.call @payload_do(%arg2) : (i32) -> i32
scf.yield %next_arg1 : i32
}
return %res : i32
}
-// CHECK-LABEL: emitc.func @payload_doOp(
+// CHECK-LABEL: emitc.func @payload_do(
// 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-LABEL: func.func @do() -> 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
@@ -95,26 +98,29 @@ func.func @doOp() -> i32 {
// 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: %[[VAL_9:.*]] = call @payload_do(%[[VAL_8]]) : (i32) -> i32
// CHECK: assign %[[VAL_9]] : i32 to %[[VAL_3]] : <i32>
// CHECK: }
// CHECK: } while {
-// CHECK: %[[VAL_10:.*]] = load %[[VAL_4]] : <i1>
+// CHECK: %[[VAL_10:.*]] = expression : i1 {
+// CHECK: %[[VAL_11:.*]] = load %[[VAL_4]] : <i1>
+// CHECK: yield %[[VAL_11]] : i1
+// CHECK: }
// 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: %[[VAL_12:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: %[[VAL_13:.*]] = emitc.load %[[VAL_3]] : <i32>
+// CHECK: emitc.assign %[[VAL_13]] : i32 to %[[VAL_12]] : <i32>
+// CHECK: %[[VAL_14:.*]] = emitc.load %[[VAL_12]] : <i32>
+// CHECK: return %[[VAL_14]] : i32
// CHECK: }
-emitc.func @payload_whileOp_two_results(%arg: i32) -> i32 {
- %result = emitc.add %arg, %arg : (i32, i32) -> i32
+emitc.func @payload_while_two_results(%arg: i32) -> i32 {
+ %result = add %arg, %arg : (i32, i32) -> i32
return %result : i32
}
-func.func @whileOp_two_results() -> i32 {
+func.func @while_two_results() -> i32 {
%init = emitc.literal "1.0" : i32
%exit = emitc.literal "10.0" : i32
@@ -124,21 +130,21 @@ func.func @whileOp_two_results() -> i32 {
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
+ %next1 = emitc.call @payload_while_two_results(%arg2_1) : (i32) -> i32
+ %next2 = emitc.call @payload_while_two_results(%arg2_2) : (i32) -> i32
scf.yield %next1, %next2 : i32, i32
}
return %res1 : i32
}
-// CHECK-LABEL: emitc.func @payload_whileOp_two_results(
+// CHECK-LABEL: emitc.func @payload_while_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-LABEL: func.func @while_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>
@@ -146,36 +152,39 @@ func.func @whileOp_two_results() -> 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: %[[VAL_4:.*]] = expression : i1 {
+// 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: yield %[[VAL_8]] : i1
+// CHECK: }
+// CHECK: yield %[[VAL_4]] : 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: %[[VAL_9:.*]] = load %[[VAL_2]] : <i32>
+// CHECK: %[[VAL_10:.*]] = load %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_11:.*]] = call @payload_while_two_results(%[[VAL_9]]) : (i32) -> i32
+// CHECK: %[[VAL_12:.*]] = call @payload_while_two_results(%[[VAL_10]]) : (i32) -> i32
+// CHECK: assign %[[VAL_11]] : i32 to %[[VAL_2]] : <i32>
+// CHECK: assign %[[VAL_12]] : 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: %[[VAL_14:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: %[[VAL_15:.*]] = emitc.load %[[VAL_2]] : <i32>
// CHECK: emitc.assign %[[VAL_15]] : i32 to %[[VAL_13]] : <i32>
-// CHECK: %[[VAL_16:.*]] = emitc.load %[[VAL_12]] : <i32>
+// CHECK: %[[VAL_16:.*]] = emitc.load %[[VAL_3]] : <i32>
+// CHECK: emitc.assign %[[VAL_16]] : i32 to %[[VAL_14]] : <i32>
// CHECK: %[[VAL_17:.*]] = emitc.load %[[VAL_13]] : <i32>
-// CHECK: return %[[VAL_16]] : i32
+// CHECK: %[[VAL_18:.*]] = emitc.load %[[VAL_14]] : <i32>
+// CHECK: return %[[VAL_17]] : i32
// CHECK: }
-emitc.func @payload_doOp_two_results(%arg: i32) -> i32 {
- %result = emitc.add %arg, %arg : (i32, i32) -> i32
+emitc.func @payload_do_two_results(%arg: i32) -> i32 {
+ %result = add %arg, %arg : (i32, i32) -> i32
return %result : i32
}
-func.func @doOp_two_results() -> i32 {
+func.func @do_two_results() -> i32 {
%init = emitc.literal "1.0" : i32
%exit = emitc.literal "10.0" : i32
@@ -185,21 +194,21 @@ func.func @doOp_two_results() -> i32 {
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
+ %next1 = emitc.call @payload_do_two_results(%arg2_1) : (i32) -> i32
+ %next2 = emitc.call @payload_do_two_results(%arg2_2) : (i32) -> i32
scf.yield %next1, %next2 : i32, i32
}
return %res1 : i32
}
-// CHECK-LABEL: emitc.func @payload_doOp_two_results(
+// CHECK-LABEL: emitc.func @payload_do_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-LABEL: func.func @do_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>
@@ -214,22 +223,25 @@ func.func @doOp_two_results() -> 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: %[[VAL_9:.*]] = call @payload_do_two_results(%[[VAL_0]]) : (i32) -> i32
+// CHECK: %[[VAL_10:.*]] = call @payload_do_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: %[[VAL_11:.*]] = expression : i1 {
+// CHECK: %[[VAL_12:.*]] = load %[[VAL_4]] : <i1>
+// CHECK: yield %[[VAL_12]] : i1
+// CHECK: }
// 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: %[[VAL_14:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: %[[VAL_15:.*]] = emitc.load %[[VAL_2]] : <i32>
// CHECK: emitc.assign %[[VAL_15]] : i32 to %[[VAL_13]] : <i32>
-// CHECK: %[[VAL_16:.*]] = emitc.load %[[VAL_12]] : <i32>
+// CHECK: %[[VAL_16:.*]] = emitc.load %[[VAL_3]] : <i32>
+// CHECK: emitc.assign %[[VAL_16]] : i32 to %[[VAL_14]] : <i32>
// CHECK: %[[VAL_17:.*]] = emitc.load %[[VAL_13]] : <i32>
-// CHECK: return %[[VAL_16]] : i32
+// CHECK: %[[VAL_18:.*]] = emitc.load %[[VAL_14]] : <i32>
+// CHECK: return %[[VAL_17]] : i32
// CHECK: }
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index 4644e71a5101e..b8854271feb19 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -733,8 +733,55 @@ emitc.class @testClass {
// -----
func.func @test_while(%arg0 : !emitc.ptr<i32>) {
- // expected-error @+1 {{'emitc.while' op condition region cannot be empty}}
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+
+ // expected-error @+1 {{'emitc.while' op condition region must contain exactly two operations: 'emitc.expression' followed by 'emitc.yield', but found 3 operations}}
+ emitc.while {
+ %r = emitc.expression : i1 {
+ %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ %3 = emitc.literal "3" : i32
+
+ emitc.yield %r : i1
+ } do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ }
+
+ return
+}
+
+// -----
+
+func.func @test_while(%arg0 : !emitc.ptr<i32>) {
+ // expected-error @+1 {{'emitc.while' op expected first op in condition region to be 'emitc.expression', but got emitc.literal}}
+ emitc.while {
+ %true = emitc.literal "true" : i1
+
+ emitc.yield %true : 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 emitc.expression in condition region must return 'i1', but returns 'i32'}}
emitc.while {
+ %r = emitc.expression : i32 {
+ %add = emitc.add %1, %2 : (i32, i32) -> i32
+ emitc.yield %add : i32
+ }
+
+ emitc.yield %r : i32
} do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
}
@@ -744,14 +791,21 @@ func.func @test_while(%arg0 : !emitc.ptr<i32>) {
// -----
-emitc.func @test_while(%arg0 : !emitc.ptr<i32>) {
+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 all operations in condition region must implement CExpression trait, but emitc.literal does not}}
+ // expected-error @+1 {{'emitc.while' op expected last op in condition region to be 'emitc.yield', but got emitc.expression}}
emitc.while {
- %2 = emitc.literal "2" : i32
- %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
- emitc.yield %3 : i1
+ %r1 = emitc.expression : i1 {
+ %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ %r2 = emitc.expression : i32 {
+ %add = emitc.add %1, %2 : (i32, i32) -> i32
+ emitc.yield %add : i32
+ }
} do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
}
@@ -765,9 +819,14 @@ 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}}
+ // expected-error @+1 {{'emitc.while' op expected condition region to return 1 value, but it returns 0 values}}
emitc.while {
- %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ %r = emitc.expression : i1 {
+ %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ emitc.yield
} do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
}
@@ -781,10 +840,16 @@ 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}}
+ %true = emitc.literal "true" : i1
+
+ // expected-error @+1 {{'emitc.yield' must return result of 'emitc.expression' from this condition region}}
emitc.while {
- %3 = emitc.add %1, %2 : (i32, i32) -> i32
- emitc.yield %3 : i32
+ %r = emitc.expression : i1 {
+ %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ emitc.yield %true: i1
} do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
}
@@ -800,9 +865,14 @@ func.func @test_while() {
// 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
+ %r = emitc.expression : i1 {
+ %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ emitc.yield %r: i1
} do {
+ ^bb0:
}
return
@@ -810,18 +880,43 @@ func.func @test_while() {
// -----
-emitc.func @test_while(%arg0 : !emitc.ptr<i32>) {
+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 body region to return 0 values, but body returns 1}}
+ // expected-error @+1 {{'emitc.while' op body region must not contain terminator}}
emitc.while {
- %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
- emitc.yield %3 : i1
+ %r = emitc.expression : i1 {
+ %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ emitc.yield %r: i1
} do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
- %4 = emitc.add %1, %2 : (i32, i32) -> i32
- emitc.yield %4 : i32
+ emitc.yield
+ }
+
+ return
+}
+
+// -----
+
+func.func @test_do(%arg0 : !emitc.ptr<i32>) {
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+
+ // expected-error @+1 {{'emitc.do' op condition region must contain exactly two operations: 'emitc.expression' followed by 'emitc.yield', but found 3 operations}}
+ emitc.do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ } while {
+ %r = emitc.expression : i1 {
+ %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ %3 = emitc.literal "3" : i32
+ emitc.yield %r : i1
}
return
@@ -829,11 +924,13 @@ emitc.func @test_while(%arg0 : !emitc.ptr<i32>) {
// -----
-func.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
- // expected-error @+1 {{'emitc.do' op condition region cannot be empty}}
+func.func @test_do(%arg0 : !emitc.ptr<i32>) {
+ // expected-error @+1 {{'emitc.do' op expected first op in condition region to be 'emitc.expression', but got emitc.literal}}
emitc.do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
} while {
+ %true = emitc.literal "true" : i1
+ emitc.yield %true : i1
}
return
@@ -841,16 +938,20 @@ func.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
// -----
-emitc.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
+func.func @test_do(%arg0 : !emitc.ptr<i32>) {
%1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
- // expected-error @+1 {{'emitc.do' op expected all operations in condition region must implement CExpression trait, but emitc.literal does not}}
+ // expected-error @+1 {{'emitc.do' op emitc.expression in condition region must return 'i1', but returns 'i32'}}
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
+ %r = emitc.expression : i32 {
+ %add = emitc.add %1, %2 : (i32, i32) -> i32
+ emitc.yield %add : i32
+ }
+
+ emitc.yield %r : i32
}
return
@@ -858,15 +959,23 @@ emitc.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
// -----
-func.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
+func.func @test_do(%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}}
+ // expected-error @+1 {{'emitc.do' op expected last op in condition region to be 'emitc.yield', but got emitc.expression}}
emitc.do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
} while {
- %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ %r1 = emitc.expression : i1 {
+ %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ %r2 = emitc.expression : i32 {
+ %add = emitc.add %1, %2 : (i32, i32) -> i32
+ emitc.yield %add : i32
+ }
}
return
@@ -874,16 +983,20 @@ func.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
// -----
-func.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
+func.func @test_do(%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}}
+ // expected-error @+1 {{'emitc.do' op expected condition region to return 1 value, but it returns 0 values}}
emitc.do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
} while {
- %3 = emitc.add %1, %2 : (i32, i32) -> i32
- emitc.yield %3 : i32
+ %r = emitc.expression : i1 {
+ %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ emitc.yield
}
return
@@ -891,15 +1004,44 @@ func.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
// -----
-func.func @test_do_while() {
+func.func @test_do(%arg0 : !emitc.ptr<i32>) {
+ %1 = emitc.literal "1" : i32
+ %2 = emitc.literal "2" : i32
+
+ %true = emitc.literal "true" : i1
+
+ // expected-error @+1 {{'emitc.yield' must return result of 'emitc.expression' from this condition region}}
+ emitc.do {
+ emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ } while {
+ %r = emitc.expression : i1 {
+ %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ emitc.yield %true: i1
+ }
+
+ return
+}
+
+// -----
+
+
+func.func @test_do() {
%1 = emitc.literal "1" : i32
%2 = emitc.literal "2" : i32
// expected-error @+1 {{'emitc.do' op body region cannot be empty}}
emitc.do {
+ ^bb0:
} while {
- %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
- emitc.yield %3 : i1
+ %r = emitc.expression : i1 {
+ %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ emitc.yield %r: i1
}
return
@@ -907,18 +1049,21 @@ func.func @test_do_while() {
// -----
-emitc.func @test_do_while(%arg0 : !emitc.ptr<i32>) {
+func.func @test_do(%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}}
+ // expected-error @+1 {{'emitc.do' op body region must not contain terminator}}
emitc.do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
- %4 = emitc.add %1, %2 : (i32, i32) -> i32
- emitc.yield %4 : i32
+ emitc.yield
} while {
- %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
- emitc.yield %3 : i1
+ %r = emitc.expression : i1 {
+ %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ emitc.yield %r: i1
}
return
diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir
index 6a41c839399f4..90058a41f62d1 100644
--- a/mlir/test/Dialect/EmitC/ops.mlir
+++ b/mlir/test/Dialect/EmitC/ops.mlir
@@ -342,9 +342,13 @@ func.func @while(%arg0 : !emitc.ptr<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
+ %r = emitc.expression : i1 {
+ %add = emitc.add %1, %2 : (i32, i32) -> i32
+ %cmp = emitc.cmp eq, %add, %3 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ emitc.yield %r : i1
} do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
}
@@ -360,9 +364,13 @@ func.func @do(%arg0 : !emitc.ptr<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
+ %r = emitc.expression : i1 {
+ %add = emitc.add %1, %2 : (i32, i32) -> i32
+ %cmp = emitc.cmp eq, %add, %3 : (i32, i32) -> i1
+ emitc.yield %cmp : i1
+ }
+
+ emitc.yield %r : i1
}
return
diff --git a/mlir/test/Target/Cpp/do.mlir b/mlir/test/Target/Cpp/do.mlir
index 034fe77ed22c4..9ffbb38f25239 100644
--- a/mlir/test/Target/Cpp/do.mlir
+++ b/mlir/test/Target/Cpp/do.mlir
@@ -14,18 +14,22 @@
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
+ %0 = literal "10" : i32
+ %1 = literal "1" : i32
- emitc.do {
- emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ do {
+ 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
+ %r = expression : i1 {
+ %var_load = load %var : <i32>
+ %cmp = cmp le, %var_load, %0 : (i32, i32) -> i1
+ yield %cmp : i1
+ }
+
+ yield %r : i1
}
return
@@ -46,23 +50,86 @@ emitc.func @emitc_do(%arg0 : !emitc.ptr<i32>) {
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
+ %0 = literal "10" : i32
+ %1 = literal "1" : i32
- %add = emitc.expression : i32 {
+ %add = expression : i32 {
%add = add %0, %1 : (i32, i32) -> i32
yield %add : i32
}
- emitc.do {
- emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ do {
+ 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
+ %r = expression : i1 {
+ %var_load = load %var : <i32>
+ %cmp = cmp le, %var_load, %add : (i32, i32) -> i1
+ yield %cmp : i1
+ }
+
+ yield %r : i1
+ }
+
+ return
+}
+
+
+// CPP-DEFAULT-LABEL: void emitc_double_do() {
+// CPP-DEFAULT: int32_t v1 = 0;
+// CPP-DEFAULT: int32_t v2 = 0;
+// CPP-DEFAULT: do {
+// CPP-DEFAULT: int32_t v3 = v1;
+// CPP-DEFAULT: do {
+// CPP-DEFAULT: int32_t v4 = v2;
+// CPP-DEFAULT: printf("i = %d, j = %d", v3, v4);
+// CPP-DEFAULT: int32_t v5 = v4 + 1;
+// CPP-DEFAULT: v2 = v5;
+// CPP-DEFAULT: } while (v2 <= 5);
+// CPP-DEFAULT: int32_t v6 = v3 + 1;
+// CPP-DEFAULT: v1 = v6;
+// CPP-DEFAULT: } while (v1 <= 3);
+// CPP-DEFAULT: return;
+// CPP-DEFAULT: }
+
+emitc.func @emitc_double_do() {
+ %var_1 = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+ %var_2 = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+
+ %step = literal "1" : i32
+ %end_1 = literal "3" : i32
+ %end_2 = literal "5" : i32
+
+ do {
+ %var_1_load = load %var_1 : <i32>
+
+ do {
+ %var_2_load = load %var_2 : <i32>
+ verbatim "printf(\"i = %d, j = %d\", {}, {});" args %var_1_load, %var_2_load : i32, i32
+ %tmp_add = add %var_2_load, %step : (i32, i32) -> i32
+ "emitc.assign"(%var_2, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
+ } while {
+ %r = expression : i1 {
+ %var_2_load = load %var_2 : <i32>
+ %cmp = cmp le, %var_2_load, %end_2 : (i32, i32) -> i1
+ yield %cmp : i1
+ }
+
+ yield %r : i1
+ }
+
+ %tmp_add = add %var_1_load, %step : (i32, i32) -> i32
+ "emitc.assign"(%var_1, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
+ } while {
+ %r = expression : i1 {
+ %var_1_load = load %var_1 : <i32>
+ %cmp = cmp le, %var_1_load, %end_1 : (i32, i32) -> i1
+ yield %cmp : i1
+ }
+
+ yield %r : i1
}
return
diff --git a/mlir/test/Target/Cpp/while.mlir b/mlir/test/Target/Cpp/while.mlir
index 76cf7c2f752cf..8770550f3ad4d 100644
--- a/mlir/test/Target/Cpp/while.mlir
+++ b/mlir/test/Target/Cpp/while.mlir
@@ -14,15 +14,19 @@
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
+ %0 = literal "10" : i32
+ %1 = literal "1" : i32
- emitc.while {
- %var_load = load %var : <i32>
- %res = emitc.cmp le, %var_load, %0 : (i32, i32) -> i1
- emitc.yield %res : i1
+ while {
+ %r = expression : i1 {
+ %var_load = load %var : <i32>
+ %cmp = cmp le, %var_load, %0 : (i32, i32) -> i1
+ yield %cmp : i1
+ }
+
+ yield %r : i1
} do {
- emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ 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) -> ()
@@ -46,20 +50,24 @@ emitc.func @emitc_while(%arg0 : !emitc.ptr<i32>) {
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
+ %0 = literal "10" : i32
+ %1 = literal "1" : i32
- %add = emitc.expression : i32 {
+ %add = 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
+ while {
+ %r = expression : i1 {
+ %var_load = load %var : <i32>
+ %cmp = cmp le, %var_load, %add : (i32, i32) -> i1
+ yield %cmp : i1
+ }
+
+ yield %r : i1
} do {
- emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
+ 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) -> ()
@@ -67,3 +75,62 @@ emitc.func @emitc_while_with_expression(%arg0 : !emitc.ptr<i32>) {
return
}
+
+
+// CPP-DEFAULT-LABEL: void emitc_double_while() {
+// CPP-DEFAULT: int32_t v1 = 0;
+// CPP-DEFAULT: int32_t v2 = 0;
+// CPP-DEFAULT: while (v1 <= 3) {
+// CPP-DEFAULT: int32_t v3 = v1;
+// CPP-DEFAULT: while (v2 <= 5) {
+// CPP-DEFAULT: int32_t v4 = v2;
+// CPP-DEFAULT: printf("i = %d, j = %d", v3, v4);
+// CPP-DEFAULT: int32_t v5 = v4 + 1;
+// CPP-DEFAULT: v2 = v5;
+// CPP-DEFAULT: }
+// CPP-DEFAULT: int32_t v6 = v3 + 1;
+// CPP-DEFAULT: v1 = v6;
+// CPP-DEFAULT: }
+// CPP-DEFAULT: return;
+// CPP-DEFAULT: }
+
+emitc.func @emitc_double_while() {
+ %var_1 = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+ %var_2 = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+
+ %step = literal "1" : i32
+ %end_1 = literal "3" : i32
+ %end_2 = literal "5" : i32
+
+ while {
+ %r = expression : i1 {
+ %var_1_load = load %var_1 : <i32>
+ %cmp = cmp le, %var_1_load, %end_1 : (i32, i32) -> i1
+ yield %cmp : i1
+ }
+
+ yield %r : i1
+ } do {
+ %var_1_load = load %var_1 : <i32>
+
+ while {
+ %r = expression : i1 {
+ %var_2_load = load %var_2 : <i32>
+ %cmp = cmp le, %var_2_load, %end_2 : (i32, i32) -> i1
+ yield %cmp : i1
+ }
+
+ yield %r : i1
+ } do {
+ %var_2_load = load %var_2 : <i32>
+ verbatim "printf(\"i = %d, j = %d\", {}, {});" args %var_1_load, %var_2_load : i32, i32
+ %tmp_add = add %var_2_load, %step : (i32, i32) -> i32
+ "emitc.assign"(%var_2, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
+ }
+
+ %tmp_add = add %var_1_load, %step : (i32, i32) -> i32
+ "emitc.assign"(%var_1, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
+ }
+
+ return
+}
>From 046b038d4385eb050709eb63176a6f1c470296c2 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Tue, 19 Aug 2025 16:30:28 +0300
Subject: [PATCH 03/13] [mlir][emitc] Changes after review
---
mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 2 +-
mlir/lib/Target/Cpp/TranslateToCpp.cpp | 16 ++++++++--------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index f23d076752d5d..aa7f64b923715 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -1595,7 +1595,7 @@ static LogicalResult verifyLoopRegions(Operation &op, Region &condition,
"'i1', but returns ")
<< exprOp.getResult().getType();
- Operation &last = *std::next(condBlock.begin());
+ Operation &last = condBlock.back();
auto condYield = dyn_cast<emitc::YieldOp>(last);
if (!condYield)
return op.emitOpError("expected last op in condition region to be "
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 787997191bc77..8c80331d03be9 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -260,7 +260,7 @@ struct CppEmitter {
}
/// Get expression currently being emitted.
- Operation *getEmittedExpression() { return emittedExpression; }
+ ExpressionOp getEmittedExpression() { return emittedExpression; }
/// Determine whether given value is part of the expression potentially being
/// emitted.
@@ -324,7 +324,7 @@ struct CppEmitter {
unsigned int valueCount{0};
/// State of the current expression being emitted.
- Operation *emittedExpression = nullptr;
+ ExpressionOp emittedExpression;
SmallVector<int> emittedExpressionPrecedence;
void pushExpressionPrecedence(int precedence) {
@@ -1764,12 +1764,12 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp,
emitc::DeclareFuncOp, emitc::DivOp, emitc::DoOp,
emitc::ExpressionOp, emitc::FieldOp, emitc::FileOp,
- emitc::ForOp, emitc::FuncOp, emitc::GetFieldOp, 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>(
+ 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.
>From 248500d7327a94e50fb6ea1f7f7539c3802ef908 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Tue, 26 Aug 2025 20:03:53 +0300
Subject: [PATCH 04/13] [mlir][emitc] Changes after review
Deleted lovering from scf.while to emitc.while. Minor code style
changes have been made.
---
mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp | 19 +-
mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 8 +-
mlir/test/Conversion/SCFToEmitC/while.mlir | 190 +++++++-----------
3 files changed, 79 insertions(+), 138 deletions(-)
diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index c98609915b942..5661d19452322 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -350,25 +350,10 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
// 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))) {
+ 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))
+ if (failed(lowerDoWhile(whileOp, variables, context, rewriter, loc)))
return failure();
// Create an emitc::variable op for each result. These variables will be
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index aa7f64b923715..dc1326a1cdbc3 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -1602,11 +1602,10 @@ static LogicalResult verifyLoopRegions(Operation &op, Region &condition,
"'emitc.yield', but got ")
<< last.getName();
- if (condYield.getNumOperands() != 1) {
+ if (condYield.getNumOperands() != 1)
return op.emitOpError("expected condition region to return 1 value, but "
"it returns ")
<< condYield.getNumOperands() << " values";
- }
if (condYield.getOperand(0) != exprOp.getResult())
return op.emitError("'emitc.yield' must return result of "
@@ -1636,9 +1635,8 @@ static ParseResult parseLoop(OpAsmParser &parser, OperationState &res,
Region *firstRegion = res.addRegion();
Region *secondRegion = res.addRegion();
- if (parser.parseRegion(*firstRegion))
- return failure();
- if (parser.parseKeyword(midKeyword) || parser.parseRegion(*secondRegion))
+ if (parser.parseRegion(*firstRegion) || parser.parseKeyword(midKeyword) ||
+ parser.parseRegion(*secondRegion))
return failure();
return parser.parseOptionalAttrDictWithKeyword(res.attributes);
diff --git a/mlir/test/Conversion/SCFToEmitC/while.mlir b/mlir/test/Conversion/SCFToEmitC/while.mlir
index 9ded0123e3cca..59a4c20387fba 100644
--- a/mlir/test/Conversion/SCFToEmitC/while.mlir
+++ b/mlir/test/Conversion/SCFToEmitC/while.mlir
@@ -1,66 +1,12 @@
// 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_while(%arg: i32) -> i32 {
+emitc.func @payload_one_result(%arg: i32) -> i32 {
%result = add %arg, %arg : (i32, i32) -> i32
return %result : i32
}
-func.func @while() -> 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_while(%arg2) : (i32) -> i32
- scf.yield %next_arg1 : i32
- }
-
- return %res : i32
-}
-// CHECK-LABEL: emitc.func @payload_while(
-// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
-// CHECK: %[[VAL_0:.*]] = add %[[ARG0]], %[[ARG0]] : (i32, i32) -> i32
-// CHECK: return %[[VAL_0]] : i32
-// CHECK: }
-
-// CHECK-LABEL: func.func @while() -> 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:.*]] = expression : i1 {
-// 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: yield %[[VAL_7]] : i1
-// CHECK: }
-// CHECK: yield %[[VAL_4]] : i1
-// CHECK: } do {
-// CHECK: %[[VAL_8:.*]] = load %[[VAL_3]] : <i32>
-// CHECK: %[[VAL_9:.*]] = call @payload_while(%[[VAL_8]]) : (i32) -> i32
-// CHECK: assign %[[VAL_9]] : i32 to %[[VAL_3]] : <i32>
-// CHECK: }
-// CHECK: %[[VAL_10:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
-// CHECK: %[[VAL_11:.*]] = emitc.load %[[VAL_3]] : <i32>
-// CHECK: emitc.assign %[[VAL_11]] : i32 to %[[VAL_10]] : <i32>
-// CHECK: %[[VAL_12:.*]] = emitc.load %[[VAL_10]] : <i32>
-// CHECK: return %[[VAL_12]] : i32
-// CHECK: }
-
-emitc.func @payload_do(%arg: i32) -> i32 {
- %result = add %arg, %arg : (i32, i32) -> i32
- return %result : i32
-}
-
-func.func @do() -> i32 {
+func.func @one_result() -> i32 {
%init = emitc.literal "1.0" : i32
%var = emitc.literal "1.0" : i32
%exit = emitc.literal "10.0" : i32
@@ -72,19 +18,19 @@ func.func @do() -> i32 {
scf.condition(%condition) %next : i32
} do {
^bb0(%arg2: i32):
- %next_arg1 = emitc.call @payload_do(%arg2) : (i32) -> i32
+ %next_arg1 = emitc.call @payload_one_result(%arg2) : (i32) -> i32
scf.yield %next_arg1 : i32
}
return %res : i32
}
-// CHECK-LABEL: emitc.func @payload_do(
+// CHECK-LABEL: emitc.func @payload_one_result(
// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
// CHECK: %[[VAL_0:.*]] = add %[[ARG0]], %[[ARG0]] : (i32, i32) -> i32
// CHECK: return %[[VAL_0]] : i32
// CHECK: }
-// CHECK-LABEL: func.func @do() -> i32 {
+// CHECK-LABEL: func.func @one_result() -> 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
@@ -98,7 +44,7 @@ func.func @do() -> i32 {
// 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_do(%[[VAL_8]]) : (i32) -> i32
+// CHECK: %[[VAL_9:.*]] = call @payload_one_result(%[[VAL_8]]) : (i32) -> i32
// CHECK: assign %[[VAL_9]] : i32 to %[[VAL_3]] : <i32>
// CHECK: }
// CHECK: } while {
@@ -115,58 +61,61 @@ func.func @do() -> i32 {
// CHECK: return %[[VAL_14]] : i32
// CHECK: }
-emitc.func @payload_while_two_results(%arg: i32) -> i32 {
+emitc.func @payload_two_results(%arg: i32) -> i32 {
%result = add %arg, %arg : (i32, i32) -> i32
return %result : i32
}
-func.func @while_two_results() -> i32 {
+func.func @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
+ scf.condition(%condition) %init, %arg1_2 : i32, i32
} do {
^bb0(%arg2_1 : i32, %arg2_2 : i32):
- %next1 = emitc.call @payload_while_two_results(%arg2_1) : (i32) -> i32
- %next2 = emitc.call @payload_while_two_results(%arg2_2) : (i32) -> i32
+ %next1 = emitc.call @payload_two_results(%arg2_1) : (i32) -> i32
+ %next2 = emitc.call @payload_two_results(%arg2_2) : (i32) -> i32
scf.yield %next1, %next2 : i32, i32
}
return %res1 : i32
}
-// CHECK-LABEL: emitc.func @payload_while_two_results(
+// CHECK-LABEL: emitc.func @payload_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 @while_two_results() -> i32 {
+// CHECK-LABEL: func.func @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:.*]] = expression : i1 {
-// 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: yield %[[VAL_8]] : i1
+// 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_two_results(%[[VAL_0]]) : (i32) -> i32
+// CHECK: %[[VAL_10:.*]] = call @payload_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: yield %[[VAL_4]] : i1
-// CHECK: } do {
-// CHECK: %[[VAL_9:.*]] = load %[[VAL_2]] : <i32>
-// CHECK: %[[VAL_10:.*]] = load %[[VAL_3]] : <i32>
-// CHECK: %[[VAL_11:.*]] = call @payload_while_two_results(%[[VAL_9]]) : (i32) -> i32
-// CHECK: %[[VAL_12:.*]] = call @payload_while_two_results(%[[VAL_10]]) : (i32) -> i32
-// CHECK: assign %[[VAL_11]] : i32 to %[[VAL_2]] : <i32>
-// CHECK: assign %[[VAL_12]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: } while {
+// CHECK: %[[VAL_11:.*]] = expression : i1 {
+// CHECK: %[[VAL_12:.*]] = load %[[VAL_4]] : <i1>
+// CHECK: yield %[[VAL_12]] : i1
+// CHECK: }
+// CHECK: yield %[[VAL_11]] : i1
// CHECK: }
// CHECK: %[[VAL_13:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
// CHECK: %[[VAL_14:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
@@ -179,53 +128,66 @@ func.func @while_two_results() -> i32 {
// CHECK: return %[[VAL_17]] : i32
// CHECK: }
-emitc.func @payload_do_two_results(%arg: i32) -> i32 {
+emitc.func @payload_double_use(%arg: i32) -> i32 {
%result = add %arg, %arg : (i32, i32) -> i32
return %result : i32
}
-func.func @do_two_results() -> i32 {
+emitc.func @foo_with_side_effect(%arg: i32, %p : !emitc.ptr<i32>) -> i32 {
+ %sum = add %arg, %arg : (i32, i32) -> i32
+ emitc.verbatim "{}[0] = {};" args %p, %sum : !emitc.ptr<i32>, i32
+ return %sum : i32
+}
+
+func.func @double_use(%p : !emitc.ptr<i32>) -> i32 {
%init = emitc.literal "1.0" : i32
+ %var = 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
+ %res = scf.while (%arg1 = %init) : (i32) -> i32 {
+ %used_twice = emitc.call @foo_with_side_effect(%arg1, %p) : (i32, !emitc.ptr<i32>) -> i32
+ %prod = emitc.add %used_twice, %used_twice : (i32, i32) -> i32
+ %sum = emitc.add %arg1, %prod : (i32, i32) -> i32
%condition = emitc.cmp lt, %sum, %exit : (i32, i32) -> i1
- scf.condition(%condition) %init, %arg1_2 : i32, i32
+ scf.condition(%condition) %arg1 : i32
} do {
- ^bb0(%arg2_1 : i32, %arg2_2 : i32):
- %next1 = emitc.call @payload_do_two_results(%arg2_1) : (i32) -> i32
- %next2 = emitc.call @payload_do_two_results(%arg2_2) : (i32) -> i32
- scf.yield %next1, %next2 : i32, i32
+ ^bb0(%arg2: i32):
+ %next_arg1 = emitc.call @payload_double_use(%arg2) : (i32) -> i32
+ scf.yield %next_arg1 : i32
}
-
- return %res1 : i32
+ return %res : i32
}
-// CHECK-LABEL: emitc.func @payload_do_two_results(
+// CHECK-LABEL: emitc.func @payload_double_use(
// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
// CHECK: %[[VAL_0:.*]] = add %[[ARG0]], %[[ARG0]] : (i32, i32) -> i32
// CHECK: return %[[VAL_0]] : i32
// CHECK: }
-// CHECK-LABEL: func.func @do_two_results() -> i32 {
+// CHECK-LABEL: emitc.func @foo_with_side_effect(
+// CHECK-SAME: %[[ARG0:.*]]: i32,
+// CHECK-SAME: %[[ARG1:.*]]: !emitc.ptr<i32>) -> i32 {
+// CHECK: %[[VAL_0:.*]] = add %[[ARG0]], %[[ARG0]] : (i32, i32) -> i32
+// CHECK: verbatim "{}[0] = {};" args %[[ARG1]], %[[VAL_0]] : !emitc.ptr<i32>, i32
+// CHECK: return %[[VAL_0]] : i32
+// CHECK: }
+
+// CHECK-LABEL: func.func @double_use(
+// CHECK-SAME: %[[ARG0:.*]]: !emitc.ptr<i32>) -> 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_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_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_do_two_results(%[[VAL_0]]) : (i32) -> i32
-// CHECK: %[[VAL_10:.*]] = call @payload_do_two_results(%[[VAL_6]]) : (i32) -> i32
-// CHECK: assign %[[VAL_9]] : i32 to %[[VAL_2]] : <i32>
+// CHECK: %[[VAL_5:.*]] = load %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_6:.*]] = call @foo_with_side_effect(%[[VAL_5]], %[[ARG0]]) : (i32, !emitc.ptr<i32>) -> i32
+// CHECK: %[[VAL_7:.*]] = add %[[VAL_6]], %[[VAL_6]] : (i32, i32) -> i32
+// CHECK: %[[VAL_8:.*]] = add %[[VAL_5]], %[[VAL_7]] : (i32, i32) -> i32
+// CHECK: %[[VAL_9:.*]] = cmp lt, %[[VAL_8]], %[[VAL_2]] : (i32, i32) -> i1
+// CHECK: assign %[[VAL_9]] : i1 to %[[VAL_4]] : <i1>
+// CHECK: if %[[VAL_9]] {
+// CHECK: %[[VAL_10:.*]] = call @payload_double_use(%[[VAL_5]]) : (i32) -> i32
// CHECK: assign %[[VAL_10]] : i32 to %[[VAL_3]] : <i32>
// CHECK: }
// CHECK: } while {
@@ -236,12 +198,8 @@ func.func @do_two_results() -> i32 {
// CHECK: yield %[[VAL_11]] : i1
// CHECK: }
// CHECK: %[[VAL_13:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
-// CHECK: %[[VAL_14:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
-// CHECK: %[[VAL_15:.*]] = emitc.load %[[VAL_2]] : <i32>
-// CHECK: emitc.assign %[[VAL_15]] : i32 to %[[VAL_13]] : <i32>
-// CHECK: %[[VAL_16:.*]] = emitc.load %[[VAL_3]] : <i32>
-// CHECK: emitc.assign %[[VAL_16]] : i32 to %[[VAL_14]] : <i32>
-// CHECK: %[[VAL_17:.*]] = emitc.load %[[VAL_13]] : <i32>
-// CHECK: %[[VAL_18:.*]] = emitc.load %[[VAL_14]] : <i32>
-// CHECK: return %[[VAL_17]] : i32
+// CHECK: %[[VAL_14:.*]] = emitc.load %[[VAL_3]] : <i32>
+// CHECK: emitc.assign %[[VAL_14]] : i32 to %[[VAL_13]] : <i32>
+// CHECK: %[[VAL_15:.*]] = emitc.load %[[VAL_13]] : <i32>
+// CHECK: return %[[VAL_15]] : i32
// CHECK: }
>From 8b96e3258ad89d1f9285e28fea4b9ac51b50ebc1 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Wed, 3 Sep 2025 20:23:21 +0300
Subject: [PATCH 05/13] [mlir][emitc] Changes after updating ExpressionOp
---
mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp | 53 +++----------------
mlir/test/Conversion/SCFToEmitC/while.mlir | 6 +--
mlir/test/Dialect/EmitC/invalid_ops.mlir | 32 +++++------
mlir/test/Dialect/EmitC/ops.mlir | 4 +-
mlir/test/Target/Cpp/do.mlir | 10 ++--
mlir/test/Target/Cpp/while.mlir | 10 ++--
6 files changed, 37 insertions(+), 78 deletions(-)
diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index 5661d19452322..07df270f8221f 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -453,51 +453,6 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
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.
- Region &condRegion = loweredWhile.getConditionRegion();
- Block *condBlock = rewriter.createBlock(&condRegion);
- rewriter.setInsertionPointToStart(condBlock);
-
- Type i1Type = IntegerType::get(context, 1);
- auto exprOp = rewriter.create<emitc::ExpressionOp>(loc, TypeRange{i1Type});
- Region &exprRegion = exprOp.getBodyRegion();
-
- rewriter.inlineRegionBefore(whileOp.getBefore(), exprRegion,
- exprRegion.begin());
-
- Block *exprBlock = &exprRegion.front();
- replaceBlockArgsWithVarLoads(exprBlock, vars, rewriter, loc);
-
- auto condOp = cast<scf::ConditionOp>(exprBlock->getTerminator());
- Value condition = rewriter.getRemappedValue(condOp.getCondition());
- rewriter.setInsertionPointAfter(condOp);
- rewriter.replaceOpWithNewOp<emitc::YieldOp>(condOp, condition);
-
- rewriter.setInsertionPointToEnd(condBlock);
- rewriter.create<emitc::YieldOp>(loc, exprOp);
-
- // Lower after region to body region.
- Region &bodyRegion = loweredWhile.getBodyRegion();
- rewriter.inlineRegionBefore(whileOp.getAfter(), bodyRegion,
- bodyRegion.end());
-
- Block *bodyBlock = &bodyRegion.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,
@@ -555,11 +510,15 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
Block *condBlock = rewriter.createBlock(&condRegion);
rewriter.setInsertionPointToStart(condBlock);
- auto exprOp = rewriter.create<emitc::ExpressionOp>(loc, TypeRange{i1Type});
+ auto exprOp = rewriter.create<emitc::ExpressionOp>(
+ loc, i1Type, conditionVal, /*do_not_inline=*/false);
Block *exprBlock = rewriter.createBlock(&exprOp.getBodyRegion());
+
+ exprBlock->addArgument(conditionVal.getType(), loc);
rewriter.setInsertionPointToStart(exprBlock);
- Value cond = rewriter.create<emitc::LoadOp>(loc, i1Type, conditionVal);
+ Value cond =
+ rewriter.create<emitc::LoadOp>(loc, i1Type, exprBlock->getArgument(0));
rewriter.create<emitc::YieldOp>(loc, cond);
rewriter.setInsertionPointToEnd(condBlock);
diff --git a/mlir/test/Conversion/SCFToEmitC/while.mlir b/mlir/test/Conversion/SCFToEmitC/while.mlir
index 59a4c20387fba..c2a446e4f8017 100644
--- a/mlir/test/Conversion/SCFToEmitC/while.mlir
+++ b/mlir/test/Conversion/SCFToEmitC/while.mlir
@@ -48,7 +48,7 @@ func.func @one_result() -> i32 {
// CHECK: assign %[[VAL_9]] : i32 to %[[VAL_3]] : <i32>
// CHECK: }
// CHECK: } while {
-// CHECK: %[[VAL_10:.*]] = expression : i1 {
+// CHECK: %[[VAL_10:.*]] = expression %[[VAL_4]] : (!emitc.lvalue<i1>) -> i1 {
// CHECK: %[[VAL_11:.*]] = load %[[VAL_4]] : <i1>
// CHECK: yield %[[VAL_11]] : i1
// CHECK: }
@@ -111,7 +111,7 @@ func.func @two_results() -> i32 {
// CHECK: assign %[[VAL_10]] : i32 to %[[VAL_3]] : <i32>
// CHECK: }
// CHECK: } while {
-// CHECK: %[[VAL_11:.*]] = expression : i1 {
+// CHECK: %[[VAL_11:.*]] = expression %[[VAL_4]] : (!emitc.lvalue<i1>) -> i1 {
// CHECK: %[[VAL_12:.*]] = load %[[VAL_4]] : <i1>
// CHECK: yield %[[VAL_12]] : i1
// CHECK: }
@@ -191,7 +191,7 @@ func.func @double_use(%p : !emitc.ptr<i32>) -> i32 {
// CHECK: assign %[[VAL_10]] : i32 to %[[VAL_3]] : <i32>
// CHECK: }
// CHECK: } while {
-// CHECK: %[[VAL_11:.*]] = expression : i1 {
+// CHECK: %[[VAL_11:.*]] = expression %[[VAL_4]] : (!emitc.lvalue<i1>) -> i1 {
// CHECK: %[[VAL_12:.*]] = load %[[VAL_4]] : <i1>
// CHECK: yield %[[VAL_12]] : i1
// CHECK: }
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index b8854271feb19..dc7eb343aa2d7 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -738,7 +738,7 @@ func.func @test_while(%arg0 : !emitc.ptr<i32>) {
// expected-error @+1 {{'emitc.while' op condition region must contain exactly two operations: 'emitc.expression' followed by 'emitc.yield', but found 3 operations}}
emitc.while {
- %r = emitc.expression : i1 {
+ %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
%cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
emitc.yield %cmp : i1
}
@@ -776,7 +776,7 @@ func.func @test_while(%arg0 : !emitc.ptr<i32>) {
// expected-error @+1 {{'emitc.while' op emitc.expression in condition region must return 'i1', but returns 'i32'}}
emitc.while {
- %r = emitc.expression : i32 {
+ %r = emitc.expression %1, %2 : (i32, i32) -> i32 {
%add = emitc.add %1, %2 : (i32, i32) -> i32
emitc.yield %add : i32
}
@@ -797,12 +797,12 @@ func.func @test_while(%arg0 : !emitc.ptr<i32>) {
// expected-error @+1 {{'emitc.while' op expected last op in condition region to be 'emitc.yield', but got emitc.expression}}
emitc.while {
- %r1 = emitc.expression : i1 {
+ %r1 = emitc.expression %1, %2 : (i32, i32) -> i1 {
%cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
emitc.yield %cmp : i1
}
- %r2 = emitc.expression : i32 {
+ %r2 = emitc.expression %1, %2 : (i32, i32) -> i32 {
%add = emitc.add %1, %2 : (i32, i32) -> i32
emitc.yield %add : i32
}
@@ -821,7 +821,7 @@ func.func @test_while(%arg0 : !emitc.ptr<i32>) {
// expected-error @+1 {{'emitc.while' op expected condition region to return 1 value, but it returns 0 values}}
emitc.while {
- %r = emitc.expression : i1 {
+ %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
%cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
emitc.yield %cmp : i1
}
@@ -844,7 +844,7 @@ func.func @test_while(%arg0 : !emitc.ptr<i32>) {
// expected-error @+1 {{'emitc.yield' must return result of 'emitc.expression' from this condition region}}
emitc.while {
- %r = emitc.expression : i1 {
+ %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
%cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
emitc.yield %cmp : i1
}
@@ -865,7 +865,7 @@ func.func @test_while() {
// expected-error @+1 {{'emitc.while' op body region cannot be empty}}
emitc.while {
- %r = emitc.expression : i1 {
+ %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
%cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
emitc.yield %cmp : i1
}
@@ -886,7 +886,7 @@ func.func @test_while(%arg0 : !emitc.ptr<i32>) {
// expected-error @+1 {{'emitc.while' op body region must not contain terminator}}
emitc.while {
- %r = emitc.expression : i1 {
+ %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
%cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
emitc.yield %cmp : i1
}
@@ -910,7 +910,7 @@ func.func @test_do(%arg0 : !emitc.ptr<i32>) {
emitc.do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
} while {
- %r = emitc.expression : i1 {
+ %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
%cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
emitc.yield %cmp : i1
}
@@ -946,7 +946,7 @@ func.func @test_do(%arg0 : !emitc.ptr<i32>) {
emitc.do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
} while {
- %r = emitc.expression : i32 {
+ %r = emitc.expression %1, %2 : (i32, i32) -> i32 {
%add = emitc.add %1, %2 : (i32, i32) -> i32
emitc.yield %add : i32
}
@@ -967,12 +967,12 @@ func.func @test_do(%arg0 : !emitc.ptr<i32>) {
emitc.do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
} while {
- %r1 = emitc.expression : i1 {
+ %r1 = emitc.expression %1, %2 : (i32, i32) -> i1 {
%cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
emitc.yield %cmp : i1
}
- %r2 = emitc.expression : i32 {
+ %r2 = emitc.expression %1, %2 : (i32, i32) -> i32 {
%add = emitc.add %1, %2 : (i32, i32) -> i32
emitc.yield %add : i32
}
@@ -991,7 +991,7 @@ func.func @test_do(%arg0 : !emitc.ptr<i32>) {
emitc.do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
} while {
- %r = emitc.expression : i1 {
+ %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
%cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
emitc.yield %cmp : i1
}
@@ -1014,7 +1014,7 @@ func.func @test_do(%arg0 : !emitc.ptr<i32>) {
emitc.do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
} while {
- %r = emitc.expression : i1 {
+ %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
%cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
emitc.yield %cmp : i1
}
@@ -1036,7 +1036,7 @@ func.func @test_do() {
emitc.do {
^bb0:
} while {
- %r = emitc.expression : i1 {
+ %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
%cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
emitc.yield %cmp : i1
}
@@ -1058,7 +1058,7 @@ func.func @test_do(%arg0 : !emitc.ptr<i32>) {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
emitc.yield
} while {
- %r = emitc.expression : i1 {
+ %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
%cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
emitc.yield %cmp : i1
}
diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir
index 90058a41f62d1..d85d725f7b20b 100644
--- a/mlir/test/Dialect/EmitC/ops.mlir
+++ b/mlir/test/Dialect/EmitC/ops.mlir
@@ -342,7 +342,7 @@ func.func @while(%arg0 : !emitc.ptr<i32>) {
%3 = emitc.literal "3" : i32
emitc.while {
- %r = emitc.expression : i1 {
+ %r = emitc.expression %1, %2, %3 : (i32, i32, i32) -> i1 {
%add = emitc.add %1, %2 : (i32, i32) -> i32
%cmp = emitc.cmp eq, %add, %3 : (i32, i32) -> i1
emitc.yield %cmp : i1
@@ -364,7 +364,7 @@ func.func @do(%arg0 : !emitc.ptr<i32>) {
emitc.do {
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
} while {
- %r = emitc.expression : i1 {
+ %r = emitc.expression %1, %2, %3 : (i32, i32, i32) -> i1 {
%add = emitc.add %1, %2 : (i32, i32) -> i32
%cmp = emitc.cmp eq, %add, %3 : (i32, i32) -> i1
emitc.yield %cmp : i1
diff --git a/mlir/test/Target/Cpp/do.mlir b/mlir/test/Target/Cpp/do.mlir
index 9ffbb38f25239..a9b8ceddcf3a4 100644
--- a/mlir/test/Target/Cpp/do.mlir
+++ b/mlir/test/Target/Cpp/do.mlir
@@ -23,7 +23,7 @@ emitc.func @emitc_do(%arg0 : !emitc.ptr<i32>) {
%tmp_add = add %var_load, %1 : (i32, i32) -> i32
"emitc.assign"(%var, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
} while {
- %r = expression : i1 {
+ %r = expression %var, %0 : (!emitc.lvalue<i32>, i32) -> i1 {
%var_load = load %var : <i32>
%cmp = cmp le, %var_load, %0 : (i32, i32) -> i1
yield %cmp : i1
@@ -53,7 +53,7 @@ emitc.func @emitc_do_with_expression(%arg0 : !emitc.ptr<i32>) {
%0 = literal "10" : i32
%1 = literal "1" : i32
- %add = expression : i32 {
+ %add = expression %0, %1 : (i32, i32) -> i32 {
%add = add %0, %1 : (i32, i32) -> i32
yield %add : i32
}
@@ -64,7 +64,7 @@ emitc.func @emitc_do_with_expression(%arg0 : !emitc.ptr<i32>) {
%tmp_add = add %var_load, %1 : (i32, i32) -> i32
"emitc.assign"(%var, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
} while {
- %r = expression : i1 {
+ %r = expression %var, %add : (!emitc.lvalue<i32>, i32) -> i1 {
%var_load = load %var : <i32>
%cmp = cmp le, %var_load, %add : (i32, i32) -> i1
yield %cmp : i1
@@ -111,7 +111,7 @@ emitc.func @emitc_double_do() {
%tmp_add = add %var_2_load, %step : (i32, i32) -> i32
"emitc.assign"(%var_2, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
} while {
- %r = expression : i1 {
+ %r = expression %var_2, %end_2 : (!emitc.lvalue<i32>, i32) -> i1 {
%var_2_load = load %var_2 : <i32>
%cmp = cmp le, %var_2_load, %end_2 : (i32, i32) -> i1
yield %cmp : i1
@@ -123,7 +123,7 @@ emitc.func @emitc_double_do() {
%tmp_add = add %var_1_load, %step : (i32, i32) -> i32
"emitc.assign"(%var_1, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
} while {
- %r = expression : i1 {
+ %r = expression %var_1, %end_1 : (!emitc.lvalue<i32>, i32) -> i1 {
%var_1_load = load %var_1 : <i32>
%cmp = cmp le, %var_1_load, %end_1 : (i32, i32) -> i1
yield %cmp : i1
diff --git a/mlir/test/Target/Cpp/while.mlir b/mlir/test/Target/Cpp/while.mlir
index 8770550f3ad4d..cff154161ab40 100644
--- a/mlir/test/Target/Cpp/while.mlir
+++ b/mlir/test/Target/Cpp/while.mlir
@@ -18,7 +18,7 @@ emitc.func @emitc_while(%arg0 : !emitc.ptr<i32>) {
%1 = literal "1" : i32
while {
- %r = expression : i1 {
+ %r = expression %var, %0 : (!emitc.lvalue<i32>, i32) -> i1 {
%var_load = load %var : <i32>
%cmp = cmp le, %var_load, %0 : (i32, i32) -> i1
yield %cmp : i1
@@ -53,13 +53,13 @@ emitc.func @emitc_while_with_expression(%arg0 : !emitc.ptr<i32>) {
%0 = literal "10" : i32
%1 = literal "1" : i32
- %add = expression : i32 {
+ %add = expression %0, %1 : (i32, i32) -> i32 {
%add = add %0, %1 : (i32, i32) -> i32
yield %add : i32
}
while {
- %r = expression : i1 {
+ %r = expression %var, %add : (!emitc.lvalue<i32>, i32) -> i1 {
%var_load = load %var : <i32>
%cmp = cmp le, %var_load, %add : (i32, i32) -> i1
yield %cmp : i1
@@ -103,7 +103,7 @@ emitc.func @emitc_double_while() {
%end_2 = literal "5" : i32
while {
- %r = expression : i1 {
+ %r = expression %var_1, %end_1 : (!emitc.lvalue<i32>, i32) -> i1 {
%var_1_load = load %var_1 : <i32>
%cmp = cmp le, %var_1_load, %end_1 : (i32, i32) -> i1
yield %cmp : i1
@@ -114,7 +114,7 @@ emitc.func @emitc_double_while() {
%var_1_load = load %var_1 : <i32>
while {
- %r = expression : i1 {
+ %r = expression %var_2, %end_2 : (!emitc.lvalue<i32>, i32) -> i1 {
%var_2_load = load %var_2 : <i32>
%cmp = cmp le, %var_2_load, %end_2 : (i32, i32) -> i1
yield %cmp : i1
>From 531a8d87d19410829a9ce2c604ddd728c09108fe Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Wed, 3 Sep 2025 20:38:48 +0300
Subject: [PATCH 06/13] [mlir][emitc] Update description (NFC)
---
mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 8f58d671e20bf..7d3beb23babb5 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1752,7 +1752,7 @@ def EmitC_WhileOp : EmitC_Op<"while",
```mlir
emitc.while {
// Condition region (must yield i1)
- %condition = emitc.expression : i1 {
+ %condition = emitc.expression : () -> i1 {
// Condition computation...
%result = ... : i1 // Last operation must produce i1
emitc.yield %result : i1
@@ -1774,7 +1774,7 @@ def EmitC_WhileOp : EmitC_Op<"while",
%step = emitc.literal "1" : i32
emitc.while {
- %condition = emitc.expression : i1 {
+ %condition = emitc.expression %counter, %end : (!emitc.lvalue<i32>, i32) -> i1 {
%current = emitc.load %counter : !emitc.lvalue<i32>
%cmp_res = emitc.cmp lt, %current, %end : (i32, i32) -> i1
emitc.yield %cmp_res : i1
@@ -1857,7 +1857,7 @@ def EmitC_DoOp : EmitC_Op<"do",
// Loop body operations...
} while {
// Condition region (must yield i1)
- %condition = emitc.expression : i1 {
+ %condition = emitc.expression : () -> i1 {
// Condition computation...
%result = ... : i1 // Last operation must produce i1
emitc.yield %result : i1
@@ -1884,7 +1884,7 @@ def EmitC_DoOp : EmitC_Op<"do",
%new_val = emitc.add %val, %step : (i32, i32) -> i32
"emitc.assign"(%counter, %new_val) : (!emitc.lvalue<i32>, i32) -> ()
} while {
- %condition = emitc.expression : i1 {
+ %condition = emitc.expression %counter, %end : (!emitc.lvalue<i32>, i32) -> i1 {
%current = emitc.load %counter : !emitc.lvalue<i32>
%cmp_res = emitc.cmp lt, %current, %end : (i32, i32) -> i1
emitc.yield %cmp_res : i1
>From 5af06b8340cf8dbc8a05d0ad88f7173405d92e10 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Sat, 6 Sep 2025 02:00:33 +0300
Subject: [PATCH 07/13] [mlir][emitc] Remove unused WhileOp implementation
---
mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 107 +----------
mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp | 7 +-
mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 119 ++++--------
mlir/lib/Target/Cpp/TranslateToCpp.cpp | 29 +--
mlir/test/Dialect/EmitC/invalid_ops.mlir | 173 +-----------------
mlir/test/Dialect/EmitC/ops.mlir | 20 --
mlir/test/Target/Cpp/while.mlir | 136 --------------
7 files changed, 46 insertions(+), 545 deletions(-)
delete 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 7d3beb23babb5..3fc2bd4bb1841 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1393,7 +1393,7 @@ def EmitC_AssignOp : EmitC_Op<"assign", []> {
}
def EmitC_YieldOp : EmitC_Op<"yield",
- [Pure, Terminator, ParentOneOf<["DoOp", "ExpressionOp", "ForOp", "IfOp", "SwitchOp", "WhileOp"]>]> {
+ [Pure, Terminator, ParentOneOf<["DoOp", "ExpressionOp", "ForOp", "IfOp", "SwitchOp"]>]> {
let summary = "Block termination operation";
let description = [{
The `emitc.yield` terminates its parent EmitC op's region, optionally yielding
@@ -1727,107 +1727,6 @@ def EmitC_GetFieldOp
let hasVerifier = 1;
}
-def EmitC_WhileOp : EmitC_Op<"while",
- [NoTerminator, OpAsmOpInterface, RecursiveMemoryEffects]> {
- 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 is evaluated before each iteration as follows:
- - The condition region must contain exactly one block with:
- 1. An `emitc.expression` operation producing an i1 value
- 2. An `emitc.yield` passing through the expression result
- - The expression's body contains the actual condition logic
-
- If the condition yields true, the body region is executed. The loop terminates
- when the condition yields false. The body region must not yield any values.
-
- The canonical structure of `emitc.while` is:
-
- ```mlir
- emitc.while {
- // Condition region (must yield i1)
- %condition = emitc.expression : () -> i1 {
- // Condition computation...
- %result = ... : i1 // Last operation must produce i1
- emitc.yield %result : i1
- }
- // Forward expression result
- emitc.yield %condition : i1
- } do {
- // Body region (no terminator required).
- // Loop body operations...
- }
- ```
-
- Example:
-
- ```mlir
- emitc.func @while_example() {
- %counter = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
- %end = emitc.literal "10" : i32
- %step = emitc.literal "1" : i32
-
- emitc.while {
- %condition = emitc.expression %counter, %end : (!emitc.lvalue<i32>, i32) -> i1 {
- %current = emitc.load %counter : !emitc.lvalue<i32>
- %cmp_res = emitc.cmp lt, %current, %end : (i32, i32) -> i1
- emitc.yield %cmp_res : i1
- }
- emitc.yield %condition : i1
- } do {
- // Print current value
- %val = emitc.load %counter : !emitc.lvalue<i32>
- emitc.verbatim "printf(\"%d\\n\", {});" args %val : i32
-
- // Increment counter
- %new_val = emitc.add %val, %step : (i32, i32) -> i32
- "emitc.assign"(%counter, %new_val) : (!emitc.lvalue<i32>, i32) -> ()
- }
- return
- }
- ```
- ```c++
- // Code emitted for the operation above.
- void while_example() {
- int32_t v1 = 0;
- while (v1 < 10) {
- int32_t v2 = v1;
- printf("%d\n", v2);
- int32_t v3 = v2 + 1;
- v1 = v3;
- }
- return;
- }
- ```
- }];
-
- let results = (outs);
- let regions = (region SizedRegion<1>:$conditionRegion,
- SizedRegion<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",
[NoTerminator, OpAsmOpInterface, RecursiveMemoryEffects]> {
let summary = "Do-while operation";
@@ -1845,8 +1744,8 @@ def EmitC_DoOp : EmitC_Op<"do",
2. An `emitc.yield` passing through the expression result
- The expression's body contains the actual condition logic
- Unlike a while loop, the body region is executed before the first evaluation
- of the condition. Thus, there is a guarantee that the loop will be executed
+ The body region is executed before the first evaluation of the
+ condition. Thus, there is a guarantee that the loop will be executed
at least once. The loop terminates when the condition yields false.
The canonical structure of `emitc.do` is:
diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index 07df270f8221f..698325e73f3ef 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -336,8 +336,9 @@ 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.
+// Lower scf::while to emitc::do using mutable variables to maintain loop state
+// across iterations. The do-while structure ensures the condition is evaluated
+// after each iteration, matching SCF while semantics.
struct WhileLowering : public OpConversionPattern<WhileOp> {
using OpConversionPattern::OpConversionPattern;
@@ -453,7 +454,7 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
return finalResults;
}
- // Lower to emitc.do when condition arguments differ from region inputs.
+ // Lower scf.while to emitc.do.
LogicalResult lowerDoWhile(WhileOp whileOp, ArrayRef<Value> vars,
MLIRContext *context,
ConversionPatternRewriter &rewriter,
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index dc1326a1cdbc3..1ad621ae14b69 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -974,12 +974,10 @@ LogicalResult emitc::YieldOp::verify() {
Value result = getResult();
Operation *containingOp = getOperation()->getParentOp();
- if (result && containingOp->getNumResults() != 1 &&
- !isa<WhileOp, DoOp>(containingOp))
+ if (result && containingOp->getNumResults() != 1 && !isa<DoOp>(containingOp))
return emitOpError() << "yields a value not returned by parent";
- if (!result && containingOp->getNumResults() != 0 &&
- !isa<WhileOp, DoOp>(containingOp))
+ if (!result && containingOp->getNumResults() != 0 && !isa<DoOp>(containingOp))
return emitOpError() << "does not yield a value to be returned by parent";
return success();
@@ -1564,20 +1562,28 @@ LogicalResult GetFieldOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
}
//===----------------------------------------------------------------------===//
-// Common functions for WhileOp and DoOp
+// DoOp
//===----------------------------------------------------------------------===//
-static Operation *getRootOpFromLoopCondition(Region &condRegion) {
- auto yieldOp = cast<emitc::YieldOp>(condRegion.front().getTerminator());
+Operation *DoOp::getRootOp() {
+ auto yieldOp =
+ cast<emitc::YieldOp>(getConditionRegion().front().getTerminator());
return yieldOp.getResult().getDefiningOp();
}
-static LogicalResult verifyLoopRegions(Operation &op, Region &condition,
- Region &body) {
- Block &condBlock = condition.front();
+void DoOp::print(OpAsmPrinter &p) {
+ p << ' ';
+ p.printRegion(getBodyRegion(), /*printEntryBlockArgs=*/false);
+ p << " while ";
+ p.printRegion(getConditionRegion());
+ p.printOptionalAttrDictWithKeyword(getOperation()->getAttrs());
+}
+
+LogicalResult emitc::DoOp::verify() {
+ Block &condBlock = getConditionRegion().front();
if (condBlock.getOperations().size() != 2)
- return op.emitOpError(
+ return emitOpError(
"condition region must contain exactly two operations: "
"'emitc.expression' followed by 'emitc.yield', but found ")
<< condBlock.getOperations().size() << " operations";
@@ -1585,103 +1591,50 @@ static LogicalResult verifyLoopRegions(Operation &op, Region &condition,
Operation &first = condBlock.front();
auto exprOp = dyn_cast<emitc::ExpressionOp>(first);
if (!exprOp)
- return op.emitOpError("expected first op in condition region to be "
- "'emitc.expression', "
- "but got ")
+ return emitOpError("expected first op in condition region to be "
+ "'emitc.expression', but got ")
<< first.getName();
if (!exprOp.getResult().getType().isInteger(1))
- return op.emitOpError("emitc.expression in condition region must return "
- "'i1', but returns ")
+ return emitOpError("emitc.expression in condition region must return "
+ "'i1', but returns ")
<< exprOp.getResult().getType();
Operation &last = condBlock.back();
auto condYield = dyn_cast<emitc::YieldOp>(last);
if (!condYield)
- return op.emitOpError("expected last op in condition region to be "
- "'emitc.yield', but got ")
+ return emitOpError("expected last op in condition region to be "
+ "'emitc.yield', but got ")
<< last.getName();
if (condYield.getNumOperands() != 1)
- return op.emitOpError("expected condition region to return 1 value, but "
- "it returns ")
+ return emitOpError("expected condition region to return 1 value, but "
+ "it returns ")
<< condYield.getNumOperands() << " values";
if (condYield.getOperand(0) != exprOp.getResult())
- return op.emitError("'emitc.yield' must return result of "
- "'emitc.expression' from this condition region");
+ return emitError("'emitc.yield' must return result of "
+ "'emitc.expression' from this condition region");
- Block &bodyBlock = body.front();
+ Block &bodyBlock = getBodyRegion().front();
if (bodyBlock.empty())
- return op.emitOpError("body region cannot be empty");
+ return emitOpError("body region cannot be empty");
if (bodyBlock.mightHaveTerminator())
- return op.emitOpError("body region must not contain terminator");
+ return emitOpError("body region must not contain terminator");
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();
+ParseResult DoOp::parse(OpAsmParser &parser, OperationState &result) {
+ Region *bodyRegion = result.addRegion();
+ Region *condRegion = result.addRegion();
- if (parser.parseRegion(*firstRegion) || parser.parseKeyword(midKeyword) ||
- parser.parseRegion(*secondRegion))
+ if (parser.parseRegion(*bodyRegion) || parser.parseKeyword("while") ||
+ parser.parseRegion(*condRegion))
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");
+ return parser.parseOptionalAttrDictWithKeyword(result.attributes);
}
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 8c80331d03be9..5fe5f4181b71d 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -559,31 +559,6 @@ static LogicalResult printOperation(CppEmitter &emitter,
return success();
}
-static LogicalResult printOperation(CppEmitter &emitter,
- emitc::WhileOp whileOp) {
- raw_indented_ostream &os = emitter.ostream();
-
- os << "while (";
-
- Block &condBlock = whileOp.getConditionRegion().front();
- auto condYield = cast<emitc::YieldOp>(condBlock.back());
- if (failed(emitter.emitExpression(
- cast<emitc::ExpressionOp>(condYield.getOperand(0).getDefiningOp()))))
- return failure();
-
- os << ") {\n";
- os.indent();
-
- Block &bodyBlock = whileOp.getBodyRegion().front();
- for (Operation &op : bodyBlock) {
- if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/true)))
- return failure();
- }
-
- os.unindent() << "}";
- return success();
-}
-
static LogicalResult printOperation(CppEmitter &emitter, emitc::DoOp doOp) {
raw_indented_ostream &os = emitter.ostream();
@@ -1769,7 +1744,7 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
emitc::LogicalNotOp, emitc::LogicalOrOp, emitc::MulOp,
emitc::RemOp, emitc::ReturnOp, emitc::SubOp, emitc::SwitchOp,
emitc::UnaryMinusOp, emitc::UnaryPlusOp, emitc::VariableOp,
- emitc::VerbatimOp, emitc::WhileOp>(
+ emitc::VerbatimOp>(
[&](auto op) { return printOperation(*this, op); })
// Func ops.
@@ -1819,7 +1794,7 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
trailingSemicolon &=
!isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::DoOp, emitc::FileOp,
emitc::ForOp, emitc::IfOp, emitc::IncludeOp, emitc::SwitchOp,
- emitc::VerbatimOp, emitc::WhileOp>(op);
+ emitc::VerbatimOp>(op);
os << (trailingSemicolon ? ";\n" : "\n");
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index dc7eb343aa2d7..9a8aabceec5cb 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.do, emitc.expression, emitc.for, emitc.if, emitc.switch, emitc.while'}}
+ // expected-error @+1 {{'emitc.yield' op expects parent op to be one of 'emitc.do, emitc.expression, emitc.for, emitc.if, emitc.switch'}}
emitc.yield
return
}
@@ -732,176 +732,6 @@ emitc.class @testClass {
// -----
-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 contain exactly two operations: 'emitc.expression' followed by 'emitc.yield', but found 3 operations}}
- emitc.while {
- %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
- %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
- emitc.yield %cmp : i1
- }
-
- %3 = emitc.literal "3" : i32
-
- emitc.yield %r : i1
- } do {
- emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
- }
-
- return
-}
-
-// -----
-
-func.func @test_while(%arg0 : !emitc.ptr<i32>) {
- // expected-error @+1 {{'emitc.while' op expected first op in condition region to be 'emitc.expression', but got emitc.literal}}
- emitc.while {
- %true = emitc.literal "true" : i1
-
- emitc.yield %true : 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 emitc.expression in condition region must return 'i1', but returns 'i32'}}
- emitc.while {
- %r = emitc.expression %1, %2 : (i32, i32) -> i32 {
- %add = emitc.add %1, %2 : (i32, i32) -> i32
- emitc.yield %add : i32
- }
-
- emitc.yield %r : i32
- } 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 last op in condition region to be 'emitc.yield', but got emitc.expression}}
- emitc.while {
- %r1 = emitc.expression %1, %2 : (i32, i32) -> i1 {
- %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
- emitc.yield %cmp : i1
- }
-
- %r2 = emitc.expression %1, %2 : (i32, i32) -> i32 {
- %add = emitc.add %1, %2 : (i32, i32) -> i32
- emitc.yield %add : i32
- }
- } 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 return 1 value, but it returns 0 values}}
- emitc.while {
- %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
- %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
- emitc.yield %cmp : i1
- }
-
- emitc.yield
- } 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
-
- %true = emitc.literal "true" : i1
-
- // expected-error @+1 {{'emitc.yield' must return result of 'emitc.expression' from this condition region}}
- emitc.while {
- %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
- %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
- emitc.yield %cmp : i1
- }
-
- emitc.yield %true: i1
- } 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 {
- %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
- %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
- emitc.yield %cmp : i1
- }
-
- emitc.yield %r: i1
- } do {
- ^bb0:
- }
-
- 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 body region must not contain terminator}}
- emitc.while {
- %r = emitc.expression %1, %2 : (i32, i32) -> i1 {
- %cmp = emitc.cmp eq, %1, %2 : (i32, i32) -> i1
- emitc.yield %cmp : i1
- }
-
- emitc.yield %r: i1
- } do {
- emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
- emitc.yield
- }
-
- return
-}
-
-// -----
-
func.func @test_do(%arg0 : !emitc.ptr<i32>) {
%1 = emitc.literal "1" : i32
%2 = emitc.literal "2" : i32
@@ -1027,7 +857,6 @@ func.func @test_do(%arg0 : !emitc.ptr<i32>) {
// -----
-
func.func @test_do() {
%1 = emitc.literal "1" : i32
%2 = emitc.literal "2" : i32
diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir
index d85d725f7b20b..1259748dfce84 100644
--- a/mlir/test/Dialect/EmitC/ops.mlir
+++ b/mlir/test/Dialect/EmitC/ops.mlir
@@ -336,26 +336,6 @@ emitc.class final @finalClass {
}
}
-func.func @while(%arg0 : !emitc.ptr<i32>) {
- %1 = emitc.literal "1" : i32
- %2 = emitc.literal "2" : i32
- %3 = emitc.literal "3" : i32
-
- emitc.while {
- %r = emitc.expression %1, %2, %3 : (i32, i32, i32) -> i1 {
- %add = emitc.add %1, %2 : (i32, i32) -> i32
- %cmp = emitc.cmp eq, %add, %3 : (i32, i32) -> i1
- emitc.yield %cmp : i1
- }
-
- emitc.yield %r : 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
diff --git a/mlir/test/Target/Cpp/while.mlir b/mlir/test/Target/Cpp/while.mlir
deleted file mode 100644
index cff154161ab40..0000000000000
--- a/mlir/test/Target/Cpp/while.mlir
+++ /dev/null
@@ -1,136 +0,0 @@
-// 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 = literal "10" : i32
- %1 = literal "1" : i32
-
- while {
- %r = expression %var, %0 : (!emitc.lvalue<i32>, i32) -> i1 {
- %var_load = load %var : <i32>
- %cmp = cmp le, %var_load, %0 : (i32, i32) -> i1
- yield %cmp : i1
- }
-
- yield %r : i1
- } do {
- 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 = literal "10" : i32
- %1 = literal "1" : i32
-
- %add = expression %0, %1 : (i32, i32) -> i32 {
- %add = add %0, %1 : (i32, i32) -> i32
- yield %add : i32
- }
-
- while {
- %r = expression %var, %add : (!emitc.lvalue<i32>, i32) -> i1 {
- %var_load = load %var : <i32>
- %cmp = cmp le, %var_load, %add : (i32, i32) -> i1
- yield %cmp : i1
- }
-
- yield %r : i1
- } do {
- 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_double_while() {
-// CPP-DEFAULT: int32_t v1 = 0;
-// CPP-DEFAULT: int32_t v2 = 0;
-// CPP-DEFAULT: while (v1 <= 3) {
-// CPP-DEFAULT: int32_t v3 = v1;
-// CPP-DEFAULT: while (v2 <= 5) {
-// CPP-DEFAULT: int32_t v4 = v2;
-// CPP-DEFAULT: printf("i = %d, j = %d", v3, v4);
-// CPP-DEFAULT: int32_t v5 = v4 + 1;
-// CPP-DEFAULT: v2 = v5;
-// CPP-DEFAULT: }
-// CPP-DEFAULT: int32_t v6 = v3 + 1;
-// CPP-DEFAULT: v1 = v6;
-// CPP-DEFAULT: }
-// CPP-DEFAULT: return;
-// CPP-DEFAULT: }
-
-emitc.func @emitc_double_while() {
- %var_1 = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
- %var_2 = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
-
- %step = literal "1" : i32
- %end_1 = literal "3" : i32
- %end_2 = literal "5" : i32
-
- while {
- %r = expression %var_1, %end_1 : (!emitc.lvalue<i32>, i32) -> i1 {
- %var_1_load = load %var_1 : <i32>
- %cmp = cmp le, %var_1_load, %end_1 : (i32, i32) -> i1
- yield %cmp : i1
- }
-
- yield %r : i1
- } do {
- %var_1_load = load %var_1 : <i32>
-
- while {
- %r = expression %var_2, %end_2 : (!emitc.lvalue<i32>, i32) -> i1 {
- %var_2_load = load %var_2 : <i32>
- %cmp = cmp le, %var_2_load, %end_2 : (i32, i32) -> i1
- yield %cmp : i1
- }
-
- yield %r : i1
- } do {
- %var_2_load = load %var_2 : <i32>
- verbatim "printf(\"i = %d, j = %d\", {}, {});" args %var_1_load, %var_2_load : i32, i32
- %tmp_add = add %var_2_load, %step : (i32, i32) -> i32
- "emitc.assign"(%var_2, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
- }
-
- %tmp_add = add %var_1_load, %step : (i32, i32) -> i32
- "emitc.assign"(%var_1, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
- }
-
- return
-}
>From 6df8ceb0696a6b88b85318a34f814a340beb3773 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Mon, 8 Sep 2025 11:54:59 +0300
Subject: [PATCH 08/13] [mlir][emitc] Changes after review
---
mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 1ad621ae14b69..7100ed46294a0 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -974,10 +974,10 @@ LogicalResult emitc::YieldOp::verify() {
Value result = getResult();
Operation *containingOp = getOperation()->getParentOp();
- if (result && containingOp->getNumResults() != 1 && !isa<DoOp>(containingOp))
+ if (!isa<DoOp>(containingOp) && result && containingOp->getNumResults() != 1)
return emitOpError() << "yields a value not returned by parent";
- if (!result && containingOp->getNumResults() != 0 && !isa<DoOp>(containingOp))
+ if (!isa<DoOp>(containingOp) && !result && containingOp->getNumResults() != 0)
return emitOpError() << "does not yield a value to be returned by parent";
return success();
>From dd244fe95ccdcae19b3b37a630f0029d11df9d73 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Tue, 16 Sep 2025 15:43:25 +0300
Subject: [PATCH 09/13] [mlir][emitc] Changes after review
---
mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp | 20 ++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index 698325e73f3ef..db022002ea0fd 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -21,6 +21,7 @@
#include "mlir/IR/PatternMatch.h"
#include "mlir/Transforms/DialectConversion.h"
#include "mlir/Transforms/Passes.h"
+#include "llvm/Support/LogicalResult.h"
namespace mlir {
#define GEN_PASS_DEF_SCFTOEMITC
@@ -415,16 +416,22 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
// 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 {
+ LogicalResult processYieldTerminator(Operation *terminator,
+ ArrayRef<Value> vars,
+ ConversionPatternRewriter &rewriter,
+ Location loc) const {
auto yieldOp = cast<scf::YieldOp>(terminator);
- SmallVector<Value> yields(yieldOp.getOperands());
+ SmallVector<Value> yields;
+ if (failed(rewriter.getRemappedValues(yieldOp.getOperands(), yields)))
+ return rewriter.notifyMatchFailure(yieldOp,
+ "failed to lower yield operands");
rewriter.eraseOp(yieldOp);
rewriter.setInsertionPointToEnd(yieldOp->getBlock());
for (auto [var, val] : llvm::zip(vars, yields))
rewriter.create<emitc::AssignOp>(loc, var, val);
+
+ return success();
}
// Transfers final loop state from mutable variables to result variables,
@@ -500,7 +507,10 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
ifBlock->eraseArguments(0, ifBlock->getNumArguments());
// Convert scf.yield to variable assignments for state updates.
- processYieldTerminator(ifBlock->getTerminator(), vars, rewriter, loc);
+ if (failed(processYieldTerminator(ifBlock->getTerminator(), vars,
+ rewriter, loc)))
+ return failure();
+
rewriter.create<emitc::YieldOp>(loc);
}
>From 96962f68ae8865adc0e4a8c1ac70b26fa3607913 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Wed, 17 Sep 2025 16:02:14 +0300
Subject: [PATCH 10/13] [mlir][emitc] Do not create an ifOp if the after-region
is empty
---
mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp | 15 ++++----
mlir/test/Conversion/SCFToEmitC/while.mlir | 36 +++++++++++++++++++
2 files changed, 43 insertions(+), 8 deletions(-)
diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index db022002ea0fd..2da287b4e7156 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -474,7 +474,7 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
auto loweredDo = rewriter.create<emitc::DoOp>(loc);
- // Lower before region as body.
+ // Lower before-region as body.
rewriter.inlineRegionBefore(whileOp.getBefore(), loweredDo.getBodyRegion(),
loweredDo.getBodyRegion().end());
@@ -489,14 +489,13 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
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);
+ // Wrap body region in conditional to preserve scf semantics. Only create
+ // ifOp if after-region is non-empty.
+ if (whileOp.getAfterBody()->getOperations().size() > 1) {
+ auto ifOp = rewriter.create<emitc::IfOp>(loc, condition, false, false);
+ rewriter.inlineRegionBefore(whileOp.getAfter(), ifOp.getBodyRegion(),
+ ifOp.getBodyRegion().begin());
- // 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.
diff --git a/mlir/test/Conversion/SCFToEmitC/while.mlir b/mlir/test/Conversion/SCFToEmitC/while.mlir
index c2a446e4f8017..3e958630f5e27 100644
--- a/mlir/test/Conversion/SCFToEmitC/while.mlir
+++ b/mlir/test/Conversion/SCFToEmitC/while.mlir
@@ -203,3 +203,39 @@ func.func @double_use(%p : !emitc.ptr<i32>) -> i32 {
// CHECK: %[[VAL_15:.*]] = emitc.load %[[VAL_13]] : <i32>
// CHECK: return %[[VAL_15]] : i32
// CHECK: }
+
+emitc.func @payload_empty_after_region() -> i1 {
+ %true = emitc.literal "true" : i1
+ return %true : i1
+}
+
+func.func @empty_after_region() {
+ scf.while () : () -> () {
+ %condition = emitc.call @payload_empty_after_region() : () -> i1
+ scf.condition(%condition)
+ } do {
+ ^bb0():
+ scf.yield
+ }
+ return
+}
+
+// CHECK-LABEL: emitc.func @payload_empty_after_region() -> i1 {
+// CHECK: %[[VAL_0:.*]] = literal "true" : i1
+// CHECK: return %[[VAL_0]] : i1
+// CHECK: }
+
+// CHECK-LABEL: func.func @empty_after_region() {
+// CHECK: %[[VAL_0:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i1>
+// CHECK: emitc.do {
+// CHECK: %[[VAL_1:.*]] = call @payload_empty_after_region() : () -> i1
+// CHECK: assign %[[VAL_1]] : i1 to %[[VAL_0]] : <i1>
+// CHECK: } while {
+// CHECK: %[[VAL_2:.*]] = expression %[[VAL_0]] : (!emitc.lvalue<i1>) -> i1 {
+// CHECK: %[[VAL_3:.*]] = load %[[VAL_0]] : <i1>
+// CHECK: yield %[[VAL_3]] : i1
+// CHECK: }
+// CHECK: yield %[[VAL_2]] : i1
+// CHECK: }
+// CHECK: return
+// CHECK: }
>From ba3f5c183a5d26edf2fb997787191ae0263034f3 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Wed, 17 Sep 2025 19:03:47 +0300
Subject: [PATCH 11/13] [mlir][emitc] Duplicate of the 'lowerYield' function
has been removed
---
mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp | 32 +++----------------
mlir/test/Conversion/SCFToEmitC/while.mlir | 3 --
2 files changed, 4 insertions(+), 31 deletions(-)
diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index 2da287b4e7156..38a881bca3f91 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -117,16 +117,15 @@ SmallVector<Value> loadValues(const SmallVector<Value> &variables,
static LogicalResult lowerYield(Operation *op, ValueRange resultVariables,
ConversionPatternRewriter &rewriter,
- scf::YieldOp yield) {
+ scf::YieldOp yield, bool createYield = true) {
Location loc = yield.getLoc();
OpBuilder::InsertionGuard guard(rewriter);
rewriter.setInsertionPoint(yield);
SmallVector<Value> yieldOperands;
- if (failed(rewriter.getRemappedValues(yield.getOperands(), yieldOperands))) {
+ if (failed(rewriter.getRemappedValues(yield.getOperands(), yieldOperands)))
return rewriter.notifyMatchFailure(op, "failed to lower yield operands");
- }
assignValues(yieldOperands, resultVariables, rewriter, loc);
@@ -414,26 +413,6 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
block->eraseArguments(0, block->getNumArguments());
}
- // Convert SCF yield terminators to imperative assignments to update loop
- // variables, maintaining loop semantics while transitioning to emitc model.
- LogicalResult processYieldTerminator(Operation *terminator,
- ArrayRef<Value> vars,
- ConversionPatternRewriter &rewriter,
- Location loc) const {
- auto yieldOp = cast<scf::YieldOp>(terminator);
- SmallVector<Value> yields;
- if (failed(rewriter.getRemappedValues(yieldOp.getOperands(), yields)))
- return rewriter.notifyMatchFailure(yieldOp,
- "failed to lower yield operands");
- rewriter.eraseOp(yieldOp);
-
- rewriter.setInsertionPointToEnd(yieldOp->getBlock());
- for (auto [var, val] : llvm::zip(vars, yields))
- rewriter.create<emitc::AssignOp>(loc, var, val);
-
- return success();
- }
-
// Transfers final loop state from mutable variables to result variables,
// then returns the final SSA values to replace the original scf::while
// results.
@@ -505,12 +484,9 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
ifBlock->eraseArguments(0, ifBlock->getNumArguments());
- // Convert scf.yield to variable assignments for state updates.
- if (failed(processYieldTerminator(ifBlock->getTerminator(), vars,
- rewriter, loc)))
+ if (failed(lowerYield(whileOp, vars, rewriter,
+ cast<scf::YieldOp>(ifBlock->getTerminator()))))
return failure();
-
- rewriter.create<emitc::YieldOp>(loc);
}
rewriter.eraseOp(condOp);
diff --git a/mlir/test/Conversion/SCFToEmitC/while.mlir b/mlir/test/Conversion/SCFToEmitC/while.mlir
index 3e958630f5e27..a791ed59521e2 100644
--- a/mlir/test/Conversion/SCFToEmitC/while.mlir
+++ b/mlir/test/Conversion/SCFToEmitC/while.mlir
@@ -83,7 +83,6 @@ func.func @two_results() -> i32 {
return %res1 : i32
}
-
// CHECK-LABEL: emitc.func @payload_two_results(
// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
// CHECK: %[[VAL_0:.*]] = add %[[ARG0]], %[[ARG0]] : (i32, i32) -> i32
@@ -156,7 +155,6 @@ func.func @double_use(%p : !emitc.ptr<i32>) -> i32 {
}
return %res : i32
}
-
// CHECK-LABEL: emitc.func @payload_double_use(
// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
// CHECK: %[[VAL_0:.*]] = add %[[ARG0]], %[[ARG0]] : (i32, i32) -> i32
@@ -219,7 +217,6 @@ func.func @empty_after_region() {
}
return
}
-
// CHECK-LABEL: emitc.func @payload_empty_after_region() -> i1 {
// CHECK: %[[VAL_0:.*]] = literal "true" : i1
// CHECK: return %[[VAL_0]] : i1
>From d2fb44cff73c5e7e01d8b41296de9228e227d9b3 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Mon, 22 Sep 2025 18:18:51 +0300
Subject: [PATCH 12/13] [mlir][emitc] Changes after review
---
mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp | 81 ++++++++++---------
1 file changed, 42 insertions(+), 39 deletions(-)
diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index 38a881bca3f91..8e8c75968db55 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -107,7 +107,7 @@ static void assignValues(ValueRange values, ValueRange variables,
emitc::AssignOp::create(rewriter, loc, var, value);
}
-SmallVector<Value> loadValues(const SmallVector<Value> &variables,
+SmallVector<Value> loadValues(ArrayRef<Value> variables,
PatternRewriter &rewriter, Location loc) {
return llvm::map_to_vector<>(variables, [&](Value var) {
Type type = cast<emitc::LValueType>(var.getType()).getValueType();
@@ -351,7 +351,8 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
// 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)))
+ if (failed(createVariablesForLoopCarriedValues(whileOp, rewriter, variables,
+ loc, context)))
return failure();
if (failed(lowerDoWhile(whileOp, variables, context, rewriter, loc)))
@@ -379,15 +380,19 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
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) {
+ LogicalResult createVariablesForLoopCarriedValues(
+ WhileOp whileOp, ConversionPatternRewriter &rewriter,
+ SmallVectorImpl<Value> &outVars, Location loc,
+ MLIRContext *context) const {
emitc::OpaqueAttr noInit = emitc::OpaqueAttr::get(context, "");
for (Value init : whileOp.getInits()) {
+ Type convertedType = getTypeConverter()->convertType(init.getType());
+ if (!convertedType)
+ return rewriter.notifyMatchFailure(whileOp, "type conversion failed");
+
emitc::VariableOp var = rewriter.create<emitc::VariableOp>(
- loc, emitc::LValueType::get(init.getType()), noInit);
+ loc, emitc::LValueType::get(convertedType), noInit);
rewriter.create<emitc::AssignOp>(loc, var.getResult(), init);
outVars.push_back(var.getResult());
}
@@ -395,24 +400,6 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
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());
- }
-
// Transfers final loop state from mutable variables to result variables,
// then returns the final SSA values to replace the original scf::while
// results.
@@ -445,6 +432,7 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
MLIRContext *context,
ConversionPatternRewriter &rewriter,
Location loc) const {
+ // Create a global boolean variable to store the loop condition state.
Type i1Type = IntegerType::get(context, 1);
auto globalCondition =
rewriter.create<emitc::VariableOp>(loc, emitc::LValueType::get(i1Type),
@@ -453,12 +441,24 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
auto loweredDo = rewriter.create<emitc::DoOp>(loc);
- // Lower before-region as body.
- rewriter.inlineRegionBefore(whileOp.getBefore(), loweredDo.getBodyRegion(),
- loweredDo.getBodyRegion().end());
+ // Convert region types to match the target dialect type system.
+ if (failed(rewriter.convertRegionTypes(&whileOp.getBefore(),
+ *getTypeConverter(), nullptr)) ||
+ failed(rewriter.convertRegionTypes(&whileOp.getAfter(),
+ *getTypeConverter(), nullptr))) {
+ return rewriter.notifyMatchFailure(whileOp,
+ "region types conversion failed");
+ }
+
+ // Prepare the before region (condition evaluation) for merging.
+ Block *beforeBlock = &whileOp.getBefore().front();
+ Block *bodyBlock = rewriter.createBlock(&loweredDo.getBodyRegion());
+ rewriter.setInsertionPointToStart(bodyBlock);
- Block *bodyBlock = &loweredDo.getBodyRegion().front();
- replaceBlockArgsWithVarLoads(bodyBlock, vars, rewriter, loc);
+ // Load current variable values to use as initial arguments for the
+ // condition block.
+ SmallVector<Value> replacingValues = loadValues(vars, rewriter, loc);
+ rewriter.mergeBlocks(beforeBlock, bodyBlock, replacingValues);
// Convert scf.condition to condition variable assignment.
Operation *condTerminator =
@@ -472,20 +472,20 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
// ifOp if after-region is non-empty.
if (whileOp.getAfterBody()->getOperations().size() > 1) {
auto ifOp = rewriter.create<emitc::IfOp>(loc, condition, false, false);
- rewriter.inlineRegionBefore(whileOp.getAfter(), ifOp.getBodyRegion(),
- ifOp.getBodyRegion().begin());
- Block *ifBlock = &ifOp.getBodyRegion().front();
+ // Prepare the after region (loop body) for merging.
+ Block *afterBlock = &whileOp.getAfter().front();
+ Block *ifBodyBlock = rewriter.createBlock(&ifOp.getBodyRegion());
- // 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));
+ // Replacement values for after block using condition op arguments.
+ SmallVector<Value> afterReplacingValues;
+ for (Value arg : condOp.getArgs())
+ afterReplacingValues.push_back(rewriter.getRemappedValue(arg));
- ifBlock->eraseArguments(0, ifBlock->getNumArguments());
+ rewriter.mergeBlocks(afterBlock, ifBodyBlock, afterReplacingValues);
if (failed(lowerYield(whileOp, vars, rewriter,
- cast<scf::YieldOp>(ifBlock->getTerminator()))))
+ cast<scf::YieldOp>(ifBodyBlock->getTerminator()))))
return failure();
}
@@ -500,13 +500,16 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
loc, i1Type, conditionVal, /*do_not_inline=*/false);
Block *exprBlock = rewriter.createBlock(&exprOp.getBodyRegion());
+ // Set up the expression block to load the condition variable.
exprBlock->addArgument(conditionVal.getType(), loc);
rewriter.setInsertionPointToStart(exprBlock);
+ // Load the condition value and yield it as the expression result.
Value cond =
rewriter.create<emitc::LoadOp>(loc, i1Type, exprBlock->getArgument(0));
rewriter.create<emitc::YieldOp>(loc, cond);
+ // Yield the expression as the condition region result.
rewriter.setInsertionPointToEnd(condBlock);
rewriter.create<emitc::YieldOp>(loc, exprOp);
>From cff30128e94f271555b75ea49279235ceee3e614 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Fri, 26 Sep 2025 14:56:49 +0300
Subject: [PATCH 13/13] [mlir][emitc] Fixed a bug with return values in the
lowering
---
mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp | 79 +++++-------
mlir/test/Conversion/SCFToEmitC/while.mlir | 118 +++++++++---------
2 files changed, 87 insertions(+), 110 deletions(-)
diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index 8e8c75968db55..939cfca806660 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -348,32 +348,32 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
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(createVariablesForLoopCarriedValues(whileOp, rewriter, variables,
- loc, context)))
- return failure();
-
- if (failed(lowerDoWhile(whileOp, variables, context, rewriter, loc)))
- 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))) {
+ resultVariables)))
return rewriter.notifyMatchFailure(whileOp,
"Failed to create result variables");
- }
+
+ // Create variable storage for loop-carried values to enable imperative
+ // updates while maintaining SSA semantics at conversion boundaries.
+ SmallVector<Value> loopVariables;
+ if (failed(createVariablesForLoopCarriedValues(
+ whileOp, rewriter, loopVariables, loc, context)))
+ return failure();
+
+ if (failed(lowerDoWhile(whileOp, loopVariables, resultVariables, context,
+ rewriter, loc)))
+ return failure();
rewriter.setInsertionPointAfter(whileOp);
- // Transfer final loop state to result variables and get final SSA results.
+ // Load the final result values from result variables.
SmallVector<Value> finalResults =
- finalizeLoopResults(resultVariables, variables, rewriter, loc);
-
+ loadValues(resultVariables, rewriter, loc);
rewriter.replaceOp(whileOp, finalResults);
+
return success();
}
@@ -382,7 +382,7 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
// across iterations without SSA argument passing.
LogicalResult createVariablesForLoopCarriedValues(
WhileOp whileOp, ConversionPatternRewriter &rewriter,
- SmallVectorImpl<Value> &outVars, Location loc,
+ SmallVectorImpl<Value> &loopVars, Location loc,
MLIRContext *context) const {
emitc::OpaqueAttr noInit = emitc::OpaqueAttr::get(context, "");
@@ -394,42 +394,15 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
emitc::VariableOp var = rewriter.create<emitc::VariableOp>(
loc, emitc::LValueType::get(convertedType), noInit);
rewriter.create<emitc::AssignOp>(loc, var.getResult(), init);
- outVars.push_back(var.getResult());
+ loopVars.push_back(var.getResult());
}
return success();
}
- // 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;
- }
-
// Lower scf.while to emitc.do.
- LogicalResult lowerDoWhile(WhileOp whileOp, ArrayRef<Value> vars,
- MLIRContext *context,
+ LogicalResult lowerDoWhile(WhileOp whileOp, ArrayRef<Value> loopVars,
+ ArrayRef<Value> resultVars, MLIRContext *context,
ConversionPatternRewriter &rewriter,
Location loc) const {
// Create a global boolean variable to store the loop condition state.
@@ -457,14 +430,22 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
// Load current variable values to use as initial arguments for the
// condition block.
- SmallVector<Value> replacingValues = loadValues(vars, rewriter, loc);
+ SmallVector<Value> replacingValues = loadValues(loopVars, rewriter, loc);
rewriter.mergeBlocks(beforeBlock, bodyBlock, replacingValues);
- // Convert scf.condition to condition variable assignment.
Operation *condTerminator =
loweredDo.getBodyRegion().back().getTerminator();
scf::ConditionOp condOp = cast<scf::ConditionOp>(condTerminator);
rewriter.setInsertionPoint(condOp);
+
+ // Update result variables with values from scf::condition.
+ SmallVector<Value> conditionArgs;
+ for (Value arg : condOp.getArgs()) {
+ conditionArgs.push_back(rewriter.getRemappedValue(arg));
+ }
+ assignValues(conditionArgs, resultVars, rewriter, loc);
+
+ // Convert scf.condition to condition variable assignment.
Value condition = rewriter.getRemappedValue(condOp.getCondition());
rewriter.create<emitc::AssignOp>(loc, conditionVal, condition);
@@ -484,7 +465,7 @@ struct WhileLowering : public OpConversionPattern<WhileOp> {
rewriter.mergeBlocks(afterBlock, ifBodyBlock, afterReplacingValues);
- if (failed(lowerYield(whileOp, vars, rewriter,
+ if (failed(lowerYield(whileOp, loopVars, rewriter,
cast<scf::YieldOp>(ifBodyBlock->getTerminator()))))
return failure();
}
diff --git a/mlir/test/Conversion/SCFToEmitC/while.mlir b/mlir/test/Conversion/SCFToEmitC/while.mlir
index a791ed59521e2..f6ea41f2ebb8f 100644
--- a/mlir/test/Conversion/SCFToEmitC/while.mlir
+++ b/mlir/test/Conversion/SCFToEmitC/while.mlir
@@ -35,30 +35,29 @@ func.func @one_result() -> 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: %[[VAL_4:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_4]] : <i32>
+// CHECK: %[[VAL_5:.*]] = "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_one_result(%[[VAL_8]]) : (i32) -> i32
-// CHECK: assign %[[VAL_9]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_6:.*]] = load %[[VAL_4]] : <i32>
+// CHECK: %[[VAL_7:.*]] = add %[[VAL_6]], %[[VAL_1]] : (i32, i32) -> i32
+// CHECK: %[[VAL_8:.*]] = cmp lt, %[[VAL_7]], %[[VAL_2]] : (i32, i32) -> i1
+// CHECK: %[[VAL_9:.*]] = add %[[VAL_6]], %[[VAL_6]] : (i32, i32) -> i32
+// CHECK: assign %[[VAL_9]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: assign %[[VAL_8]] : i1 to %[[VAL_5]] : <i1>
+// CHECK: if %[[VAL_8]] {
+// CHECK: %[[VAL_10:.*]] = call @payload_one_result(%[[VAL_9]]) : (i32) -> i32
+// CHECK: assign %[[VAL_10]] : i32 to %[[VAL_4]] : <i32>
// CHECK: }
// CHECK: } while {
-// CHECK: %[[VAL_10:.*]] = expression %[[VAL_4]] : (!emitc.lvalue<i1>) -> i1 {
-// CHECK: %[[VAL_11:.*]] = load %[[VAL_4]] : <i1>
-// CHECK: yield %[[VAL_11]] : i1
+// CHECK: %[[VAL_11:.*]] = expression %[[VAL_5]] : (!emitc.lvalue<i1>) -> i1 {
+// CHECK: %[[VAL_12:.*]] = load %[[VAL_5]] : <i1>
+// CHECK: yield %[[VAL_12]] : i1
// CHECK: }
-// CHECK: yield %[[VAL_10]] : i1
+// CHECK: yield %[[VAL_11]] : i1
// CHECK: }
-// CHECK: %[[VAL_12:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
// CHECK: %[[VAL_13:.*]] = emitc.load %[[VAL_3]] : <i32>
-// CHECK: emitc.assign %[[VAL_13]] : i32 to %[[VAL_12]] : <i32>
-// CHECK: %[[VAL_14:.*]] = emitc.load %[[VAL_12]] : <i32>
-// CHECK: return %[[VAL_14]] : i32
+// CHECK: return %[[VAL_13]] : i32
// CHECK: }
emitc.func @payload_two_results(%arg: i32) -> i32 {
@@ -93,38 +92,36 @@ func.func @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: %[[VAL_4:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_4]] : <i32>
+// CHECK: %[[VAL_5:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_5]] : <i32>
+// CHECK: %[[VAL_6:.*]] = "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_two_results(%[[VAL_0]]) : (i32) -> i32
-// CHECK: %[[VAL_10:.*]] = call @payload_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: %[[VAL_7:.*]] = load %[[VAL_4]] : <i32>
+// CHECK: %[[VAL_8:.*]] = load %[[VAL_5]] : <i32>
+// CHECK: %[[VAL_9:.*]] = add %[[VAL_7]], %[[VAL_8]] : (i32, i32) -> i32
+// CHECK: %[[VAL_10:.*]] = cmp lt, %[[VAL_9]], %[[VAL_1]] : (i32, i32) -> i1
+// CHECK: assign %[[VAL_0]] : i32 to %[[VAL_2]] : <i32>
+// CHECK: assign %[[VAL_8]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: assign %[[VAL_10]] : i1 to %[[VAL_6]] : <i1>
+// CHECK: if %[[VAL_10]] {
+// CHECK: %[[VAL_11:.*]] = call @payload_two_results(%[[VAL_0]]) : (i32) -> i32
+// CHECK: %[[VAL_12:.*]] = call @payload_two_results(%[[VAL_8]]) : (i32) -> i32
+// CHECK: assign %[[VAL_11]] : i32 to %[[VAL_4]] : <i32>
+// CHECK: assign %[[VAL_12]] : i32 to %[[VAL_5]] : <i32>
// CHECK: }
// CHECK: } while {
-// CHECK: %[[VAL_11:.*]] = expression %[[VAL_4]] : (!emitc.lvalue<i1>) -> i1 {
-// CHECK: %[[VAL_12:.*]] = load %[[VAL_4]] : <i1>
-// CHECK: yield %[[VAL_12]] : i1
+// CHECK: %[[VAL_13:.*]] = expression %[[VAL_6]] : (!emitc.lvalue<i1>) -> i1 {
+// CHECK: %[[VAL_14:.*]] = load %[[VAL_6]] : <i1>
+// CHECK: yield %[[VAL_14]] : i1
// CHECK: }
-// CHECK: yield %[[VAL_11]] : i1
+// CHECK: yield %[[VAL_13]] : i1
// CHECK: }
-// CHECK: %[[VAL_13:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
-// CHECK: %[[VAL_14:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
// CHECK: %[[VAL_15:.*]] = emitc.load %[[VAL_2]] : <i32>
-// CHECK: emitc.assign %[[VAL_15]] : i32 to %[[VAL_13]] : <i32>
// CHECK: %[[VAL_16:.*]] = emitc.load %[[VAL_3]] : <i32>
-// CHECK: emitc.assign %[[VAL_16]] : i32 to %[[VAL_14]] : <i32>
-// CHECK: %[[VAL_17:.*]] = emitc.load %[[VAL_13]] : <i32>
-// CHECK: %[[VAL_18:.*]] = emitc.load %[[VAL_14]] : <i32>
-// CHECK: return %[[VAL_17]] : i32
+// CHECK: return %[[VAL_15]] : i32
// CHECK: }
emitc.func @payload_double_use(%arg: i32) -> i32 {
@@ -175,31 +172,30 @@ func.func @double_use(%p : !emitc.ptr<i32>) -> 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: %[[VAL_4:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_4]] : <i32>
+// CHECK: %[[VAL_5:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i1>
// CHECK: emitc.do {
-// CHECK: %[[VAL_5:.*]] = load %[[VAL_3]] : <i32>
-// CHECK: %[[VAL_6:.*]] = call @foo_with_side_effect(%[[VAL_5]], %[[ARG0]]) : (i32, !emitc.ptr<i32>) -> i32
-// CHECK: %[[VAL_7:.*]] = add %[[VAL_6]], %[[VAL_6]] : (i32, i32) -> i32
-// CHECK: %[[VAL_8:.*]] = add %[[VAL_5]], %[[VAL_7]] : (i32, i32) -> i32
-// CHECK: %[[VAL_9:.*]] = cmp lt, %[[VAL_8]], %[[VAL_2]] : (i32, i32) -> i1
-// CHECK: assign %[[VAL_9]] : i1 to %[[VAL_4]] : <i1>
-// CHECK: if %[[VAL_9]] {
-// CHECK: %[[VAL_10:.*]] = call @payload_double_use(%[[VAL_5]]) : (i32) -> i32
-// CHECK: assign %[[VAL_10]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: %[[VAL_6:.*]] = load %[[VAL_4]] : <i32>
+// CHECK: %[[VAL_7:.*]] = call @foo_with_side_effect(%[[VAL_6]], %[[ARG0]]) : (i32, !emitc.ptr<i32>) -> i32
+// CHECK: %[[VAL_8:.*]] = add %[[VAL_7]], %[[VAL_7]] : (i32, i32) -> i32
+// CHECK: %[[VAL_9:.*]] = add %[[VAL_6]], %[[VAL_8]] : (i32, i32) -> i32
+// CHECK: %[[VAL_10:.*]] = cmp lt, %[[VAL_9]], %[[VAL_2]] : (i32, i32) -> i1
+// CHECK: assign %[[VAL_6]] : i32 to %[[VAL_3]] : <i32>
+// CHECK: assign %[[VAL_10]] : i1 to %[[VAL_5]] : <i1>
+// CHECK: if %[[VAL_10]] {
+// CHECK: %[[VAL_11:.*]] = call @payload_double_use(%[[VAL_6]]) : (i32) -> i32
+// CHECK: assign %[[VAL_11]] : i32 to %[[VAL_4]] : <i32>
// CHECK: }
// CHECK: } while {
-// CHECK: %[[VAL_11:.*]] = expression %[[VAL_4]] : (!emitc.lvalue<i1>) -> i1 {
-// CHECK: %[[VAL_12:.*]] = load %[[VAL_4]] : <i1>
-// CHECK: yield %[[VAL_12]] : i1
+// CHECK: %[[VAL_12:.*]] = expression %[[VAL_5]] : (!emitc.lvalue<i1>) -> i1 {
+// CHECK: %[[VAL_13:.*]] = load %[[VAL_5]] : <i1>
+// CHECK: yield %[[VAL_13]] : i1
// CHECK: }
-// CHECK: yield %[[VAL_11]] : i1
+// CHECK: yield %[[VAL_12]] : i1
// CHECK: }
-// CHECK: %[[VAL_13:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
// CHECK: %[[VAL_14:.*]] = emitc.load %[[VAL_3]] : <i32>
-// CHECK: emitc.assign %[[VAL_14]] : i32 to %[[VAL_13]] : <i32>
-// CHECK: %[[VAL_15:.*]] = emitc.load %[[VAL_13]] : <i32>
-// CHECK: return %[[VAL_15]] : i32
+// CHECK: return %[[VAL_14]] : i32
// CHECK: }
emitc.func @payload_empty_after_region() -> i1 {
More information about the Mlir-commits
mailing list