From d2e6f155b679c931b0e5bdc23bfd67d22eccbef3 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 27 Aug 2025 12:25:15 +0300 Subject: [PATCH 1/5] [mlir][emitc] Mark emitc.literal with CExpression This PR marks `emitc.literal` as a valid operation within `emitc.expression`, removing an artificial restriction since literals are inherently inlined into expressions during emission. Example: ```mlir %0 = emitc.expression : i32 { %literal = emitc.literal "42" : i32 emitc.yield %literal : i32 } ``` --- mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 2 +- mlir/lib/Target/Cpp/TranslateToCpp.cpp | 23 ++++++++++------ mlir/test/Dialect/EmitC/form-expressions.mlir | 26 +++++++++++++++++++ mlir/test/Target/Cpp/expressions.mlir | 26 +++++++++++++++++++ 4 files changed, 68 insertions(+), 9 deletions(-) diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td index 721f9f6b320ad..316a4ddeb6822 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td @@ -873,7 +873,7 @@ def EmitC_IncludeOp let hasCustomAssemblyFormat = 1; } -def EmitC_LiteralOp : EmitC_Op<"literal", [Pure]> { +def EmitC_LiteralOp : EmitC_Op<"literal", [Pure, CExpressionInterface]> { let summary = "Literal operation"; let description = [{ The `emitc.literal` operation produces an SSA value equal to some constant diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index a5bd80e9d6b8b..d1639df1ae0bc 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -100,6 +100,7 @@ static FailureOr getOperatorPrecedence(Operation *operation) { .Case([&](auto op) { return 17; }) .Case([&](auto op) { return 13; }) .Case([&](auto op) { return 16; }) + .Case([&](auto op) { return 16; }) .Case([&](auto op) { return 4; }) .Case([&](auto op) { return 15; }) .Case([&](auto op) { return 3; }) @@ -452,6 +453,16 @@ static LogicalResult printOperation(CppEmitter &emitter, emitc::LoadOp loadOp) { return emitter.emitOperand(loadOp.getOperand()); } +static LogicalResult printOperation(CppEmitter &emitter, + emitc::LiteralOp literalOp) { + // If literalOp is used inside an expression, we treat it as an embedded one. + if (emitter.isPartOfCurrentExpression(literalOp.getResult())) + return emitter.ostream() << literalOp.getValue(), success(); + + emitter.cacheDeferredOpResult(literalOp.getResult(), literalOp.getValue()); + return success(); +} + static LogicalResult printBinaryOperation(CppEmitter &emitter, Operation *operation, StringRef binaryOperator) { @@ -1714,10 +1725,10 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) { emitc::DeclareFuncOp, emitc::DivOp, 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::LiteralOp, emitc::LogicalAndOp, emitc::LogicalNotOp, + emitc::LogicalOrOp, emitc::MulOp, emitc::RemOp, emitc::ReturnOp, + emitc::SubOp, emitc::SwitchOp, emitc::UnaryMinusOp, + emitc::UnaryPlusOp, emitc::VariableOp, emitc::VerbatimOp>( [&](auto op) { return printOperation(*this, op); }) // Func ops. @@ -1731,10 +1742,6 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) { cacheDeferredOpResult(op.getResult(), op.getFieldName()); return success(); }) - .Case([&](auto op) { - cacheDeferredOpResult(op.getResult(), op.getValue()); - return success(); - }) .Case([&](auto op) { cacheDeferredOpResult(op.getResult(), createMemberAccess(op)); return success(); diff --git a/mlir/test/Dialect/EmitC/form-expressions.mlir b/mlir/test/Dialect/EmitC/form-expressions.mlir index 7b6723989e260..f7f4c08f16593 100644 --- a/mlir/test/Dialect/EmitC/form-expressions.mlir +++ b/mlir/test/Dialect/EmitC/form-expressions.mlir @@ -208,3 +208,29 @@ func.func @expression_with_constant(%arg0: i32) -> i32 { %a = emitc.mul %arg0, %c42 : (i32, i32) -> i32 return %a : i32 } + +// CHECK-LABEL: func.func @expression_with_literal( +// CHECK-SAME: %[[ARG0:.*]]: i32) -> i1 { +// CHECK: %[[VAL_0:.*]] = emitc.expression : i32 { +// CHECK: %[[VAL_1:.*]] = literal "1" : i32 +// CHECK: yield %[[VAL_1]] : i32 +// CHECK: } +// CHECK: %[[VAL_2:.*]] = emitc.expression : i32 { +// CHECK: %[[VAL_3:.*]] = literal "2" : i32 +// CHECK: yield %[[VAL_3]] : i32 +// CHECK: } +// CHECK: %[[VAL_4:.*]] = emitc.expression : i1 { +// CHECK: %[[VAL_5:.*]] = add %[[VAL_0]], %[[VAL_2]] : (i32, i32) -> i32 +// CHECK: %[[VAL_6:.*]] = cmp lt, %[[VAL_5]], %[[ARG0]] : (i32, i32) -> i1 +// CHECK: yield %[[VAL_6]] : i1 +// CHECK: } +// CHECK: return %[[VAL_4]] : i1 +// CHECK: } + +func.func @expression_with_literal(%arg0: i32) -> i1 { + %literal1 = emitc.literal "1" : i32 + %literal2 = emitc.literal "2" : i32 + %b = emitc.add %literal1, %literal2 : (i32, i32) -> i32 + %c = emitc.cmp lt, %b, %arg0 :(i32, i32) -> i1 + return %c : i1 +} diff --git a/mlir/test/Target/Cpp/expressions.mlir b/mlir/test/Target/Cpp/expressions.mlir index 4281f41d0b3fb..aaf8acababaa9 100644 --- a/mlir/test/Target/Cpp/expressions.mlir +++ b/mlir/test/Target/Cpp/expressions.mlir @@ -458,3 +458,29 @@ emitc.func @expression_with_call_opaque_with_args_array(%0 : i32, %1 : i32) { } return } + + +// CPP-DEFAULT: bool expression_with_literal(int32_t [[VAL_1:v[0-9]+]]) { +// CPP-DEFAULT-NEXT: bool [[VAL_2:v[0-9]+]] = (1 + [[VAL_1]]) / 2 < 3; +// CPP-DEFAULT-NEXT: return [[VAL_2]]; +// CPP-DEFAULT-NEXT: } + +// CPP-DECLTOP: bool expression_with_literal(int32_t [[VAL_1:v[0-9]+]]) { +// CPP-DECLTOP-NEXT: bool [[VAL_2:v[0-9]+]]; +// CPP-DECLTOP-NEXT: [[VAL_2]] = (1 + [[VAL_1]]) / 2 < 3; +// CPP-DECLTOP-NEXT: return [[VAL_2]]; +// CPP-DECLTOP-NEXT: } + +func.func @expression_with_literal(%arg0 : i32) -> i1 { + %ret = emitc.expression noinline : i1 { + %literal1 = emitc.literal "1" : i32 + %literal2 = emitc.literal "2" : i32 + %add = add %literal1, %arg0 : (i32, i32) -> i32 + %div = div %add, %literal2 : (i32, i32) -> i32 + %literal3 = emitc.literal "3" : i32 + %f = emitc.cmp lt, %div, %literal3 :(i32, i32) -> i1 + emitc.yield %f : i1 + } + + return %ret : i1 +} From 0c3aabb6642e55130cf92986795a489ce9923602 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 27 Aug 2025 17:12:45 +0300 Subject: [PATCH 2/5] [mlir][emitc] Changes after review --- mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 6 ++++++ mlir/lib/Target/Cpp/TranslateToCpp.cpp | 2 +- mlir/test/Dialect/EmitC/form-expressions.mlir | 18 ++++++------------ mlir/test/Target/Cpp/expressions.mlir | 17 +++++++++++++++++ 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td index 316a4ddeb6822..ecd833bd9572e 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td @@ -896,6 +896,12 @@ def EmitC_LiteralOp : EmitC_Op<"literal", [Pure, CExpressionInterface]> { let hasVerifier = 1; let assemblyFormat = "$value attr-dict `:` type($result)"; + + let extraClassDeclaration = [{ + bool hasSideEffects() { + return false; + } + }]; } def EmitC_LogicalAndOp : EmitC_BinaryOp<"logical_and", []> { diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index d1639df1ae0bc..60ec5a7d487e8 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -100,7 +100,7 @@ static FailureOr getOperatorPrecedence(Operation *operation) { .Case([&](auto op) { return 17; }) .Case([&](auto op) { return 13; }) .Case([&](auto op) { return 16; }) - .Case([&](auto op) { return 16; }) + .Case([&](auto op) { return 17; }) .Case([&](auto op) { return 4; }) .Case([&](auto op) { return 15; }) .Case([&](auto op) { return 3; }) diff --git a/mlir/test/Dialect/EmitC/form-expressions.mlir b/mlir/test/Dialect/EmitC/form-expressions.mlir index f7f4c08f16593..2e81711eea706 100644 --- a/mlir/test/Dialect/EmitC/form-expressions.mlir +++ b/mlir/test/Dialect/EmitC/form-expressions.mlir @@ -211,20 +211,14 @@ func.func @expression_with_constant(%arg0: i32) -> i32 { // CHECK-LABEL: func.func @expression_with_literal( // CHECK-SAME: %[[ARG0:.*]]: i32) -> i1 { -// CHECK: %[[VAL_0:.*]] = emitc.expression : i32 { +// CHECK: %[[VAL_0:.*]] = emitc.expression : i1 { // CHECK: %[[VAL_1:.*]] = literal "1" : i32 -// CHECK: yield %[[VAL_1]] : i32 +// CHECK: %[[VAL_2:.*]] = literal "2" : i32 +// CHECK: %[[VAL_3:.*]] = add %[[VAL_1]], %[[VAL_2]] : (i32, i32) -> i32 +// CHECK: %[[VAL_4:.*]] = cmp lt, %[[VAL_3]], %[[ARG0]] : (i32, i32) -> i1 +// CHECK: yield %[[VAL_4]] : i1 // CHECK: } -// CHECK: %[[VAL_2:.*]] = emitc.expression : i32 { -// CHECK: %[[VAL_3:.*]] = literal "2" : i32 -// CHECK: yield %[[VAL_3]] : i32 -// CHECK: } -// CHECK: %[[VAL_4:.*]] = emitc.expression : i1 { -// CHECK: %[[VAL_5:.*]] = add %[[VAL_0]], %[[VAL_2]] : (i32, i32) -> i32 -// CHECK: %[[VAL_6:.*]] = cmp lt, %[[VAL_5]], %[[ARG0]] : (i32, i32) -> i1 -// CHECK: yield %[[VAL_6]] : i1 -// CHECK: } -// CHECK: return %[[VAL_4]] : i1 +// CHECK: return %[[VAL_0]] : i1 // CHECK: } func.func @expression_with_literal(%arg0: i32) -> i1 { diff --git a/mlir/test/Target/Cpp/expressions.mlir b/mlir/test/Target/Cpp/expressions.mlir index aaf8acababaa9..212d06bd6b70f 100644 --- a/mlir/test/Target/Cpp/expressions.mlir +++ b/mlir/test/Target/Cpp/expressions.mlir @@ -484,3 +484,20 @@ func.func @expression_with_literal(%arg0 : i32) -> i1 { return %ret : i1 } + + +// CPP-DEFAULT: int32_t single_literal_in_expression() { +// CPP-DEFAULT-NEXT: return 42; +// CPP-DEFAULT-NEXT: } + +// CPP-DECLTOP: int32_t single_literal_in_expression() { +// CPP-DECLTOP-NEXT: return 42; +// CPP-DECLTOP-NEXT: } + +func.func @single_literal_in_expression() -> i32 { + %result = emitc.expression : i32 { + %lit = emitc.literal "42" : i32 + emitc.yield %lit : i32 + } + return %result : i32 +} From 64af4c5af6232eb5ea0d20ddcf8fe9d00310f34f Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 3 Sep 2025 01:11:36 +0300 Subject: [PATCH 3/5] [mlir][emitc] Ignore literal in form-expression Literal is always inlined, so there's no need to form expressions with it as well. --- mlir/lib/Dialect/EmitC/Transforms/FormExpressions.cpp | 2 +- mlir/test/Dialect/EmitC/form-expressions.mlir | 3 +++ mlir/test/Target/Cpp/expressions.mlir | 10 +++++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/mlir/lib/Dialect/EmitC/Transforms/FormExpressions.cpp b/mlir/lib/Dialect/EmitC/Transforms/FormExpressions.cpp index 2f3e2618f4d74..4593a40631dc0 100644 --- a/mlir/lib/Dialect/EmitC/Transforms/FormExpressions.cpp +++ b/mlir/lib/Dialect/EmitC/Transforms/FormExpressions.cpp @@ -38,7 +38,7 @@ struct FormExpressionsPass auto matchFun = [&](Operation *op) { if (isa(*op) && !op->getParentOfType() && - op->getNumResults() == 1) + !isa(op) && op->getNumResults() == 1) createExpression(op, builder); }; rootOp->walk(matchFun); diff --git a/mlir/test/Dialect/EmitC/form-expressions.mlir b/mlir/test/Dialect/EmitC/form-expressions.mlir index 2e81711eea706..d4c2409815989 100644 --- a/mlir/test/Dialect/EmitC/form-expressions.mlir +++ b/mlir/test/Dialect/EmitC/form-expressions.mlir @@ -192,6 +192,7 @@ func.func @opaque_type_expression(%arg0: i32, %arg1: !emitc.opaque<"T0">, %arg2 %c = emitc.cmp lt, %b, %arg2 :(i32, i32) -> i1 return %c : i1 } +<<<<<<< HEAD // CHECK-LABEL: func.func @expression_with_constant( // CHECK-SAME: %[[VAL_0:.*]]: i32) -> i32 { @@ -228,3 +229,5 @@ func.func @expression_with_literal(%arg0: i32) -> i1 { %c = emitc.cmp lt, %b, %arg0 :(i32, i32) -> i1 return %c : i1 } +======= +>>>>>>> 6a27d24c62dd ([mlir][emitc] Ignore literal in form-expression) diff --git a/mlir/test/Target/Cpp/expressions.mlir b/mlir/test/Target/Cpp/expressions.mlir index 212d06bd6b70f..62252cbcce15b 100644 --- a/mlir/test/Target/Cpp/expressions.mlir +++ b/mlir/test/Target/Cpp/expressions.mlir @@ -472,7 +472,7 @@ emitc.func @expression_with_call_opaque_with_args_array(%0 : i32, %1 : i32) { // CPP-DECLTOP-NEXT: } func.func @expression_with_literal(%arg0 : i32) -> i1 { - %ret = emitc.expression noinline : i1 { + %ret = emitc.expression %arg0 noinline : (i32) -> i1 { %literal1 = emitc.literal "1" : i32 %literal2 = emitc.literal "2" : i32 %add = add %literal1, %arg0 : (i32, i32) -> i32 @@ -495,9 +495,9 @@ func.func @expression_with_literal(%arg0 : i32) -> i1 { // CPP-DECLTOP-NEXT: } func.func @single_literal_in_expression() -> i32 { - %result = emitc.expression : i32 { - %lit = emitc.literal "42" : i32 - emitc.yield %lit : i32 + %ret = emitc.expression : () -> i32 { + %literal = emitc.literal "42" : i32 + emitc.yield %literal : i32 } - return %result : i32 + return %ret : i32 } From 0a5c0c351a2371c92dbd0fc89de92618a2e53dad Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 3 Sep 2025 15:31:12 +0300 Subject: [PATCH 4/5] [mlir][emitc] Reorder match conditions for efficiency --- mlir/lib/Dialect/EmitC/Transforms/FormExpressions.cpp | 6 +++--- mlir/lib/Target/Cpp/TranslateToCpp.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mlir/lib/Dialect/EmitC/Transforms/FormExpressions.cpp b/mlir/lib/Dialect/EmitC/Transforms/FormExpressions.cpp index 4593a40631dc0..b6fee09770ee7 100644 --- a/mlir/lib/Dialect/EmitC/Transforms/FormExpressions.cpp +++ b/mlir/lib/Dialect/EmitC/Transforms/FormExpressions.cpp @@ -36,9 +36,9 @@ struct FormExpressionsPass // Wrap each C operator op with an expression op. OpBuilder builder(context); auto matchFun = [&](Operation *op) { - if (isa(*op) && - !op->getParentOfType() && - !isa(op) && op->getNumResults() == 1) + if (op->getNumResults() == 1 && !isa(op) && + isa(*op) && + !op->getParentOfType()) createExpression(op, builder); }; rootOp->walk(matchFun); diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index 60ec5a7d487e8..ec8c03c04244f 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -455,7 +455,7 @@ static LogicalResult printOperation(CppEmitter &emitter, emitc::LoadOp loadOp) { static LogicalResult printOperation(CppEmitter &emitter, emitc::LiteralOp literalOp) { - // If literalOp is used inside an expression, we treat it as an embedded one. + // If literalOp is used inside an expression, we treat it as an inlined one. if (emitter.isPartOfCurrentExpression(literalOp.getResult())) return emitter.ostream() << literalOp.getValue(), success(); From 3af37cb3dd7ad1ffdcf120a9f513e425ef4d9130 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Sun, 7 Sep 2025 17:38:54 +0300 Subject: [PATCH 5/5] [mlir][emitc] Corrected the test --- mlir/test/Dialect/EmitC/form-expressions.mlir | 23 ------------------- mlir/test/Target/Cpp/expressions.mlir | 5 ++-- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/mlir/test/Dialect/EmitC/form-expressions.mlir b/mlir/test/Dialect/EmitC/form-expressions.mlir index d4c2409815989..7b6723989e260 100644 --- a/mlir/test/Dialect/EmitC/form-expressions.mlir +++ b/mlir/test/Dialect/EmitC/form-expressions.mlir @@ -192,7 +192,6 @@ func.func @opaque_type_expression(%arg0: i32, %arg1: !emitc.opaque<"T0">, %arg2 %c = emitc.cmp lt, %b, %arg2 :(i32, i32) -> i1 return %c : i1 } -<<<<<<< HEAD // CHECK-LABEL: func.func @expression_with_constant( // CHECK-SAME: %[[VAL_0:.*]]: i32) -> i32 { @@ -209,25 +208,3 @@ func.func @expression_with_constant(%arg0: i32) -> i32 { %a = emitc.mul %arg0, %c42 : (i32, i32) -> i32 return %a : i32 } - -// CHECK-LABEL: func.func @expression_with_literal( -// CHECK-SAME: %[[ARG0:.*]]: i32) -> i1 { -// CHECK: %[[VAL_0:.*]] = emitc.expression : i1 { -// CHECK: %[[VAL_1:.*]] = literal "1" : i32 -// CHECK: %[[VAL_2:.*]] = literal "2" : i32 -// CHECK: %[[VAL_3:.*]] = add %[[VAL_1]], %[[VAL_2]] : (i32, i32) -> i32 -// CHECK: %[[VAL_4:.*]] = cmp lt, %[[VAL_3]], %[[ARG0]] : (i32, i32) -> i1 -// CHECK: yield %[[VAL_4]] : i1 -// CHECK: } -// CHECK: return %[[VAL_0]] : i1 -// CHECK: } - -func.func @expression_with_literal(%arg0: i32) -> i1 { - %literal1 = emitc.literal "1" : i32 - %literal2 = emitc.literal "2" : i32 - %b = emitc.add %literal1, %literal2 : (i32, i32) -> i32 - %c = emitc.cmp lt, %b, %arg0 :(i32, i32) -> i1 - return %c : i1 -} -======= ->>>>>>> 6a27d24c62dd ([mlir][emitc] Ignore literal in form-expression) diff --git a/mlir/test/Target/Cpp/expressions.mlir b/mlir/test/Target/Cpp/expressions.mlir index 62252cbcce15b..09748505d3563 100644 --- a/mlir/test/Target/Cpp/expressions.mlir +++ b/mlir/test/Target/Cpp/expressions.mlir @@ -478,10 +478,9 @@ func.func @expression_with_literal(%arg0 : i32) -> i1 { %add = add %literal1, %arg0 : (i32, i32) -> i32 %div = div %add, %literal2 : (i32, i32) -> i32 %literal3 = emitc.literal "3" : i32 - %f = emitc.cmp lt, %div, %literal3 :(i32, i32) -> i1 - emitc.yield %f : i1 + %cmp = emitc.cmp lt, %div, %literal3 : (i32, i32) -> i1 + emitc.yield %cmp : i1 } - return %ret : i1 }