Skip to content

Conversation

@agozillon
Copy link
Contributor

Currently we don't check for the presence of descriptor/BoxTypes before emitting stores which lower to memcpys, the issue with this is that users can have optional arguments, where they don't provide an input, making the argument effectively null. This can still be mapped and this causes issues at the moment as we'll emit a memcpy for function arguments to store to a local variable for certain edge cases, when we perform this memcpy on a null input, we cause a segfault at runtime.

The fix to this is to simply create a branch around the store that checks if the data we're copying from is actually present. If it is, we proceed with the store, if it isn't we skip it.

…store in MapInfoFinalization pass

Currently we don't check for the presence of descriptor/BoxTypes before emitting stores which lower
to memcpys, the issue with this is that users can have optional arguments, where they don't provide
an input, making the argument effectively null. This can still be mapped and this causes issues
at the moment as we'll emit a memcpy for function arguments to store to a local variable for certain
edge cases, when we perform this memcpy on a null input, we cause a segfault at runtime.

The fix to this is to simply create a branch around the store that checks if the data we're copying
from is actually present. If it is, we procede with the store, if it isn't we skip it.
@llvmbot llvmbot added flang Flang issues not falling into any other category flang:fir-hlfir flang:openmp offload labels Apr 12, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 12, 2025

@llvm/pr-subscribers-flang-openmp

@llvm/pr-subscribers-flang-fir-hlfir

Author: None (agozillon)

Changes

Currently we don't check for the presence of descriptor/BoxTypes before emitting stores which lower to memcpys, the issue with this is that users can have optional arguments, where they don't provide an input, making the argument effectively null. This can still be mapped and this causes issues at the moment as we'll emit a memcpy for function arguments to store to a local variable for certain edge cases, when we perform this memcpy on a null input, we cause a segfault at runtime.

The fix to this is to simply create a branch around the store that checks if the data we're copying from is actually present. If it is, we proceed with the store, if it isn't we skip it.


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

3 Files Affected:

  • (modified) flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp (+9-1)
  • (added) flang/test/Lower/OpenMP/optional-argument-map.f90 (+27)
  • (added) offload/test/offloading/fortran/optional-mapped-arguments.f90 (+36)
diff --git a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
index 61f8713028a7f..3fcb4b04a7b76 100644
--- a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
+++ b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
@@ -153,7 +153,15 @@ class MapInfoFinalizationPass
     builder.setInsertionPointToStart(allocaBlock);
     auto alloca = builder.create<fir::AllocaOp>(loc, descriptor.getType());
     builder.restoreInsertionPoint(insPt);
-    builder.create<fir::StoreOp>(loc, descriptor, alloca);
+    // We should only emit a store if the passed in data is present, it is
+    // possible a user passes in no argument to an optional parameter, in which
+    // case we cannot store or we'll segfault on the emitted memcpy.
+    auto isPresent =
+        builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), descriptor);
+    builder.genIfOp(loc, {}, isPresent, false)
+        .genThen(
+            [&]() { builder.create<fir::StoreOp>(loc, descriptor, alloca); })
+        .end();
     return slot = alloca;
   }
 
