[Mlir-commits] [mlir] [mlir][emitc] Mark emitc.literal with CExpression (PR #155582)
Vlad Lazar
llvmlistbot at llvm.org
Wed Sep 3 05:31:29 PDT 2025
https://github.com/Vladislave0-0 updated https://github.com/llvm/llvm-project/pull/155582
>From 17294da69edde1b24d873ef2786ff4837818c3be Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Wed, 27 Aug 2025 12:25:15 +0300
Subject: [PATCH 1/4] [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 fb7a108b39fc0..8bb3b30392ed1 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -864,7 +864,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 570f38c60020b..c6383fc25c064 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -99,6 +99,7 @@ static FailureOr<int> getOperatorPrecedence(Operation *operation) {
.Case<emitc::ConditionalOp>([&](auto op) { return 2; })
.Case<emitc::DivOp>([&](auto op) { return 13; })
.Case<emitc::LoadOp>([&](auto op) { return 16; })
+ .Case<emitc::LiteralOp>([&](auto op) { return 16; })
.Case<emitc::LogicalAndOp>([&](auto op) { return 4; })
.Case<emitc::LogicalNotOp>([&](auto op) { return 15; })
.Case<emitc::LogicalOrOp>([&](auto op) { return 3; })
@@ -442,6 +443,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) {
@@ -1704,10 +1715,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.
@@ -1721,10 +1732,6 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
cacheDeferredOpResult(op.getResult(), op.getFieldName());
return success();
})
- .Case<emitc::LiteralOp>([&](auto op) {
- cacheDeferredOpResult(op.getResult(), op.getValue());
- return success();
- })
.Case<emitc::MemberOp>([&](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 67cd6fddba638..0670fa2e466da 100644
--- a/mlir/test/Dialect/EmitC/form-expressions.mlir
+++ b/mlir/test/Dialect/EmitC/form-expressions.mlir
@@ -183,3 +183,29 @@ func.func @opaque_type_expression(%arg0: i32, %arg1: !emitc.opaque<"T0">, %arg2
%c = emitc.cmp lt, %b, %arg2 :(i32, i32) -> i1
return %c : i1
}
+
+// 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 433a67ccb3f39..8be4523fd6ba1 100644
--- a/mlir/test/Target/Cpp/expressions.mlir
+++ b/mlir/test/Target/Cpp/expressions.mlir
@@ -439,3 +439,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 7b7538ea500c10a488efe1377f305be10276ff6f Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Wed, 27 Aug 2025 17:12:45 +0300
Subject: [PATCH 2/4] [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 8bb3b30392ed1..bcd716f9efe54 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -887,6 +887,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 c6383fc25c064..2cb6d30f6a2ba 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -99,7 +99,7 @@ static FailureOr<int> getOperatorPrecedence(Operation *operation) {
.Case<emitc::ConditionalOp>([&](auto op) { return 2; })
.Case<emitc::DivOp>([&](auto op) { return 13; })
.Case<emitc::LoadOp>([&](auto op) { return 16; })
- .Case<emitc::LiteralOp>([&](auto op) { return 16; })
+ .Case<emitc::LiteralOp>([&](auto op) { return 17; })
.Case<emitc::LogicalAndOp>([&](auto op) { return 4; })
.Case<emitc::LogicalNotOp>([&](auto op) { return 15; })
.Case<emitc::LogicalOrOp>([&](auto op) { return 3; })
diff --git a/mlir/test/Dialect/EmitC/form-expressions.mlir b/mlir/test/Dialect/EmitC/form-expressions.mlir
index 0670fa2e466da..3e53ad287359f 100644
--- a/mlir/test/Dialect/EmitC/form-expressions.mlir
+++ b/mlir/test/Dialect/EmitC/form-expressions.mlir
@@ -186,20 +186,14 @@ func.func @opaque_type_expression(%arg0: i32, %arg1: !emitc.opaque<"T0">, %arg2
// 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 8be4523fd6ba1..b3ab712038bc1 100644
--- a/mlir/test/Target/Cpp/expressions.mlir
+++ b/mlir/test/Target/Cpp/expressions.mlir
@@ -465,3 +465,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 6a27d24c62dd2357b67eeb3d5a006e8695bb9ca9 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Wed, 3 Sep 2025 01:11:36 +0300
Subject: [PATCH 3/4] [mlir][emitc] Ignore literal in form-expression
Literal is always inlined, so there's no need to form expressions
with it as well.
---
.../EmitC/Transforms/FormExpressions.cpp | 2 +-
mlir/test/Dialect/EmitC/form-expressions.mlir | 20 -------------------
mlir/test/Target/Cpp/expressions.mlir | 10 +++++-----
3 files changed, 6 insertions(+), 26 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<emitc::CExpressionInterface>(*op) &&
!op->getParentOfType<emitc::ExpressionOp>() &&
- op->getNumResults() == 1)
+ !isa<emitc::LiteralOp>(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 3e53ad287359f..67cd6fddba638 100644
--- a/mlir/test/Dialect/EmitC/form-expressions.mlir
+++ b/mlir/test/Dialect/EmitC/form-expressions.mlir
@@ -183,23 +183,3 @@ func.func @opaque_type_expression(%arg0: i32, %arg1: !emitc.opaque<"T0">, %arg2
%c = emitc.cmp lt, %b, %arg2 :(i32, i32) -> i1
return %c : i1
}
-
-// 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
-}
diff --git a/mlir/test/Target/Cpp/expressions.mlir b/mlir/test/Target/Cpp/expressions.mlir
index b3ab712038bc1..d84a5caa49adf 100644
--- a/mlir/test/Target/Cpp/expressions.mlir
+++ b/mlir/test/Target/Cpp/expressions.mlir
@@ -453,7 +453,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
@@ -476,9 +476,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 7cffbec90dbe25eae8a373f8f41d3a1c40fbae14 Mon Sep 17 00:00:00 2001
From: Vlad Lazar <lazar_2004 at list.ru>
Date: Wed, 3 Sep 2025 15:31:12 +0300
Subject: [PATCH 4/4] [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<emitc::CExpressionInterface>(*op) &&
- !op->getParentOfType<emitc::ExpressionOp>() &&
- !isa<emitc::LiteralOp>(op) && op->getNumResults() == 1)
+ if (op->getNumResults() == 1 && !isa<emitc::LiteralOp>(op) &&
+ isa<emitc::CExpressionInterface>(*op) &&
+ !op->getParentOfType<emitc::ExpressionOp>())
createExpression(op, builder);
};
rootOp->walk(matchFun);
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 2cb6d30f6a2ba..295e556bb3474 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -445,7 +445,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();
More information about the Mlir-commits
mailing list