diff --git a/flang/include/flang/Runtime/descriptor.h b/flang/include/flang/Runtime/descriptor.h index c9a3b1b031007..c69bb336dd29e 100644 --- a/flang/include/flang/Runtime/descriptor.h +++ b/flang/include/flang/Runtime/descriptor.h @@ -390,14 +390,16 @@ class Descriptor { if (leadingDimensions > raw_.rank) { leadingDimensions = raw_.rank; } + bool stridesAreContiguous{true}; for (int j{0}; j < leadingDimensions; ++j) { const Dimension &dim{GetDimension(j)}; - if (bytes != dim.ByteStride()) { - return false; - } + stridesAreContiguous &= bytes == dim.ByteStride(); bytes *= dim.Extent(); } - return true; + // One and zero element arrays are contiguous even if the descriptor + // byte strides are not perfect multiples. + return stridesAreContiguous || bytes == 0 || + bytes == static_cast(ElementBytes()); } // Establishes a pointer to a section or element. diff --git a/flang/runtime/ISO_Fortran_binding.cpp b/flang/runtime/ISO_Fortran_binding.cpp index 15743be88d1be..103413cb7140a 100644 --- a/flang/runtime/ISO_Fortran_binding.cpp +++ b/flang/runtime/ISO_Fortran_binding.cpp @@ -125,14 +125,19 @@ RT_API_ATTRS int CFI_establish(CFI_cdesc_t *descriptor, void *base_addr, } RT_API_ATTRS int CFI_is_contiguous(const CFI_cdesc_t *descriptor) { + bool stridesAreContiguous{true}; CFI_index_t bytes = descriptor->elem_len; for (int j{0}; j < descriptor->rank; ++j) { - if (bytes != descriptor->dim[j].sm) { - return 0; - } + stridesAreContiguous &= bytes == descriptor->dim[j].sm; bytes *= descriptor->dim[j].extent; } - return 1; + // One and zero element arrays are contiguous even if the descriptor + // byte strides are not perfect multiples. + if (stridesAreContiguous || bytes == 0 || + bytes == static_cast(descriptor->elem_len)) { + return 1; + } + return 0; } RT_API_ATTRS int CFI_section(CFI_cdesc_t *result, const CFI_cdesc_t *source, diff --git a/flang/unittests/Evaluate/ISO-Fortran-binding.cpp b/flang/unittests/Evaluate/ISO-Fortran-binding.cpp index 09a51e6cea10b..d1f0a31454056 100644 --- a/flang/unittests/Evaluate/ISO-Fortran-binding.cpp +++ b/flang/unittests/Evaluate/ISO-Fortran-binding.cpp @@ -643,13 +643,108 @@ static void run_CFI_setpointer_tests() { } } +static void run_CFI_is_contiguous_tests() { + // INTEGER :: A(0:3,0:3) + constexpr CFI_rank_t rank{2}; + CFI_index_t extents[rank] = {4, 4}; + CFI_CDESC_T(rank) dv_storage; + CFI_cdesc_t *dv{&dv_storage}; + Descriptor *dvDesc{reinterpret_cast(dv)}; + char base; + void *base_addr{&base}; + int retCode{CFI_establish(dv, base_addr, CFI_attribute_other, CFI_type_int, + /*elem_len=*/0, rank, extents)}; + MATCH(retCode == CFI_SUCCESS, true); + + MATCH(true, CFI_is_contiguous(dv) == 1); + MATCH(true, dvDesc->IsContiguous()); + + CFI_CDESC_T(rank) sectionDescriptorStorage; + CFI_cdesc_t *section{§ionDescriptorStorage}; + Descriptor *sectionDesc{reinterpret_cast(section)}; + retCode = CFI_establish(section, base_addr, CFI_attribute_other, CFI_type_int, + /*elem_len=*/0, rank, extents); + MATCH(retCode == CFI_SUCCESS, true); + + // Test empty section B = A(0:3:2,0:3:-2) is contiguous. + CFI_index_t lb[rank] = {0, 0}; + CFI_index_t ub[rank] = {3, 3}; + CFI_index_t strides[rank] = {2, -2}; + retCode = CFI_section(section, dv, lb, ub, strides); + MATCH(true, retCode == CFI_SUCCESS); + MATCH(true, CFI_is_contiguous(section) == 1); + MATCH(true, sectionDesc->IsContiguous()); + + // Test 1 element section B = A(0:1:2,0:1:2) is contiguous. + lb[0] = 0; + lb[1] = 0; + ub[0] = 1; + ub[1] = 1; + strides[0] = 2; + strides[1] = 2; + retCode = CFI_section(section, dv, lb, ub, strides); + MATCH(true, retCode == CFI_SUCCESS); + MATCH(true, CFI_is_contiguous(section) == 1); + MATCH(true, sectionDesc->IsContiguous()); + + // Test section B = A(0:3:1,0:2:1) is contiguous. + lb[0] = 0; + lb[1] = 0; + ub[0] = 3; + ub[1] = 2; + strides[0] = 1; + strides[1] = 1; + retCode = CFI_section(section, dv, lb, ub, strides); + sectionDesc->Dump(); + MATCH(true, retCode == CFI_SUCCESS); + MATCH(true, CFI_is_contiguous(section) == 1); + MATCH(true, sectionDesc->IsContiguous()); + + // Test section B = A(0:2:1,0:2:1) is not contiguous. + lb[0] = 0; + lb[1] = 0; + ub[0] = 2; + ub[1] = 2; + strides[0] = 1; + strides[1] = 1; + retCode = CFI_section(section, dv, lb, ub, strides); + sectionDesc->Dump(); + MATCH(true, retCode == CFI_SUCCESS); + MATCH(true, CFI_is_contiguous(section) == 0); + MATCH(false, sectionDesc->IsContiguous()); + + // Test section B = A(0:3:2,0:3:1) is not contiguous. + lb[0] = 0; + lb[1] = 0; + ub[0] = 3; + ub[1] = 3; + strides[0] = 2; + strides[1] = 1; + retCode = CFI_section(section, dv, lb, ub, strides); + MATCH(true, retCode == CFI_SUCCESS); + MATCH(true, CFI_is_contiguous(section) == 0); + MATCH(false, sectionDesc->IsContiguous()); + + // Test section B = A(0:3:1,0:3:2) is not contiguous. + lb[0] = 0; + lb[1] = 0; + ub[0] = 3; + ub[1] = 3; + strides[0] = 1; + strides[1] = 2; + retCode = CFI_section(section, dv, lb, ub, strides); + MATCH(true, retCode == CFI_SUCCESS); + MATCH(true, CFI_is_contiguous(section) == 0); + MATCH(false, sectionDesc->IsContiguous()); +} + int main() { TestCdescMacroForAllRanksSmallerThan(); run_CFI_establish_tests(); run_CFI_address_tests(); run_CFI_allocate_tests(); // TODO: test CFI_deallocate - // TODO: test CFI_is_contiguous + run_CFI_is_contiguous_tests(); run_CFI_section_tests(); run_CFI_select_part_tests(); run_CFI_setpointer_tests();