diff --git a/flang/test/Lower/OpenMP/optional-argument-map.f90 b/flang/test/Lower/OpenMP/optional-argument-map.f90
new file mode 100644
index 0000000000000..863742ce67927
--- /dev/null
+++ b/flang/test/Lower/OpenMP/optional-argument-map.f90
@@ -0,0 +1,27 @@
+!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
+
+module foo
+    implicit none
+    contains
+    subroutine test(I,A)
+      implicit none
+      real(4), optional, intent(inout) :: A(:)
+      integer(kind=4), intent(in) :: I
+
+     !$omp target data map(to: A) if (I>0)
+     !$omp end target data
+
+    end subroutine test
+end module foo
+
+! CHECK-LABEL:   func.func @_QMfooPtest(
+! CHECK-SAME:                           %[[VAL_0:.*]]: !fir.ref<i32> {fir.bindc_name = "i"},
+! CHECK-SAME:                           %[[VAL_1:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "a", fir.optional}) {
+! CHECK:           %[[VAL_2:.*]] = fir.alloca !fir.box<!fir.array<?xf32>>
+! CHECK:           %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_1]] dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<intent_inout, optional>, uniq_name = "_QMfooFtestEa"} : (!fir.box<!fir.array<?xf32>>, !fir.dscope) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>)
+! CHECK:           %{{.*}} = fir.is_present %{{.*}}#1 : (!fir.box<!fir.array<?xf32>>) -> i1
+! CHECK:           %{{.*}}:5 = fir.if %{{.*}}
+! CHECK:           %[[VAL_4:.*]] = fir.is_present %[[VAL_3]]#1 : (!fir.box<!fir.array<?xf32>>) -> i1
+! CHECK:           fir.if %[[VAL_4]] {
+! CHECK:             fir.store %[[VAL_3]]#1 to %[[VAL_2]] : !fir.ref<!fir.box<!fir.array<?xf32>>>
+! CHECK:           }
diff --git a/offload/test/offloading/fortran/optional-mapped-arguments.f90 b/offload/test/offloading/fortran/optional-mapped-arguments.f90
new file mode 100644
index 0000000000000..e0cdddb8e1bf7
--- /dev/null
+++ b/offload/test/offloading/fortran/optional-mapped-arguments.f90
@@ -0,0 +1,36 @@
+! OpenMP offloading test that checks we do not cause a segfault when mapping
+! optional function arguments (present or otherwise). No results requiring
+! checking other than that the program compiles and runs to completion with no
+! error.
+! REQUIRES: flang, amdgpu
+
+! RUN: %libomptarget-compile-fortran-run-and-check-generic
+module foo
+    implicit none
+    contains
+    subroutine test(I,A)
+        implicit none
+        real(4), optional, intent(inout) :: A(:)
+        integer(kind=4), intent(in) :: I
+
+        !$omp target data map(to: A) if (I>0)
+        !$omp end target data
+
+        !$omp target enter data map(to:A) if (I>0)
+
+        !$omp target exit data map(from:A) if (I>0)
+    end subroutine test
+end module foo
+
+program main
+    use foo
+    implicit none
+    real :: array(10)
+    call test(0)
+    call test(1)
+    call test(0, array)
+    call test(1, array)
+    print *, "PASSED"
+end program main
+
+! CHECK: PASSED

@llvmbot
Copy link
Member

llvmbot commented Apr 12, 2025

@llvm/pr-subscribers-offload

Author: None (agozillon)

Changes

Currently we don't check for the presence of descriptor/BoxTypes before emitting stores which lower to memcpys, the issue with this is that users can have optional arguments, where they don't provide an input, making the argument effectively null. This can still be mapped and this causes issues at the moment as we'll emit a memcpy for function arguments to store to a local variable for certain edge cases, when we perform this memcpy on a null input, we cause a segfault at runtime.

The fix to this is to simply create a branch around the store that checks if the data we're copying from is actually present. If it is, we proceed with the store, if it isn't we skip it.


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

3 Files Affected:

  • (modified) flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp (+9-1)
  • (added) flang/test/Lower/OpenMP/optional-argument-map.f90 (+27)
  • (added) offload/test/offloading/fortran/optional-mapped-arguments.f90 (+36)
diff --git a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
index 61f8713028a7f..3fcb4b04a7b76 100644
--- a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
+++ b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
@@ -153,7 +153,15 @@ class MapInfoFinalizationPass
     builder.setInsertionPointToStart(allocaBlock);
     auto alloca = builder.create<fir::AllocaOp>(loc, descriptor.getType());
     builder.restoreInsertionPoint(insPt);
