diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td index d4aea52a0d485..2390c4ef24cbc 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td @@ -1345,7 +1345,7 @@ def EmitC_AssignOp : EmitC_Op<"assign", []> { } def EmitC_YieldOp : EmitC_Op<"yield", - [Pure, Terminator, ParentOneOf<["ExpressionOp", "IfOp", "ForOp", "SwitchOp"]>]> { + [Pure, Terminator, ParentOneOf<["DoOp", "ExpressionOp", "ForOp", "IfOp", "SwitchOp", "WhileOp"]>]> { let summary = "Block termination operation"; let description = [{ The `emitc.yield` terminates its parent EmitC op's region, optionally yielding @@ -1572,4 +1572,156 @@ def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects, let hasVerifier = 1; } +def EmitC_WhileOp : EmitC_Op<"while", + [HasOnlyGraphRegion, RecursiveMemoryEffects, NoRegionArguments, OpAsmOpInterface, NoTerminator]> { + let summary = "While operation"; + let description = [{ + The `emitc.while` operation represents a C/C++ while loop construct that + repeatedly executes a body region as long as a condition region evaluates to + true. The operation has two regions: + + 1. A condition region that must yield a boolean value (i1) + 2. A body region that contains the loop body + + The condition region is evaluated before each iteration. If it yields true, + the body region is executed. The loop terminates when the condition yields + false. The condition region must contain exactly one block that terminates + with an `emitc.yield` operation producing an i1 value. + + Example: + + ```mlir + emitc.func @foo(%arg0 : !emitc.ptr) { + %var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue + %0 = emitc.literal "10" : i32 + %1 = emitc.literal "1" : i32 + + emitc.while { + %var_load = load %var : + %res = emitc.cmp le, %var_load, %0 : (i32, i32) -> i1 + emitc.yield %res : i1 + } do { + emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr + %var_load = load %var : + %tmp_add = add %var_load, %1 : (i32, i32) -> i32 + "emitc.assign"(%var, %tmp_add) : (!emitc.lvalue, 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) { + %var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue + %0 = emitc.literal "10" : i32 + %1 = emitc.literal "1" : i32 + + emitc.do { + emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr + %var_load = load %var : + %tmp_add = add %var_load, %1 : (i32, i32) -> i32 + "emitc.assign"(%var, %tmp_add) : (!emitc.lvalue, i32) -> () + } while { + %var_load = load %var : + %res = emitc.cmp le, %var_load, %0 : (i32, i32) -> i1 + emitc.yield %res : i1 + } + + return + } + ``` + + ```c++ + // Code emitted for the operation above. + void foo(int32_t* v1) { + int32_t v2 = 0; + do { + printf("%d", *v1); + int32_t v3 = v2; + int32_t v4 = v3 + 1; + v2 = v4; + } while (v2 <= 10); + return; + } + ``` + }]; + + let arguments = (ins); + let results = (outs); + let regions = (region MaxSizedRegion<1>:$bodyRegion, + MaxSizedRegion<1>:$conditionRegion); + + let hasCustomAssemblyFormat = 1; + let hasVerifier = 1; + + let extraClassDeclaration = [{ + Operation *getRootOp(); + + //===------------------------------------------------------------------===// + // OpAsmOpInterface Methods + //===------------------------------------------------------------------===// + + /// EmitC ops in the body can omit their 'emitc.' prefix in the assembly. + static ::llvm::StringRef getDefaultDialect() { + return "emitc"; + } + }]; +} + #endif // MLIR_DIALECT_EMITC_IR_EMITC diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp index 345e8494194eb..96cf97613f78c 100644 --- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp +++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp @@ -333,11 +333,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 { + 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 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(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 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 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 &outVars, + Location loc, MLIRContext *context) { + emitc::OpaqueAttr noInit = emitc::OpaqueAttr::get(context, ""); + + for (Value init : whileOp.getInits()) { + emitc::VariableOp var = rewriter.create( + loc, emitc::LValueType::get(init.getType()), noInit); + rewriter.create(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 vars, + ConversionPatternRewriter &rewriter, + Location loc) const { + rewriter.setInsertionPointToStart(block); + + for (auto [arg, var] : llvm::zip(block->getArguments(), vars)) { + Type loadedType = cast(var.getType()).getValueType(); + Value load = rewriter.create(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 vars, + ConversionPatternRewriter &rewriter, + Location loc) const { + auto yieldOp = cast(terminator); + SmallVector yields(yieldOp.getOperands()); + rewriter.eraseOp(yieldOp); + + rewriter.setInsertionPointToEnd(yieldOp->getBlock()); + for (auto [var, val] : llvm::zip(vars, yields)) + rewriter.create(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 + finalizeLoopResults(ArrayRef resultVariables, + ArrayRef 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(var.getType()).getValueType(); + Value load = rewriter.create(loc, loadedType, var); + rewriter.create(loc, resultVar, load); + } + + // Replace op with loaded values to integrate with converted SSA graph. + SmallVector finalResults; + for (Value resultVar : resultVariables) { + Type loadedType = + cast(resultVar.getType()).getValueType(); + finalResults.push_back( + rewriter.create(loc, loadedType, resultVar)); + } + + return finalResults; + } + + // Direct lowering to emitc.while when condition arguments match region + // inputs. + LogicalResult lowerWhile(WhileOp whileOp, ArrayRef vars, + MLIRContext *context, + ConversionPatternRewriter &rewriter, + Location loc) const { + auto loweredWhile = rewriter.create(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(condTerminator); + rewriter.setInsertionPoint(condOp); + Value condition = rewriter.getRemappedValue(condOp.getCondition()); + rewriter.create(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 vars, + MLIRContext *context, + ConversionPatternRewriter &rewriter, + Location loc) const { + Type i1Type = IntegerType::get(context, 1); + auto globalCondition = + rewriter.create(loc, emitc::LValueType::get(i1Type), + emitc::OpaqueAttr::get(context, "")); + Value conditionVal = globalCondition.getResult(); + + auto loweredDo = rewriter.create(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(condTerminator); + rewriter.setInsertionPoint(condOp); + Value condition = rewriter.getRemappedValue(condOp.getCondition()); + rewriter.create(loc, conditionVal, condition); + + // Wrap body region in conditional to preserve scf semantics. + auto ifOp = rewriter.create(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(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(loc, i1Type, conditionVal); + rewriter.create(loc, cond); + + return success(); + } +}; + void mlir::populateSCFToEmitCConversionPatterns(RewritePatternSet &patterns, TypeConverter &typeConverter) { patterns.add(typeConverter, patterns.getContext()); patterns.add(typeConverter, patterns.getContext()); patterns.add(typeConverter, patterns.getContext()); + patterns.add(typeConverter, patterns.getContext()); } void SCFToEmitCPass::runOnOperation() { @@ -354,7 +587,8 @@ void SCFToEmitCPass::runOnOperation() { // Configure conversion to lower out SCF operations. ConversionTarget target(getContext()); - target.addIllegalOp(); + target + .addIllegalOp(); target.markUnknownOpDynamicallyLegal([](Operation *) { return true; }); if (failed( applyPartialConversion(getOperation(), target, std::move(patterns)))) diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp index 1709654b90138..70a7150b818ff 100644 --- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp +++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp @@ -900,10 +900,12 @@ LogicalResult emitc::YieldOp::verify() { Value result = getResult(); Operation *containingOp = getOperation()->getParentOp(); - if (result && containingOp->getNumResults() != 1) + if (result && containingOp->getNumResults() != 1 && + !isa(containingOp)) return emitOpError() << "yields a value not returned by parent"; - if (!result && containingOp->getNumResults() != 0) + if (!result && containingOp->getNumResults() != 0 && + !isa(containingOp)) return emitOpError() << "does not yield a value to be returned by parent"; return success(); @@ -1394,6 +1396,116 @@ void FileOp::build(OpBuilder &builder, OperationState &state, StringRef id) { builder.getNamedAttr("id", builder.getStringAttr(id))); } +//===----------------------------------------------------------------------===// +// Common functions for WhileOp and DoOp +//===----------------------------------------------------------------------===// + +static Operation *getRootOpFromLoopCondition(Region &condRegion) { + auto yieldOp = cast(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()) + return op.emitOpError( + "expected all operations in condition region must implement " + "CExpression trait, but ") + << inner.getName() << " does not"; + } + + auto condYield = dyn_cast(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(bodyBlock.back())) + if (bodyYield.getNumOperands() != 0) + return op.emitOpError( + "expected body region to return 0 values, but body returns ") + << bodyYield.getNumOperands(); + + return success(); +} + +static void printLoop(OpAsmPrinter &p, Operation *self, Region &first, + StringRef midKeyword, Region &second) { + p << ' '; + p.printRegion(first, /*printEntryBlockArgs=*/false); + p << ' ' << midKeyword << ' '; + p.printRegion(second); + p.printOptionalAttrDictWithKeyword(self->getAttrs()); +} + +static ParseResult parseLoop(OpAsmParser &parser, OperationState &res, + StringRef midKeyword) { + Region *firstRegion = res.addRegion(); + Region *secondRegion = res.addRegion(); + + if (parser.parseRegion(*firstRegion)) + return failure(); + if (parser.parseKeyword(midKeyword) || parser.parseRegion(*secondRegion)) + return failure(); + + return parser.parseOptionalAttrDictWithKeyword(res.attributes); +} + +//===----------------------------------------------------------------------===// +// WhileOp +//===----------------------------------------------------------------------===// + +Operation *WhileOp::getRootOp() { + return getRootOpFromLoopCondition(getConditionRegion()); +} + +void WhileOp::print(OpAsmPrinter &p) { + printLoop(p, getOperation(), getConditionRegion(), "do", getBodyRegion()); +} + +LogicalResult emitc::WhileOp::verify() { + return verifyLoopRegions(*getOperation(), getConditionRegion(), + getBodyRegion()); +} + +ParseResult WhileOp::parse(OpAsmParser &parser, OperationState &result) { + return parseLoop(parser, result, "do"); +} + +//===----------------------------------------------------------------------===// +// DoOp +//===----------------------------------------------------------------------===// + +Operation *DoOp::getRootOp() { + return getRootOpFromLoopCondition(getConditionRegion()); +} + +void DoOp::print(OpAsmPrinter &p) { + printLoop(p, getOperation(), getBodyRegion(), "while", getConditionRegion()); +} + +LogicalResult emitc::DoOp::verify() { + return verifyLoopRegions(*getOperation(), getConditionRegion(), + getBodyRegion()); +} + +ParseResult DoOp::parse(OpAsmParser &parser, OperationState &result) { + return parseLoop(parser, result, "while"); +} + //===----------------------------------------------------------------------===// // TableGen'd op method definitions //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index 5abc112ab8c7a..18f25303a34b4 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -180,6 +180,12 @@ struct CppEmitter { /// Emit an expression as a C expression. LogicalResult emitExpression(ExpressionOp expressionOp); + /// Emit while as a C while. + LogicalResult emitWhile(WhileOp expressionOp); + + /// Emit do-while as a C do-while. + LogicalResult emitDo(DoOp expressionOp); + /// Insert the expression representing the operation into the value cache. void cacheDeferredOpResult(Value value, StringRef str); @@ -198,6 +204,8 @@ struct CppEmitter { /// Return the existing or a new label of a Block. StringRef getOrCreateName(Block &block); + LogicalResult emitInlinedExpression(Value value); + /// Whether to map an mlir integer to a unsigned integer in C++. bool shouldMapToUnsigned(IntegerType::SignednessSemantics val); @@ -239,7 +247,7 @@ struct CppEmitter { } /// Get expression currently being emitted. - ExpressionOp getEmittedExpression() { return emittedExpression; } + Operation *getEmittedExpression() { return emittedExpression; } /// Determine whether given value is part of the expression potentially being /// emitted. @@ -249,7 +257,8 @@ struct CppEmitter { Operation *def = value.getDefiningOp(); if (!def) return false; - auto operandExpression = dyn_cast(def->getParentOp()); + + Operation *operandExpression = def->getParentOp(); return operandExpression == emittedExpression; }; @@ -280,7 +289,7 @@ struct CppEmitter { std::stack labelInScopeCount; /// State of the current expression being emitted. - ExpressionOp emittedExpression; + Operation *emittedExpression = nullptr; SmallVector emittedExpressionPrecedence; void pushExpressionPrecedence(int precedence) { @@ -510,6 +519,16 @@ static LogicalResult printOperation(CppEmitter &emitter, return success(); } +static LogicalResult printOperation(CppEmitter &emitter, + emitc::WhileOp whileOp) { + return emitter.emitWhile(whileOp); +} + +static LogicalResult printOperation(CppEmitter &emitter, + emitc::DoOp doWhileOp) { + return emitter.emitDo(doWhileOp); +} + static LogicalResult printOperation(CppEmitter &emitter, emitc::CmpOp cmpOp) { Operation *operation = cmpOp.getOperation(); @@ -1424,6 +1443,84 @@ LogicalResult CppEmitter::emitExpression(ExpressionOp expressionOp) { return success(); } +LogicalResult CppEmitter::emitWhile(WhileOp whileOp) { + assert(emittedExpressionPrecedence.empty() && + "Expected precedence stack to be empty"); + Operation *rootOp = whileOp.getRootOp(); + + emittedExpression = whileOp; + FailureOr 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(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(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 precedence = getOperatorPrecedence(rootOp); + if (failed(precedence)) + return failure(); + pushExpressionPrecedence(precedence.value()); + + if (failed(emitOperation(*rootOp, /*trailingSemicolon=*/false))) + return failure(); + os << ");"; + + popExpressionPrecedence(); + assert(emittedExpressionPrecedence.empty() && + "Expected precedence stack to be empty"); + emittedExpression = nullptr; + + return success(); +} + LogicalResult CppEmitter::emitOperand(Value value) { if (isPartOfCurrentExpression(value)) { Operation *def = value.getDefiningOp(); @@ -1607,12 +1704,13 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) { emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp, emitc::CallOpaqueOp, emitc::CastOp, emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp, emitc::DeclareFuncOp, - emitc::DivOp, emitc::ExpressionOp, emitc::FileOp, emitc::ForOp, - emitc::FuncOp, emitc::GlobalOp, emitc::IfOp, emitc::IncludeOp, - emitc::LoadOp, emitc::LogicalAndOp, emitc::LogicalNotOp, - emitc::LogicalOrOp, emitc::MulOp, emitc::RemOp, emitc::ReturnOp, - emitc::SubOp, emitc::SwitchOp, emitc::UnaryMinusOp, - emitc::UnaryPlusOp, emitc::VariableOp, emitc::VerbatimOp>( + emitc::DivOp, emitc::DoOp, emitc::ExpressionOp, emitc::FileOp, + emitc::ForOp, emitc::FuncOp, emitc::GlobalOp, emitc::IfOp, + emitc::IncludeOp, emitc::LoadOp, emitc::LogicalAndOp, + emitc::LogicalNotOp, emitc::LogicalOrOp, emitc::MulOp, + emitc::RemOp, emitc::ReturnOp, emitc::SubOp, emitc::SwitchOp, + emitc::UnaryMinusOp, emitc::UnaryPlusOp, emitc::VariableOp, + emitc::VerbatimOp, emitc::WhileOp>( [&](auto op) { return printOperation(*this, op); }) // Func ops. @@ -1656,9 +1754,9 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) { // Never emit a semicolon for some operations, especially if endening with // `}`. trailingSemicolon &= - !isa( - op); + !isa(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 +// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_3]] : +// CHECK: emitc.while { +// CHECK: %[[VAL_4:.*]] = load %[[VAL_3]] : +// 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]] : +// CHECK: %[[VAL_8:.*]] = call @payload_whileOp(%[[VAL_7]]) : (i32) -> i32 +// CHECK: assign %[[VAL_8]] : i32 to %[[VAL_3]] : +// CHECK: } +// CHECK: %[[VAL_9:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue +// CHECK: %[[VAL_10:.*]] = emitc.load %[[VAL_3]] : +// CHECK: emitc.assign %[[VAL_10]] : i32 to %[[VAL_9]] : +// CHECK: %[[VAL_11:.*]] = emitc.load %[[VAL_9]] : +// 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 +// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_3]] : +// CHECK: %[[VAL_4:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue +// CHECK: emitc.do { +// CHECK: %[[VAL_5:.*]] = load %[[VAL_3]] : +// 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]] : +// CHECK: if %[[VAL_7]] { +// CHECK: %[[VAL_9:.*]] = call @payload_doOp(%[[VAL_8]]) : (i32) -> i32 +// CHECK: assign %[[VAL_9]] : i32 to %[[VAL_3]] : +// CHECK: } +// CHECK: } while { +// CHECK: %[[VAL_10:.*]] = load %[[VAL_4]] : +// CHECK: yield %[[VAL_10]] : i1 +// CHECK: } +// CHECK: %[[VAL_11:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue +// CHECK: %[[VAL_12:.*]] = emitc.load %[[VAL_3]] : +// CHECK: emitc.assign %[[VAL_12]] : i32 to %[[VAL_11]] : +// CHECK: %[[VAL_13:.*]] = emitc.load %[[VAL_11]] : +// 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 +// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_2]] : +// CHECK: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue +// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_3]] : +// CHECK: emitc.while { +// CHECK: %[[VAL_4:.*]] = load %[[VAL_2]] : +// CHECK: %[[VAL_5:.*]] = load %[[VAL_3]] : +// 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]] : +// CHECK: %[[VAL_9:.*]] = load %[[VAL_3]] : +// 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]] : +// CHECK: assign %[[VAL_11]] : i32 to %[[VAL_3]] : +// CHECK: } +// CHECK: %[[VAL_12:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue +// CHECK: %[[VAL_13:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue +// CHECK: %[[VAL_14:.*]] = emitc.load %[[VAL_2]] : +// CHECK: emitc.assign %[[VAL_14]] : i32 to %[[VAL_12]] : +// CHECK: %[[VAL_15:.*]] = emitc.load %[[VAL_3]] : +// CHECK: emitc.assign %[[VAL_15]] : i32 to %[[VAL_13]] : +// CHECK: %[[VAL_16:.*]] = emitc.load %[[VAL_12]] : +// CHECK: %[[VAL_17:.*]] = emitc.load %[[VAL_13]] : +// 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 +// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_2]] : +// CHECK: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue +// CHECK: emitc.assign %[[VAL_0]] : i32 to %[[VAL_3]] : +// CHECK: %[[VAL_4:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue +// CHECK: emitc.do { +// CHECK: %[[VAL_5:.*]] = load %[[VAL_2]] : +// CHECK: %[[VAL_6:.*]] = load %[[VAL_3]] : +// 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]] : +// 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]] : +// CHECK: assign %[[VAL_10]] : i32 to %[[VAL_3]] : +// CHECK: } +// CHECK: } while { +// CHECK: %[[VAL_11:.*]] = load %[[VAL_4]] : +// CHECK: yield %[[VAL_11]] : i1 +// CHECK: } +// CHECK: %[[VAL_12:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue +// CHECK: %[[VAL_13:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue +// CHECK: %[[VAL_14:.*]] = emitc.load %[[VAL_2]] : +// CHECK: emitc.assign %[[VAL_14]] : i32 to %[[VAL_12]] : +// CHECK: %[[VAL_15:.*]] = emitc.load %[[VAL_3]] : +// CHECK: emitc.assign %[[VAL_15]] : i32 to %[[VAL_13]] : +// CHECK: %[[VAL_16:.*]] = emitc.load %[[VAL_12]] : +// CHECK: %[[VAL_17:.*]] = emitc.load %[[VAL_13]] : +// CHECK: return %[[VAL_16]] : i32 +// CHECK: } diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir index 3793dfe3f173b..acc8059658e34 100644 --- a/mlir/test/Dialect/EmitC/invalid_ops.mlir +++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir @@ -252,7 +252,7 @@ func.func @sub_pointer_pointer(%arg0: !emitc.ptr, %arg1: !emitc.ptr) { // ----- func.func @test_misplaced_yield() { - // expected-error @+1 {{'emitc.yield' op expects parent op to be one of 'emitc.expression, emitc.if, emitc.for, emitc.switch'}} + // expected-error @+1 {{'emitc.yield' op expects parent op to be one of 'emitc.do, emitc.expression, emitc.for, emitc.if, emitc.switch, emitc.while'}} emitc.yield return } @@ -654,3 +654,197 @@ func.func @test_verbatim(%arg0 : !emitc.ptr, %arg1 : i32) { emitc.verbatim "{a} " args %arg0, %arg1 : !emitc.ptr, i32 return } + +// ----- + +func.func @test_while(%arg0 : !emitc.ptr) { + // expected-error @+1 {{'emitc.while' op condition region cannot be empty}} + emitc.while { + } do { + emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr + } + + return +} + +// ----- + +emitc.func @test_while(%arg0 : !emitc.ptr) { + %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 + } + + return +} + +// ----- + +func.func @test_while(%arg0 : !emitc.ptr) { + %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 + } + + return +} + +// ----- + +func.func @test_while(%arg0 : !emitc.ptr) { + %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 + } + + 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) { + %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 + %4 = emitc.add %1, %2 : (i32, i32) -> i32 + emitc.yield %4 : i32 + } + + return +} + +// ----- + +func.func @test_do_while(%arg0 : !emitc.ptr) { + // expected-error @+1 {{'emitc.do' op condition region cannot be empty}} + emitc.do { + emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr + } while { + } + + return +} + +// ----- + +emitc.func @test_do_while(%arg0 : !emitc.ptr) { + %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 + } 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) { + %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 + } while { + %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1 + } + + return +} + +// ----- + +func.func @test_do_while(%arg0 : !emitc.ptr) { + %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 + } 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) { + %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 + %4 = emitc.add %1, %2 : (i32, i32) -> i32 + emitc.yield %4 : i32 + } while { + %3 = emitc.cmp eq, %1, %2 : (i32, i32) -> i1 + emitc.yield %3 : i1 + } + + return +} diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir index 36d12e763afc7..739340f566dc4 100644 --- a/mlir/test/Dialect/EmitC/ops.mlir +++ b/mlir/test/Dialect/EmitC/ops.mlir @@ -302,3 +302,35 @@ func.func @switch() { return } + +func.func @while(%arg0 : !emitc.ptr) { + %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 + } + + return +} + +func.func @do(%arg0 : !emitc.ptr) { + %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 + } 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) { + %var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue + %0 = emitc.literal "10" : i32 + %1 = emitc.literal "1" : i32 + + emitc.do { + emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr + %var_load = load %var : + %tmp_add = add %var_load, %1 : (i32, i32) -> i32 + "emitc.assign"(%var, %tmp_add) : (!emitc.lvalue, i32) -> () + } while { + %var_load = load %var : + %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) { + %var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue + %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 + %var_load = load %var : + %tmp_add = add %var_load, %1 : (i32, i32) -> i32 + "emitc.assign"(%var, %tmp_add) : (!emitc.lvalue, i32) -> () + } while { + %var_load = load %var : + %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) { + %var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue + %0 = emitc.literal "10" : i32 + %1 = emitc.literal "1" : i32 + + emitc.while { + %var_load = load %var : + %res = emitc.cmp le, %var_load, %0 : (i32, i32) -> i1 + emitc.yield %res : i1 + } do { + emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr + %var_load = load %var : + %tmp_add = add %var_load, %1 : (i32, i32) -> i32 + "emitc.assign"(%var, %tmp_add) : (!emitc.lvalue, 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) { + %var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue + %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 : + %res = emitc.cmp le, %var_load, %add : (i32, i32) -> i1 + emitc.yield %res : i1 + } do { + emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr + %var_load = load %var : + %tmp_add = add %var_load, %1 : (i32, i32) -> i32 + "emitc.assign"(%var, %tmp_add) : (!emitc.lvalue, i32) -> () + } + + return +}