[Mlir-commits] [flang] [mlir] [flang][acc] Handle ViewLike ops with OutlineRematerializationOpInterface in OffloadLiveInValueCanonicalization (PR #184218)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Mar 5 08:55:49 PST 2026


https://github.com/khaki3 updated https://github.com/llvm/llvm-project/pull/184218

>From 596eafb6a23de8a75f94801c3c260f289fa997c3 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Mon, 2 Mar 2026 12:16:48 -0800
Subject: [PATCH 1/8] [acc] Rematerialize view-like ops with
 OutlineRematerializationOpInterface

Check OutlineRematerializationOpInterface on the direct defining op
before tracing through ViewLikeOpInterface in
OffloadLiveInValueCanonicalization. An op may implement both interfaces
(e.g. fir.convert), and tracing first would reach a block argument,
missing the rematerialization.

Also register fir::ConvertOp with OutlineRematerializationOpInterface
so that CSE merging fir.convert across ACC region boundaries does not
prevent ACCImplicitData from mapping the original pointer.

Made-with: Cursor
---
 .../Support/RegisterOpenACCExtensions.cpp     |  2 +
 .../offload-livein-value-canonicalization.fir | 42 +++++++++++++++++++
 .../OffloadLiveInValueCanonicalization.cpp    | 11 +++++
 3 files changed, 55 insertions(+)

diff --git a/flang/lib/Optimizer/OpenACC/Support/RegisterOpenACCExtensions.cpp b/flang/lib/Optimizer/OpenACC/Support/RegisterOpenACCExtensions.cpp
index d7fadbc84ff1a..a994f30a6dd76 100644
--- a/flang/lib/Optimizer/OpenACC/Support/RegisterOpenACCExtensions.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/RegisterOpenACCExtensions.cpp
@@ -77,6 +77,8 @@ void registerOpenACCExtensions(mlir::DialectRegistry &registry) {
         *ctx);
     fir::FieldIndexOp::attachInterface<
         OutlineRematerializationModel<fir::FieldIndexOp>>(*ctx);
+    fir::ConvertOp::attachInterface<
+        OutlineRematerializationModel<fir::ConvertOp>>(*ctx);
   });
 
   // Register HLFIR operation interfaces