-    builder.create<fir::StoreOp>(loc, descriptor, alloca);
+    // We should only emit a store if the passed in data is present, it is
+    // possible a user passes in no argument to an optional parameter, in which
+    // case we cannot store or we'll segfault on the emitted memcpy.
+    auto isPresent =
+        builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), descriptor);
+    builder.genIfOp(loc, {}, isPresent, false)
+        .genThen(
+            [&]() { builder.create<fir::StoreOp>(loc, descriptor, alloca); })
+        .end();
     return slot = alloca;
   }
 
diff --git a/flang/test/Lower/OpenMP/optional-argument-map.f90 b/flang/test/Lower/OpenMP/optional-argument-map.f90
new file mode 100644
index 0000000000000..863742ce67927
--- /dev/null
+++ b/flang/test/Lower/OpenMP/optional-argument-map.f90
@@ -0,0 +1,27 @@
+!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
+
+module foo
+    implicit none
+    contains
+    subroutine test(I,A)
+      implicit none
+      real(4), optional, intent(inout) :: A(:)
+      integer(kind=4), intent(in) :: I
+
+     !$omp target data map(to: A) if (I>0)
+     !$omp end target data
+
+    end subroutine test
+end module foo
+
+! CHECK-LABEL:   func.func @_QMfooPtest(
+! CHECK-SAME:                           %[[VAL_0:.*]]: !fir.ref<i32> {fir.bindc_name = "i"},
+! CHECK-SAME:                           %[[VAL_1:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "a", fir.optional}) {
+! CHECK:           %[[VAL_2:.*]] = fir.alloca !fir.box<!fir.array<?xf32>>
+! CHECK:           %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_1]] dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<intent_inout, optional>, uniq_name = "_QMfooFtestEa"} : (!fir.box<!fir.array<?xf32>>, !fir.dscope) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>)
+! CHECK:           %{{.*}} = fir.is_present %{{.*}}#1 : (!fir.box<!fir.array<?xf32>>) -> i1
+! CHECK:           %{{.*}}:5 = fir.if %{{.*}}
+! CHECK:           %[[VAL_4:.*]] = fir.is_present %[[VAL_3]]#1 : (!fir.box<!fir.array<?xf32>>) -> i1
+! CHECK:           fir.if %[[VAL_4]] {
+! CHECK:             fir.store %[[VAL_3]]#1 to %[[VAL_2]] : !fir.ref<!fir.box<!fir.array<?xf32>>>
+! CHECK:           }
diff --git a/offload/test/offloading/fortran/optional-mapped-arguments.f90 b/offload/test/offloading/fortran/optional-mapped-arguments.f90
new file mode 100644
index 0000000000000..e0cdddb8e1bf7
--- /dev/null
+++ b/offload/test/offloading/fortran/optional-mapped-arguments.f90
@@ -0,0 +1,36 @@
+! OpenMP offloading test that checks we do not cause a segfault when mapping
+! optional function arguments (present or otherwise). No results requiring
+! checking other than that the program compiles and runs to completion with no
+! error.
+! REQUIRES: flang, amdgpu
+
+! RUN: %libomptarget-compile-fortran-run-and-check-generic
+module foo
+    implicit none
+    contains
+    subroutine test(I,A)
+        implicit none
+        real(4), optional, intent(inout) :: A(:)
+        integer(kind=4), intent(in) :: I
+
+        !$omp target data map(to: A) if (I>0)
+        !$omp end target data
+
+        !$omp target enter data map(to:A) if (I>0)
+
+        !$omp target exit data map(from:A) if (I>0)
+    end subroutine test
+end module foo
+
+program main
+    use foo
+    implicit none
+    real :: array(10)
+    call test(0)
+    call test(1)
+    call test(0, array)
+    call test(1, array)
+    print *, "PASSED"
+end program main
+
+! CHECK: PASSED

Copy link
Member

@skatrak skatrak 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!

@agozillon
Copy link
Contributor Author

Thank you very much for the quick review @skatrak :-)

@agozillon agozillon merged commit b2c9a58 into llvm:main Apr 14, 2025
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

flang:fir-hlfir flang:openmp flang Flang issues not falling into any other category offload

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants