Skip to content

[mlir][emitc] mark emitc.load with CExpression #130802

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 22, 2025

Conversation

kchibisov
Copy link
Contributor

Follow the call and call_opaque operations, as well as apply, which already are marked as CExpression even though they have side effects.

Even though emitc.load can be included inside the emitc.expression, the inlining and --form-expression pass won't actually inline them inside other expression due to it having a side effect, thus unless the user manually writes the emitc.load inside the emitc.expression it won't appear there.

--

It was brought #91475 (comment) and while there was some opposition due to load having a side effect, emitc already allows all the rest operations that have it, so for consistency reasons, enabling it doesn't really hurt from my point of view. Especially given that --form-expression doesn't allow
it to really inline inside other expressions, which makes sense, since if the users want such behavior, they should explicitly opt-in.

@llvmbot
Copy link
Member

llvmbot commented Mar 11, 2025

@llvm/pr-subscribers-mlir-emitc

@llvm/pr-subscribers-mlir

Author: Kirill Chibisov (kchibisov)

Changes

Follow the call and call_opaque operations, as well as apply, which already are marked as CExpression even though they have side effects.

Even though emitc.load can be included inside the emitc.expression, the inlining and --form-expression pass won't actually inline them inside other expression due to it having a side effect, thus unless the user manually writes the emitc.load inside the emitc.expression it won't appear there.

--

It was brought #91475 (comment) and while there was some opposition due to load having a side effect, emitc already allows all the rest operations that have it, so for consistency reasons, enabling it doesn't really hurt from my point of view. Especially given that --form-expression doesn't allow
it to really inline inside other expressions, which makes sense, since if the users want such behavior, they should explicitly opt-in.


Full diff: https://github.com/llvm/llvm-project/pull/130802.diff

3 Files Affected:

  • (modified) mlir/include/mlir/Dialect/EmitC/IR/EmitC.td (+1-1)
  • (modified) mlir/lib/Target/Cpp/TranslateToCpp.cpp (+1)
  • (modified) mlir/test/Dialect/EmitC/transforms.mlir (+23)
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index a518e7425956c..af2eed81e0d5b 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -927,7 +927,7 @@ def EmitC_LogicalOrOp : EmitC_BinaryOp<"logical_or", [CExpression]> {
   let assemblyFormat = "operands attr-dict `:` type(operands)";
 }
 