diff --git a/flang/test/Fir/OpenACC/offload-livein-value-canonicalization.fir b/flang/test/Fir/OpenACC/offload-livein-value-canonicalization.fir
index 6ecccde39d3fb..d9313a1f933bd 100644
--- a/flang/test/Fir/OpenACC/offload-livein-value-canonicalization.fir
+++ b/flang/test/Fir/OpenACC/offload-livein-value-canonicalization.fir
@@ -253,3 +253,45 @@ func.func @test_accbounds_rematerialize_fir() {
 // CHECK: acc.bounds
 // CHECK: acc.serial {
 // CHECK:   acc.bounds
+
+// -----
+
+// Test fir.convert rematerialization (ViewLikeOpInterface +
+// OutlineRematerializationOpInterface).
+func.func private @use_i64(i64) -> ()
+
+func.func @test_convert_rematerialize(%arg0: !fir.ref<i32>) {
+  %0 = fir.convert %arg0 : (!fir.ref<i32>) -> i64
+  fir.call @use_i64(%0) : (i64) -> ()
+  acc.parallel {
+    fir.call @use_i64(%0) : (i64) -> ()
+    acc.yield
+  }
+  return
+}
+
+// CHECK-LABEL: @test_convert_rematerialize
+// CHECK: %[[CVT_OUTER:.*]] = fir.convert
+// CHECK: fir.call @use_i64(%[[CVT_OUTER]])
+// CHECK: acc.parallel {
+// CHECK:   %[[CVT_INNER:.*]] = fir.convert
+// CHECK:   fir.call @use_i64(%[[CVT_INNER]])
+
+// -----
+
+// Test fir.convert sinking (only used inside region).
+func.func private @use_i64(i64) -> ()
+
+func.func @test_convert_sink(%arg0: !fir.ref<i32>) {
+  %0 = fir.convert %arg0 : (!fir.ref<i32>) -> i64
+  acc.parallel {
+    fir.call @use_i64(%0) : (i64) -> ()
+    acc.yield
+  }
+  return
+}
+
+// CHECK-LABEL: @test_convert_sink
+// CHECK: acc.parallel {
+// CHECK:   %[[CVT:.*]] = fir.convert
+// CHECK:   fir.call @use_i64(%[[CVT]])
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
index ea7ee715189e3..851096b755f80 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
@@ -132,6 +132,17 @@ static Value getOriginalValue(Value val) {
 /// to find the original defining operation before making the determination.
 static bool isRematerializationCandidate(Value val,
                                          acc::OpenACCSupport &accSupport) {
+  // Check before tracing through view-like ops: an op may implement both
+  // ViewLikeOpInterface and OutlineRematerializationOpInterface.
+  if (Operation *directDefOp = val.getDefiningOp()) {
+    if (isa<acc::OutlineRematerializationOpInterface>(directDefOp)) {
+      LLVM_DEBUG(llvm::dbgs()
+                 << "\tDirect OutlineRematerializationOpInterface: "
+                 << *directDefOp << "\n");
+      return true;
+    }
+  }
+
   // Trace through view-like operations to find the original value.
   Value origVal = getOriginalValue(val);
   Operation *definingOp = origVal.getDefiningOp();

>From dd4fd06a17576318e5b0cf1fb785cef493e7860c Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Mon, 2 Mar 2026 12:27:49 -0800
Subject: [PATCH 2/8] Remove redundant OutlineRematerializationOpInterface
 check

Made-with: Cursor
---
 .../Transforms/OffloadLiveInValueCanonicalization.cpp       | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
index 851096b755f80..426f63f7508b3 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
@@ -157,12 +157,6 @@ static bool isRematerializationCandidate(Value val,
     return true;
   }
 
-  // Operations implementing OutlineRematerializationOpInterface are candidates.
-  if (isa<acc::OutlineRematerializationOpInterface>(definingOp)) {
-    LLVM_DEBUG(llvm::dbgs() << "\t\t-> OutlineRematerializationOpInterface\n");
-    return true;
-  }
-
   // Address-of operations referencing globals that are valid in GPU regions
   // or referencing constant globals should be rematerialized.
   if (auto addrOfOp = dyn_cast<acc::AddressOfGlobalOpInterface>(definingOp)) {

>From 1bca9e1da880e21190132269c87498fed2716b88 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Mon, 2 Mar 2026 12:52:19 -0800
Subject: [PATCH 3/8] Fallback to direct defining op when traced original is a
 block argument

Made-with: Cursor
---
 .../OffloadLiveInValueCanonicalization.cpp    | 23 ++++++++++---------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
index 426f63f7508b3..4fc7a49b04950 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
@@ -111,6 +111,10 @@ static Value getOriginalValue(Value val) {
   Value prev;
   while (val && val != prev) {
     prev = val;
+    // Stop tracing if the op is a rematerialization candidate itself.
+    if (isa_and_nonnull<acc::OutlineRematerializationOpInterface>(
+            val.getDefiningOp()))
+      break;
     if (auto viewLikeOp = val.getDefiningOp<ViewLikeOpInterface>())
       val = viewLikeOp.getViewSource();
     if (auto partialAccess =
@@ -132,20 +136,11 @@ static Value getOriginalValue(Value val) {
 /// to find the original defining operation before making the determination.
 static bool isRematerializationCandidate(Value val,
                                          acc::OpenACCSupport &accSupport) {
-  // Check before tracing through view-like ops: an op may implement both
-  // ViewLikeOpInterface and OutlineRematerializationOpInterface.
-  if (Operation *directDefOp = val.getDefiningOp()) {
-    if (isa<acc::OutlineRematerializationOpInterface>(directDefOp)) {
-      LLVM_DEBUG(llvm::dbgs()
-                 << "\tDirect OutlineRematerializationOpInterface: "
-                 << *directDefOp << "\n");
-      return true;
-    }
-  }
-
   // Trace through view-like operations to find the original value.
   Value origVal = getOriginalValue(val);
   Operation *definingOp = origVal.getDefiningOp();
+  if (!definingOp)
+    definingOp = val.getDefiningOp();
   if (!definingOp)
     return false;
 
@@ -157,6 +152,12 @@ static bool isRematerializationCandidate(Value val,
     return true;
   }
 
+  // Operations implementing OutlineRematerializationOpInterface are candidates.
+  if (isa<acc::OutlineRematerializationOpInterface>(definingOp)) {
+    LLVM_DEBUG(llvm::dbgs() << "\t\t-> OutlineRematerializationOpInterface\n");
+    return true;
+  }
+
   // Address-of operations referencing globals that are valid in GPU regions
   // or referencing constant globals should be rematerialized.
   if (auto addrOfOp = dyn_cast<acc::AddressOfGlobalOpInterface>(definingOp)) {

>From e5d52b8ab4074b238697f2ce10855195bf561b22 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Mon, 2 Mar 2026 12:53:09 -0800
Subject: [PATCH 4/8] Remove unnecessary getOriginalValue break

Made-with: Cursor
---
 .../OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
index 4fc7a49b04950..b1f3191bdb68b 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
@@ -111,10 +111,6 @@ static Value getOriginalValue(Value val) {
   Value prev;
   while (val && val != prev) {
     prev = val;
-    // Stop tracing if the op is a rematerialization candidate itself.
-    if (isa_and_nonnull<acc::OutlineRematerializationOpInterface>(
-            val.getDefiningOp()))
-      break;
     if (auto viewLikeOp = val.getDefiningOp<ViewLikeOpInterface>())
       val = viewLikeOp.getViewSource();
     if (auto partialAccess =

>From 72a839fb44083e27f5dda370a4491fbcb7af6025 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Wed, 4 Mar 2026 12:45:06 -0800
Subject: [PATCH 5/8] Fall back to direct defining op for
 ViewLike+Rematerialization ops

When an op implements both ViewLikeOpInterface and
OutlineRematerializationOpInterface (e.g., fir.convert), getOriginalValue
traces through it. If the traced op is not a candidate, fall back to
checking the direct defining op. This handles all input types (block
arguments, fir.alloca, fir.call, etc.).

Made-with: Cursor
---
 .../offload-livein-value-canonicalization.fir | 45 +++++++++++++++++++
 .../OffloadLiveInValueCanonicalization.cpp    | 18 ++++++--
 2 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/flang/test/Fir/OpenACC/offload-livein-value-canonicalization.fir b/flang/test/Fir/OpenACC/offload-livein-value-canonicalization.fir
index d9313a1f933bd..04bf123dc3224 100644
--- a/flang/test/Fir/OpenACC/offload-livein-value-canonicalization.fir
+++ b/flang/test/Fir/OpenACC/offload-livein-value-canonicalization.fir
@@ -295,3 +295,48 @@ func.func @test_convert_sink(%arg0: !fir.ref<i32>) {
 // CHECK: acc.parallel {
 // CHECK:   %[[CVT:.*]] = fir.convert
 // CHECK:   fir.call @use_i64(%[[CVT]])
+
+// -----
+
+// Test fir.convert sinking when input is fir.alloca (not a block argument).
+func.func private @use_i64(i64) -> ()
+
+func.func @test_convert_alloca_sink() {
+  %0 = fir.alloca i32
+  %1 = fir.convert %0 : (!fir.ref<i32>) -> i64
+  acc.parallel {
+    fir.call @use_i64(%1) : (i64) -> ()
+    acc.yield
+  }
+  return
+}
+
+// CHECK-LABEL: @test_convert_alloca_sink
+// CHECK: %[[ALLOCA:.*]] = fir.alloca i32
+// CHECK: acc.parallel {
+// CHECK:   %[[CVT:.*]] = fir.convert %[[ALLOCA]]
+// CHECK:   fir.call @use_i64(%[[CVT]])
+
+// -----
+
+// Test fir.convert rematerialization when input is fir.alloca.
+func.func private @use_i64(i64) -> ()
+
+func.func @test_convert_alloca_remat() {
+  %0 = fir.alloca i32
+  %1 = fir.convert %0 : (!fir.ref<i32>) -> i64
+  fir.call @use_i64(%1) : (i64) -> ()
+  acc.parallel {
+    fir.call @use_i64(%1) : (i64) -> ()
+    acc.yield
+  }
+  return
+}
+
+// CHECK-LABEL: @test_convert_alloca_remat
+// CHECK: %[[ALLOCA:.*]] = fir.alloca i32
+// CHECK: %[[CVT_OUTER:.*]] = fir.convert %[[ALLOCA]]
+// CHECK: fir.call @use_i64(%[[CVT_OUTER]])
+// CHECK: acc.parallel {
+// CHECK:   %[[CVT_INNER:.*]] = fir.convert %[[ALLOCA]]
+// CHECK:   fir.call @use_i64(%[[CVT_INNER]])
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
index b1f3191bdb68b..35657d3b28efb 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
@@ -135,9 +135,7 @@ static bool isRematerializationCandidate(Value val,
   // Trace through view-like operations to find the original value.
   Value origVal = getOriginalValue(val);
   Operation *definingOp = origVal.getDefiningOp();
-  if (!definingOp)
-    definingOp = val.getDefiningOp();
-  if (!definingOp)
+  if (!definingOp && !(definingOp = val.getDefiningOp()))
     return false;
 
   LLVM_DEBUG(llvm::dbgs() << "\tChecking candidate: " << *definingOp << "\n");
@@ -183,6 +181,20 @@ static bool isRematerializationCandidate(Value val,
     }
   }
 
+  // An op implementing ViewLikeOpInterface may also implement
+  // OutlineRematerializationOpInterface (e.g., fir.convert). When
+  // getOriginalValue traces through it, the traced op may not be a candidate.
+  // Fall back to the direct defining op.
+  if (origVal != val) {
+    definingOp = val.getDefiningOp();
+    if (definingOp &&
+        isa<acc::OutlineRematerializationOpInterface>(definingOp)) {
+      LLVM_DEBUG(llvm::dbgs()
+                 << "\t\t-> OutlineRematerializationOpInterface (direct)\n");
+      return true;
+    }
+  }
+
   LLVM_DEBUG(llvm::dbgs() << "\t\t-> not a candidate\n");
   return false;
 }

>From 1de2eb3214c83b0e587e6ca198fb254fa7a93946 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Wed, 4 Mar 2026 14:02:58 -0800
Subject: [PATCH 6/8] Stop tracing at OutlineRematerializationOpInterface in
 getOriginalValue

Made-with: Cursor
---
 .../OffloadLiveInValueCanonicalization.cpp    | 23 ++++++-------------
 1 file changed, 7 insertions(+), 16 deletions(-)

diff --git a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
index 35657d3b28efb..d0c6bc3f0c423 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
@@ -106,11 +106,16 @@ static bool allUsersAreInsideRegion(Value val, Region &region) {
 }
 
 /// Traces through view-like and partial entity access operations to find the
-/// original defining value.
+/// original defining value. Stops tracing at ops that implement
+/// OutlineRematerializationOpInterface, since those ops are rematerialization
+/// candidates themselves and should not be traced through.
 static Value getOriginalValue(Value val) {
   Value prev;
   while (val && val != prev) {
     prev = val;
+    if (isa_and_nonnull<acc::OutlineRematerializationOpInterface>(
+            val.getDefiningOp()))
+      break;
     if (auto viewLikeOp = val.getDefiningOp<ViewLikeOpInterface>())
       val = viewLikeOp.getViewSource();
     if (auto partialAccess =
@@ -135,7 +140,7 @@ static bool isRematerializationCandidate(Value val,
   // Trace through view-like operations to find the original value.
   Value origVal = getOriginalValue(val);
   Operation *definingOp = origVal.getDefiningOp();
-  if (!definingOp && !(definingOp = val.getDefiningOp()))
+  if (!definingOp)
     return false;
 
   LLVM_DEBUG(llvm::dbgs() << "\tChecking candidate: " << *definingOp << "\n");
@@ -181,20 +186,6 @@ static bool isRematerializationCandidate(Value val,
     }
   }
 
-  // An op implementing ViewLikeOpInterface may also implement
-  // OutlineRematerializationOpInterface (e.g., fir.convert). When
-  // getOriginalValue traces through it, the traced op may not be a candidate.
-  // Fall back to the direct defining op.
-  if (origVal != val) {
-    definingOp = val.getDefiningOp();
-    if (definingOp &&
-        isa<acc::OutlineRematerializationOpInterface>(definingOp)) {
-      LLVM_DEBUG(llvm::dbgs()
-                 << "\t\t-> OutlineRematerializationOpInterface (direct)\n");
-      return true;
-    }
-  }
-
   LLVM_DEBUG(llvm::dbgs() << "\t\t-> not a candidate\n");
   return false;
 }

>From c27af818ede2411d0aa6f9d9ff5424ec1dfcbb69 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Wed, 4 Mar 2026 15:05:59 -0800
Subject: [PATCH 7/8] Revert getOriginalValue break; use fallback in
 isRematerializationCandidate

Stopping at OutlineRematerializationOpInterface in getOriginalValue is
too aggressive: it catches intermediate fir.convert ops in the trace
chain (e.g., fir.declare -> fir.convert -> unboxchar), causing the pass
to rematerialize the intermediate convert and leave its operand as an
illegal live-in. The fallback approach only checks the direct defining
op of the live-in value, avoiding this problem.

Made-with: Cursor
---
 .../OffloadLiveInValueCanonicalization.cpp    | 23 +++++++++++++------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
index d0c6bc3f0c423..7dbde227b2fab 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/OffloadLiveInValueCanonicalization.cpp
@@ -106,16 +106,11 @@ static bool allUsersAreInsideRegion(Value val, Region &region) {
 }
 
 /// Traces through view-like and partial entity access operations to find the
-/// original defining value. Stops tracing at ops that implement
-/// OutlineRematerializationOpInterface, since those ops are rematerialization
-/// candidates themselves and should not be traced through.
+/// original defining value.
 static Value getOriginalValue(Value val) {
   Value prev;
   while (val && val != prev) {
     prev = val;
-    if (isa_and_nonnull<acc::OutlineRematerializationOpInterface>(
-            val.getDefiningOp()))
-      break;
     if (auto viewLikeOp = val.getDefiningOp<ViewLikeOpInterface>())
       val = viewLikeOp.getViewSource();
     if (auto partialAccess =
@@ -140,7 +135,7 @@ static bool isRematerializationCandidate(Value val,
   // Trace through view-like operations to find the original value.
   Value origVal = getOriginalValue(val);
   Operation *definingOp = origVal.getDefiningOp();
-  if (!definingOp)
+  if (!definingOp && !(definingOp = val.getDefiningOp()))
     return false;
 
   LLVM_DEBUG(llvm::dbgs() << "\tChecking candidate: " << *definingOp << "\n");
@@ -186,6 +181,20 @@ static bool isRematerializationCandidate(Value val,
     }
   }
 
+  // An op implementing both ViewLikeOpInterface and
+  // OutlineRematerializationOpInterface may have been traced through by
+  // getOriginalValue. If the traced op is not a candidate, check the direct
+  // defining op of the live-in value.
+  if (origVal != val) {
+    definingOp = val.getDefiningOp();
+    if (definingOp &&
+        isa<acc::OutlineRematerializationOpInterface>(definingOp)) {
+      LLVM_DEBUG(llvm::dbgs()
+                 << "\t\t-> OutlineRematerializationOpInterface (direct)\n");
+      return true;
+    }
+  }
+
   LLVM_DEBUG(llvm::dbgs() << "\t\t-> not a candidate\n");
   return false;
 }

>From ec88571eec7a41f1db1e6221adbb5fa0bdc655ff Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Thu, 5 Mar 2026 08:55:09 -0800
Subject: [PATCH 8/8] Add test for intermediate fir.convert in unboxchar trace
 chain

Verify that an intermediate fir.convert in a declare -> convert ->
unboxchar chain is not rematerialized, while a direct ptr-to-int
fir.convert is correctly sunk.

Made-with: Cursor
---
 .../offload-livein-value-canonicalization.fir | 31 +++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/flang/test/Fir/OpenACC/offload-livein-value-canonicalization.fir b/flang/test/Fir/OpenACC/offload-livein-value-canonicalization.fir
index 04bf123dc3224..fa9f9c429fa02 100644
--- a/flang/test/Fir/OpenACC/offload-livein-value-canonicalization.fir
+++ b/flang/test/Fir/OpenACC/offload-livein-value-canonicalization.fir
@@ -340,3 +340,34 @@ func.func @test_convert_alloca_remat() {
 // CHECK: acc.parallel {
 // CHECK:   %[[CVT_INNER:.*]] = fir.convert %[[ALLOCA]]
 // CHECK:   fir.call @use_i64(%[[CVT_INNER]])
+
+// -----
+
+// Test that an intermediate fir.convert in a trace chain
+// (declare -> convert -> unboxchar) does not get rematerialized,
+// while a direct ptr-to-int fir.convert is correctly sunk.
+func.func private @use_i64(i64) -> ()
+func.func private @use_ref(!fir.ref<!fir.char<1,10>>) -> ()
+
+func.func @test_convert_chain_and_direct(%arg0: !fir.boxchar<1>, %arg1: !fir.ref<i32>) {
+  %c10 = arith.constant 10 : index
+  %0:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
+  %1 = fir.convert %0#0 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<!fir.char<1,10>>
+  %2 = fir.declare %1 typeparams %c10 {uniq_name = "scalar"} : (!fir.ref<!fir.char<1,10>>, index) -> !fir.ref<!fir.char<1,10>>
+  %3 = fir.convert %arg1 : (!fir.ref<i32>) -> i64
+  acc.parallel {
+    fir.call @use_ref(%2) : (!fir.ref<!fir.char<1,10>>) -> ()
+    fir.call @use_i64(%3) : (i64) -> ()
+    acc.yield
+  }
+  return
+}
+
+// CHECK-LABEL: @test_convert_chain_and_direct
+// CHECK: %[[UNBOX:.*]]:2 = fir.unboxchar %arg0
+// CHECK: %[[CVT_REF:.*]] = fir.convert %[[UNBOX]]#0
+// CHECK: %[[DECL:.*]] = fir.declare %[[CVT_REF]]
+// CHECK: acc.parallel {
+// CHECK:   %[[CVT_INT:.*]] = fir.convert %arg1
+// CHECK:   fir.call @use_ref(%[[DECL]])
+// CHECK:   fir.call @use_i64(%[[CVT_INT]])



More information about the Mlir-commits mailing list