From 96afb3a06bf94f9f41b5358c4e0c7cb52ce0bb51 Mon Sep 17 00:00:00 2001 From: Ahmad Nawab Date: Mon, 11 May 2026 14:58:58 +0200 Subject: [PATCH 1/2] FIELD_ACCESS: add a new option to control if 0 sized field accesses are guarded --- CMakeLists.txt | 8 ++++++++ Readme.md | 1 + src/util/field_RANKSUFF_access_module.fypp | 4 ++++ tests/CMakeLists.txt | 5 +++-- tests/test_0_sized_transfer.F90 | 6 ++++++ tests/test_0_sized_transfer_wrap.F90 | 6 ++++++ 6 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dc6b333e..3dadaee4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,11 @@ ecbuild_add_option( FEATURE DELAYED DEFAULT OFF ) +##Guard 0 sized allocations +ecbuild_add_option( FEATURE GUARD_0_SIZE + DESCRIPTION "Enable this option to guard 0 sized arrays in FIELD_ACCESS module" + DEFAULT ON +) ## fypp preprocessor flags if(HAVE_CUDA OR HAVE_HIPFORT) @@ -104,6 +109,9 @@ endif() if(fiat_FOUND) list( APPEND fypp_defines "-DWITH_FIAT") endif() +if(HAVE_GUARD_0_SIZE) + list( APPEND fypp_defines "-DWITH_GUARD_0_SIZE") +endif() ## build precision independent srcs add_subdirectory(src) diff --git a/Readme.md b/Readme.md index 1e4f488b..3ef4c1e0 100644 --- a/Readme.md +++ b/Readme.md @@ -51,6 +51,7 @@ Features of FIELD_API can be toggled by passing the following argument to the CM | HIPFORT | OFF | Enable the use of advanced functionality via the hipfort runtime API e.g. host memory pinning, fast strided copies, asynchronous data transfers, etc.| | GET_VIEW_ABORT | ON | If activated, get_view will abort when the data are not present on CPU. | | DELAYED | OFF | If activated, field owners will be delayed by default. | +| GUARD_0_SIZE | ON | If activated, the module-level `GET_HOST_DATA_*`/`GET_DEVICE_DATA_*` accessors in `FIELD_ACCESS_MODULE` return a size-1 dummy pointer for 0-sized fields instead of the underlying 0-sized array. | | IO_SERIAL | OFF | Use serial HDF5 to read and write FieldAPI variables. | | IO_PARALLEL | OFF | Use HDF5 (rank defined by MPI) to read and write FieldAPI variables. | diff --git a/src/util/field_RANKSUFF_access_module.fypp b/src/util/field_RANKSUFF_access_module.fypp index 4ea2377f..dd903ffc 100644 --- a/src/util/field_RANKSUFF_access_module.fypp +++ b/src/util/field_RANKSUFF_access_module.fypp @@ -84,7 +84,11 @@ CONTAINS #:for rank in range(ft.rank) ISIZE = ISIZE * (IUBOUNDS(${rank + 1}$) - ILBOUNDS(${rank + 1}$) + 1) #:endfor +#:if defined('WITH_GUARD_0_SIZE') IF (ISIZE > 0) LEXISTS = .TRUE. +#:else + LEXISTS = .TRUE. +#:endif ENDIF IF (LEXISTS) THEN CALL FIELD_PTR%GET_${what}$_DATA_${mode}$ (PTR) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e369309f..d44622e6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -265,8 +265,9 @@ foreach(TEST_FILE ${TEST_FILES}) target_compile_definitions( ${TEST_NAME}.x PRIVATE $<$:HDF5::HDF5> $<${HAVE_OMP_OFFLOAD}:OMPGPU> - $<${HAVE_CUDA}:_CUDA> - $<${HAVE_HIPFORT}:_HIP> ) + $<${HAVE_CUDA}:_CUDA> + $<${HAVE_HIPFORT}:_HIP> + $<${HAVE_GUARD_0_SIZE}:WITH_GUARD_0_SIZE> ) if( DEFAULT_PRECISION MATCHES sp ) target_compile_definitions( ${TEST_NAME}.x PRIVATE PARKIND1_SINGLE ) diff --git a/tests/test_0_sized_transfer.F90 b/tests/test_0_sized_transfer.F90 index cfd1c96a..5df027d2 100644 --- a/tests/test_0_sized_transfer.F90 +++ b/tests/test_0_sized_transfer.F90 @@ -27,9 +27,15 @@ PROGRAM TEST_0_SIZED_TRANSFER ENDIF PTR => GET_HOST_DATA_RDONLY(O) +#ifdef WITH_GUARD_0_SIZE IF( SIZE(PTR) /= 1 )THEN CALL FIELD_ABORT("ALLOCATION SHOULD BE OF SIZE==1") ENDIF +#else + IF( SIZE(PTR) /= 0 )THEN + CALL FIELD_ABORT("ALLOCATION SHOULD BE OF SIZE==0") + ENDIF +#endif !...Ensure data can be copied to device and back safely CALL O%SYNC_DEVICE_RDWR() diff --git a/tests/test_0_sized_transfer_wrap.F90 b/tests/test_0_sized_transfer_wrap.F90 index 007f153e..55337702 100644 --- a/tests/test_0_sized_transfer_wrap.F90 +++ b/tests/test_0_sized_transfer_wrap.F90 @@ -30,9 +30,15 @@ PROGRAM TEST_0_SIZED_TRANSFER ENDIF PTR => GET_HOST_DATA_RDONLY(W) +#ifdef WITH_GUARD_0_SIZE IF( SIZE(PTR) /= 1 )THEN CALL FIELD_ABORT("ALLOCATION SHOULD BE OF SIZE==1") ENDIF +#else + IF( SIZE(PTR) /= 0 )THEN + CALL FIELD_ABORT("ALLOCATION SHOULD BE OF SIZE==0") + ENDIF +#endif !...Ensure data can be copied to device and back safely CALL W%SYNC_DEVICE_RDWR() From b1e2b342a5ce70b8bcffaf3cc8e6a4113ae1dc92 Mon Sep 17 00:00:00 2001 From: Ahmad Nawab Date: Mon, 11 May 2026 15:51:15 +0200 Subject: [PATCH 2/2] ci: also test GUARD_0_SIZE option --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 111266f2..cb16f9f2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -256,5 +256,5 @@ jobs: dependency_branch: develop dependency_cmake_options: | ecmwf-ifs/fiat: "-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -G Ninja -DENABLE_TESTS=OFF ${{ matrix.cmake_options}}" - cmake_options: "-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} ${{ matrix.cmake_options }} -G Ninja -DENABLE_ACC=OFF -DENABLE_MPI=ON -DENABLE_IO_PARALLEL=ON -DENABLE_GET_VIEW_ABORT=${{ matrix.get_view_abort }} -DENABLE_DELAYED=${{ matrix.delayed }} ${{matrix.cmake_options}}" + cmake_options: "-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} ${{ matrix.cmake_options }} -G Ninja -DENABLE_ACC=OFF -DENABLE_MPI=ON -DENABLE_IO_PARALLEL=ON -DENABLE_GET_VIEW_ABORT=${{ matrix.get_view_abort }} -DENABLE_DELAYED=${{ matrix.delayed }} -DENABLE_GUARD_0_SIZE=${{ matrix.delayed }} ${{matrix.cmake_options}}" ctest_options: "${{ matrix.ctest_options }}"