-def EmitC_LoadOp : EmitC_Op<"load", [
+def EmitC_LoadOp : EmitC_Op<"load", [CExpression,
   TypesMatchWith<"result type matches value type of 'operand'",
                   "operand", "result",
                   "::llvm::cast<LValueType>($_self).getValueType()">
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index b00820ffc542b..a3761ead4adde 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -100,6 +100,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::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/transforms.mlir b/mlir/test/Dialect/EmitC/transforms.mlir
index d204dec70d449..0678b4e5515c0 100644
--- a/mlir/test/Dialect/EmitC/transforms.mlir
+++ b/mlir/test/Dialect/EmitC/transforms.mlir
@@ -129,3 +129,26 @@ func.func @single_result_requirement() -> (i32, i32) {
   %0:2 = emitc.call_opaque "foo" () : () -> (i32, i32)
   return %0#0, %0#1 : i32, i32
 }
+
+// CHECK-LABEL: func.func @expression_with_load(
+// CHECK-SAME:      %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32) -> i1 {
+// CHECK:         %[[VAL_2:.*]] = "emitc.variable"() <{value = #emitc.opaque<"42">}> : () -> !emitc.lvalue<i32>
+// CHECK:         %[[VAL_3:.*]] = emitc.expression : i32 {
+// CHECK:           %[[VAL_4:.*]] = load %[[VAL_2]] : <i32>
+// CHECK:           yield %[[VAL_4]] : i32
+// CHECK:         }
+// CHECK:         %[[VAL_5:.*]] = emitc.expression : i1 {
+// CHECK:           %[[VAL_6:.*]] = add %[[VAL_3]], %[[VAL_1]] : (i32, i32) -> i32
+// CHECK:           %[[VAL_7:.*]] = cmp lt, %[[VAL_6]], %[[VAL_0]] : (i32, i32) -> i1
+// CHECK:           yield %[[VAL_7]] : i1
+// CHECK:         }
+// CHECK:         return %[[VAL_5]] : i1
+// CHECK:       }
+
+func.func @expression_with_load(%arg0: i32, %arg1: i32) -> i1 {
+  %0 = "emitc.variable"() <{value = #emitc.opaque<"42">}> : () -> !emitc.lvalue<i32>
+  %a = emitc.load %0 : !emitc.lvalue<i32>
+  %b = emitc.add %a, %arg1 : (i32, i32) -> i32
+  %c = emitc.cmp lt, %b, %arg0 :(i32, i32) -> i1
+  return %c : i1
+}

@mgehre-amd
Copy link
Contributor

Can you please add some tests for the output of translation? Interesting test case could also be loading from an array index, and then either having the emitc.subscript also within the CExpression or outside.

@simon-camp
Copy link

Thanks for addressing this.

Can you please add some tests for the output of translation? Interesting test case could also be loading from an array index, and then either having the emitc.subscript also within the CExpression or outside.

On top of that a translation test with a combination of a load inside an expression and a call_opaque op would be useful. I'm not sure if the precedence shouldn't be higher than that of any other op.

@kchibisov
Copy link
Contributor Author

Can you please add some tests for the output of translation? Interesting test case could also be loading from an array index,

Sure done.

and then either having the emitc.subscript also within the CExpression or outside

emitc.subscript is not a CExpression.

On top of that a translation test with a combination of a load inside an expression and a call_opaque op would be useful. I'm not sure if the precedence shouldn't be higher than that of any other op.

Sure, done. If it's not what you've expected, let me know.

Copy link
Contributor

@mgehre-amd mgehre-amd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Let's wait for @simon-camp to say whether the test for precedence is what he expected.

Copy link

@simon-camp simon-camp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks.

@kchibisov
Copy link
Contributor Author

kchibisov commented Mar 13, 2025

I've just noticed one inconsistency in the handling of CExpression inlining. If you look at hasSideEffects impl it does prevent uses of call_opaque and e.g. variable to not result in inlining, however, not e.g. subscript Op and regular call.

In general, variable right now is assumed to read local storage variable and can not really load from memory that may result in undefined behavior. Thus, I think the hasSideEffects should probably disallow subscript as it has side effect, and allow variable as loading from it doesn't have a side effect.

As for the call, I'm not quite sure how to deal with it and maybe keep as is or disallow all together.

Edit:

This also applies to member, but IIRC it acts as variable, but not member_of_ptr. There's also get_global, etc.

Follow the `call` and `call_opaque` operations, as well as `apply`,
which already are marked as `CExpression` even though they have side
effects.

Even though `emitc.load` can be included inside the `emitc.expression`,
the inlining and `--form-expression` pass won't actually inline them
inside other expression due to it having a side effect, thus unless
the user manually writes the `emitc.load` inside the `emitc.expression`
it won't appear there.

Also update the `hasSideEffects` to account for `LoadOp` instead of
`VariableOp`. The `VariableOp` was used before the `LoadOp` was
introduced, but at the present time, the `LoadOp` is what is being
used to actually load and emit the instruction that does the loading.
@kchibisov
Copy link
Contributor Author

@mgehre-amd @simon-camp I've updated PR to make a correct hasSideEffects check, since it does matter now. The old one was a left over from the time where Variable was the only way to define something that will actually have a loading.

The loosening of what kind of load is considered a side effect I'll do separately. The same with e.g. call if we want that.

@simon-camp
Copy link

I don't see theses changes, maybe you forgot to push your changes?

@kchibisov
Copy link
Contributor Author

Indeed, sorry. Will re-ping once I'll be able to push changes.

@kchibisov
Copy link
Contributor Author

kchibisov commented Mar 14, 2025

@simon-camp should be pushed now.

Copy link

@simon-camp simon-camp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

I wonder if we should upgrade the CExpression trait to an interface, so that the ops themselves can override a method. Another option could be to use MLIR MemoryEffects instead.

@kchibisov
Copy link
Contributor Author

I wonder if we should upgrade the CExpression trait to an interface, so that the ops themselves can override a method. Another option could be to use MLIR MemoryEffects instead.

I'll look into this for e.g. lowering the restrictions PR, but for this one, I think it's fine as is, since it was more of a bug fix.

@mgehre-amd anything else needed from my side?

@kchibisov
Copy link
Contributor Author

ping

Copy link
Contributor

@aniragil aniragil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay, this somehow slipped under my radar.
LGTM, thanks for handling this!

@kchibisov
Copy link
Contributor Author

ping

@marbre
Copy link
Member

marbre commented Apr 22, 2025

ping

If you don't have write access, I am happy to merge this for you, it that is what you're requesting.

@kchibisov
Copy link
Contributor Author

Yes, I don't have write access, so I need someone to merge.

@marbre marbre merged commit 0ca2d4d into llvm:main Apr 22, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants