| /* Functions to convert descriptors between CFI and gfortran |
| and the CFI function declarations whose prototypes appear |
| in ISO_Fortran_binding.h. |
| Copyright (C) 2018-2021 Free Software Foundation, Inc. |
| Contributed by Daniel Celis Garza <celisdanieljr@gmail.com> |
| and Paul Thomas <pault@gcc.gnu.org> |
| |
| This file is part of the GNU Fortran runtime library (libgfortran). |
| |
| Libgfortran is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public |
| License as published by the Free Software Foundation; either |
| version 3 of the License, or (at your option) any later version. |
| |
| Libgfortran is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| Under Section 7 of GPL version 3, you are granted additional |
| permissions described in the GCC Runtime Library Exception, version |
| 3.1, as published by the Free Software Foundation. |
| |
| You should have received a copy of the GNU General Public License and |
| a copy of the GCC Runtime Library Exception along with this program; |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "libgfortran.h" |
| #include "ISO_Fortran_binding.h" |
| #include <string.h> |
| #include <inttypes.h> /* for PRIiPTR */ |
| |
| extern void cfi_desc_to_gfc_desc (gfc_array_void *, CFI_cdesc_t **); |
| export_proto(cfi_desc_to_gfc_desc); |
| |
| void |
| cfi_desc_to_gfc_desc (gfc_array_void *d, CFI_cdesc_t **s_ptr) |
| { |
| signed char type; |
| size_t size; |
| int n; |
| CFI_cdesc_t *s = *s_ptr; |
| |
| if (!s) |
| return; |
| |
| /* Verify descriptor. */ |
| switch (s->attribute) |
| { |
| case CFI_attribute_pointer: |
| case CFI_attribute_allocatable: |
| break; |
| case CFI_attribute_other: |
| if (s->base_addr) |
| break; |
| runtime_error ("Nonallocatable, nonpointer actual argument to BIND(C) " |
| "dummy argument where the effective argument is either " |
| "not allocated or not associated"); |
| break; |
| default: |
| runtime_error ("Invalid attribute type %d in CFI_cdesc_t descriptor", |
| (int) s->attribute); |
| break; |
| } |
| GFC_DESCRIPTOR_DATA (d) = s->base_addr; |
| |
| /* Correct the unfortunate difference in order with types. */ |
| type = (signed char)(s->type & CFI_type_mask); |
| switch (type) |
| { |
| case CFI_type_Character: |
| type = BT_CHARACTER; |
| break; |
| case CFI_type_struct: |
| type = BT_DERIVED; |
| break; |
| case CFI_type_cptr: |
| /* FIXME: PR 100915. GFC descriptors do not distinguish between |
| CFI_type_cptr and CFI_type_cfunptr. */ |
| type = BT_VOID; |
| break; |
| default: |
| break; |
| } |
| |
| GFC_DESCRIPTOR_TYPE (d) = type; |
| GFC_DESCRIPTOR_SIZE (d) = s->elem_len; |
| |
| d->dtype.version = 0; |
| |
| if (s->rank < 0 || s->rank > CFI_MAX_RANK) |
| internal_error (NULL, "Invalid rank in descriptor"); |
| GFC_DESCRIPTOR_RANK (d) = (signed char)s->rank; |
| |
| d->dtype.attribute = (signed short)s->attribute; |
| |
| if (s->rank) |
| { |
| if ((size_t)s->dim[0].sm % s->elem_len) |
| d->span = (index_type)s->dim[0].sm; |
| else |
| d->span = (index_type)s->elem_len; |
| } |
| |
| d->offset = 0; |
| if (GFC_DESCRIPTOR_DATA (d)) |
| for (n = 0; n < GFC_DESCRIPTOR_RANK (d); n++) |
| { |
| CFI_index_t lb = 1; |
| |
| if (s->attribute != CFI_attribute_other) |
| lb = s->dim[n].lower_bound; |
| |
| GFC_DESCRIPTOR_LBOUND(d, n) = (index_type)lb; |
| GFC_DESCRIPTOR_UBOUND(d, n) = (index_type)(s->dim[n].extent + lb - 1); |
| GFC_DESCRIPTOR_STRIDE(d, n) = (index_type)(s->dim[n].sm / s->elem_len); |
| d->offset -= GFC_DESCRIPTOR_STRIDE(d, n) * GFC_DESCRIPTOR_LBOUND(d, n); |
| } |
| } |
| |
| extern void gfc_desc_to_cfi_desc (CFI_cdesc_t **, const gfc_array_void *); |
| export_proto(gfc_desc_to_cfi_desc); |
| |
| void |
| gfc_desc_to_cfi_desc (CFI_cdesc_t **d_ptr, const gfc_array_void *s) |
| { |
| int n; |
| CFI_cdesc_t *d; |
| signed char type, kind; |
| |
| /* Play it safe with allocation of the flexible array member 'dim' |
| by setting the length to CFI_MAX_RANK. This should not be necessary |
| but valgrind complains accesses after the allocated block. */ |
| if (*d_ptr == NULL) |
| d = calloc (1, (sizeof (CFI_cdesc_t) |
| + (CFI_type_t)(CFI_MAX_RANK * sizeof (CFI_dim_t)))); |
| else |
| d = *d_ptr; |
| |
| /* Verify descriptor. */ |
| switch (s->dtype.attribute) |
| { |
| case CFI_attribute_pointer: |
| case CFI_attribute_allocatable: |
| break; |
| case CFI_attribute_other: |
| if (s->base_addr) |
| break; |
| runtime_error ("Nonallocatable, nonpointer actual argument to BIND(C) " |
| "dummy argument where the effective argument is either " |
| "not allocated or not associated"); |
| break; |
| default: |
| internal_error (NULL, "Invalid attribute in gfc_array descriptor"); |
| break; |
| } |
| d->base_addr = GFC_DESCRIPTOR_DATA (s); |
| d->elem_len = GFC_DESCRIPTOR_SIZE (s); |
| if (d->elem_len <= 0) |
| internal_error (NULL, "Invalid size in descriptor"); |
| |
| d->version = CFI_VERSION; |
| |
| d->rank = (CFI_rank_t)GFC_DESCRIPTOR_RANK (s); |
| if (d->rank < 0 || d->rank > CFI_MAX_RANK) |
| internal_error (NULL, "Invalid rank in descriptor"); |
| |
| d->attribute = (CFI_attribute_t)s->dtype.attribute; |
| |
| type = GFC_DESCRIPTOR_TYPE (s); |
| switch (type) |
| { |
| case BT_CHARACTER: |
| d->type = CFI_type_Character; |
| break; |
| case BT_DERIVED: |
| d->type = CFI_type_struct; |
| break; |
| case BT_VOID: |
| /* FIXME: PR 100915. GFC descriptors do not distinguish between |
| CFI_type_cptr and CFI_type_cfunptr. */ |
| d->type = CFI_type_cptr; |
| break; |
| default: |
| d->type = (CFI_type_t)type; |
| break; |
| } |
| |
| switch (d->type) |
| { |
| case CFI_type_Integer: |
| case CFI_type_Logical: |
| case CFI_type_Real: |
| kind = (signed char)d->elem_len; |
| break; |
| case CFI_type_Complex: |
| kind = (signed char)(d->elem_len >> 1); |
| break; |
| case CFI_type_Character: |
| /* FIXME: we can't distinguish between kind/len because |
| the GFC descriptor only encodes the elem_len.. |
| Until PR92482 is fixed, assume elem_len refers to the |
| character size and not the string length. */ |
| kind = (signed char)d->elem_len; |
| break; |
| case CFI_type_struct: |
| case CFI_type_cptr: |
| case CFI_type_other: |
| /* FIXME: PR 100915. GFC descriptors do not distinguish between |
| CFI_type_cptr and CFI_type_cfunptr. */ |
| kind = 0; |
| break; |
| default: |
| internal_error (NULL, "Invalid type in descriptor"); |
| } |
| |
| if (kind < 0) |
| internal_error (NULL, "Invalid kind in descriptor"); |
| |
| /* FIXME: This is PR100917. Because the GFC descriptor encodes only the |
| elem_len and not the kind, we get into trouble with long double kinds |
| that do not correspond directly to the elem_len, specifically the |
| kind 10 80-bit long double on x86 targets. On x86_64, this has size |
| 16 and cannot be differentiated from true _Float128. Prefer the |
| standard long double type over the GNU extension in that case. */ |
| if (d->type == CFI_type_Real && kind == sizeof (long double)) |
| d->type = CFI_type_long_double; |
| else if (d->type == CFI_type_Complex && kind == sizeof (long double)) |
| d->type = CFI_type_long_double_Complex; |
| else |
| d->type = (CFI_type_t)(d->type |
| + ((CFI_type_t)kind << CFI_type_kind_shift)); |
| |
| if (d->base_addr) |
| /* Full pointer or allocatable arrays retain their lower_bounds. */ |
| for (n = 0; n < GFC_DESCRIPTOR_RANK (s); n++) |
| { |
| if (d->attribute != CFI_attribute_other) |
| d->dim[n].lower_bound = (CFI_index_t)GFC_DESCRIPTOR_LBOUND(s, n); |
| else |
| d->dim[n].lower_bound = 0; |
| |
| /* Assumed size arrays have gfc ubound == 0 and CFI extent = -1. */ |
| if (n == GFC_DESCRIPTOR_RANK (s) - 1 |
| && GFC_DESCRIPTOR_LBOUND(s, n) == 1 |
| && GFC_DESCRIPTOR_UBOUND(s, n) == 0) |
| d->dim[n].extent = -1; |
| else |
| d->dim[n].extent = (CFI_index_t)GFC_DESCRIPTOR_UBOUND(s, n) |
| - (CFI_index_t)GFC_DESCRIPTOR_LBOUND(s, n) + 1; |
| d->dim[n].sm = (CFI_index_t)(GFC_DESCRIPTOR_STRIDE(s, n) * s->span); |
| } |
| |
| if (*d_ptr == NULL) |
| *d_ptr = d; |
| } |
| |
| void *CFI_address (const CFI_cdesc_t *dv, const CFI_index_t subscripts[]) |
| { |
| int i; |
| char *base_addr = (char *)dv->base_addr; |
| |
| if (unlikely (compile_options.bounds_check)) |
| { |
| /* C descriptor must not be NULL. */ |
| if (dv == NULL) |
| { |
| fprintf (stderr, "CFI_address: C descriptor is NULL.\n"); |
| return NULL; |
| } |
| |
| /* Base address of C descriptor must not be NULL. */ |
| if (dv->base_addr == NULL) |
| { |
| fprintf (stderr, "CFI_address: base address of C descriptor " |
| "must not be NULL.\n"); |
| return NULL; |
| } |
| } |
| |
| /* Return base address if C descriptor is a scalar. */ |
| if (dv->rank == 0) |
| return dv->base_addr; |
| |
| /* Calculate the appropriate base address if dv is not a scalar. */ |
| else |
| { |
| /* Base address is the C address of the element of the object |
| specified by subscripts. */ |
| for (i = 0; i < dv->rank; i++) |
| { |
| CFI_index_t idx = subscripts[i] - dv->dim[i].lower_bound; |
| if (unlikely (compile_options.bounds_check) |
| && ((dv->dim[i].extent != -1 && idx >= dv->dim[i].extent) |
| || idx < 0)) |
| { |
| fprintf (stderr, "CFI_address: subscripts[%d] is out of " |
| "bounds. For dimension = %d, subscripts = %d, " |
| "lower_bound = %" PRIiPTR ", upper bound = %" PRIiPTR |
| ", extent = %" PRIiPTR "\n", |
| i, i, (int)subscripts[i], |
| (ptrdiff_t)dv->dim[i].lower_bound, |
| (ptrdiff_t)(dv->dim[i].extent - dv->dim[i].lower_bound), |
| (ptrdiff_t)dv->dim[i].extent); |
| return NULL; |
| } |
| |
| base_addr = base_addr + (CFI_index_t)(idx * dv->dim[i].sm); |
| } |
| } |
| |
| return (void *)base_addr; |
| } |
| |
| |
| int |
| CFI_allocate (CFI_cdesc_t *dv, const CFI_index_t lower_bounds[], |
| const CFI_index_t upper_bounds[], size_t elem_len) |
| { |
| if (unlikely (compile_options.bounds_check)) |
| { |
| /* C descriptor must not be NULL. */ |
| if (dv == NULL) |
| { |
| fprintf (stderr, "CFI_allocate: C descriptor is NULL.\n"); |
| return CFI_INVALID_DESCRIPTOR; |
| } |
| |
| /* The C descriptor must be for an allocatable or pointer object. */ |
| if (dv->attribute == CFI_attribute_other) |
| { |
| fprintf (stderr, "CFI_allocate: The object of the C descriptor " |
| "must be a pointer or allocatable variable.\n"); |
| return CFI_INVALID_ATTRIBUTE; |
| } |
| |
| /* Base address of C descriptor must be NULL. */ |
| if (dv->base_addr != NULL) |
| { |
| fprintf (stderr, "CFI_allocate: Base address of C descriptor " |
| "must be NULL.\n"); |
| return CFI_ERROR_BASE_ADDR_NOT_NULL; |
| } |
| } |
| |
| /* If the type is a Fortran character type, the descriptor's element |
| length is replaced by the elem_len argument. */ |
| if (dv->type == CFI_type_char || dv->type == CFI_type_ucs4_char) |
| dv->elem_len = elem_len; |
| |
| /* Dimension information and calculating the array length. */ |
| size_t arr_len = 1; |
| |
| /* If rank is greater than 0, lower_bounds and upper_bounds are used. They're |
| ignored otherwise. */ |
| if (dv->rank > 0) |
| { |
| if (unlikely (compile_options.bounds_check) |
| && (lower_bounds == NULL || upper_bounds == NULL)) |
| { |
| fprintf (stderr, "CFI_allocate: The lower_bounds and " |
| "upper_bounds arguments must be non-NULL when " |
| "rank is greater than zero.\n"); |
| return CFI_INVALID_EXTENT; |
| } |
| |
| for (int i = 0; i < dv->rank; i++) |
| { |
| dv->dim[i].lower_bound = lower_bounds[i]; |
| dv->dim[i].extent = upper_bounds[i] - dv->dim[i].lower_bound + 1; |
| dv->dim[i].sm = dv->elem_len * arr_len; |
| arr_len *= dv->dim[i].extent; |
| } |
| } |
| |
| dv->base_addr = calloc (arr_len, dv->elem_len); |
| if (dv->base_addr == NULL) |
| { |
| fprintf (stderr, "CFI_allocate: Failure in memory allocation.\n"); |
| return CFI_ERROR_MEM_ALLOCATION; |
| } |
| |
| return CFI_SUCCESS; |
| } |
| |
| |
| int |
| CFI_deallocate (CFI_cdesc_t *dv) |
| { |
| if (unlikely (compile_options.bounds_check)) |
| { |
| /* C descriptor must not be NULL */ |
| if (dv == NULL) |
| { |
| fprintf (stderr, "CFI_deallocate: C descriptor is NULL.\n"); |
| return CFI_INVALID_DESCRIPTOR; |
| } |
| |
| /* Base address must not be NULL. */ |
| if (dv->base_addr == NULL) |
| { |
| fprintf (stderr, "CFI_deallocate: Base address is already NULL.\n"); |
| return CFI_ERROR_BASE_ADDR_NULL; |
| } |
| |
| /* C descriptor must be for an allocatable or pointer variable. */ |
| if (dv->attribute == CFI_attribute_other) |
| { |
| fprintf (stderr, "CFI_deallocate: C descriptor must describe a " |
| "pointer or allocatable object.\n"); |
| return CFI_INVALID_ATTRIBUTE; |
| } |
| } |
| |
| /* Free and nullify memory. */ |
| free (dv->base_addr); |
| dv->base_addr = NULL; |
| |
| return CFI_SUCCESS; |
| } |
| |
| |
| int CFI_establish (CFI_cdesc_t *dv, void *base_addr, CFI_attribute_t attribute, |
| CFI_type_t type, size_t elem_len, CFI_rank_t rank, |
| const CFI_index_t extents[]) |
| { |
| if (unlikely (compile_options.bounds_check)) |
| { |
| /* C descriptor must not be NULL. */ |
| if (dv == NULL) |
| { |
| fprintf (stderr, "CFI_establish: C descriptor is NULL.\n"); |
| return CFI_INVALID_DESCRIPTOR; |
| } |
| |
| /* Rank must be between 0 and CFI_MAX_RANK. */ |
| if (rank < 0 || rank > CFI_MAX_RANK) |
| { |
| fprintf (stderr, "CFI_establish: Rank must be between 0 and %d, " |
| "0 < rank (0 !< %d).\n", CFI_MAX_RANK, (int)rank); |
| return CFI_INVALID_RANK; |
| } |
| |
| /* If base address is not NULL, the established C descriptor is for a |
| nonallocatable entity. */ |
| if (attribute == CFI_attribute_allocatable && base_addr != NULL) |
| { |
| fprintf (stderr, "CFI_establish: If base address is not NULL, " |
| "the established C descriptor must be " |
| "for a nonallocatable entity.\n"); |
| return CFI_INVALID_ATTRIBUTE; |
| } |
| } |
| |
| dv->base_addr = base_addr; |
| |
| if (type == CFI_type_char || type == CFI_type_ucs4_char |
| || type == CFI_type_struct || type == CFI_type_other) |
| { |
| /* Note that elem_len has type size_t, which is unsigned. */ |
| if (unlikely (compile_options.bounds_check) && elem_len == 0) |
| { |
| fprintf (stderr, "CFI_establish: The supplied elem_len must " |
| "be greater than zero.\n"); |
| return CFI_INVALID_ELEM_LEN; |
| } |
| dv->elem_len = elem_len; |
| } |
| else if (type == CFI_type_cptr) |
| dv->elem_len = sizeof (void *); |
| else if (type == CFI_type_cfunptr) |
| dv->elem_len = sizeof (void (*)(void)); |
| else if (unlikely (compile_options.bounds_check) && type < 0) |
| { |
| fprintf (stderr, "CFI_establish: Invalid type (type = %d).\n", |
| (int)type); |
| return CFI_INVALID_TYPE; |
| } |
| else |
| { |
| /* base_type describes the intrinsic type with kind parameter. */ |
| size_t base_type = type & CFI_type_mask; |
| /* base_type_size is the size in bytes of the variable as given by its |
| * kind parameter. */ |
| size_t base_type_size = (type - base_type) >> CFI_type_kind_shift; |
| /* Kind type 10 maps onto the 80-bit long double encoding on x86. |
| Note that this has different storage size for -m32 than -m64. */ |
| if (base_type_size == 10) |
| base_type_size = sizeof (long double); |
| /* Complex numbers are twice the size of their real counterparts. */ |
| if (base_type == CFI_type_Complex) |
| base_type_size *= 2; |
| dv->elem_len = base_type_size; |
| } |
| |
| dv->version = CFI_VERSION; |
| dv->rank = rank; |
| dv->attribute = attribute; |
| dv->type = type; |
| |
| /* Extents must not be NULL if rank is greater than zero and base_addr is not |
| NULL */ |
| if (rank > 0 && base_addr != NULL) |
| { |
| if (unlikely (compile_options.bounds_check) && extents == NULL) |
| { |
| fprintf (stderr, "CFI_establish: Extents must not be NULL " |
| "if rank is greater than zero and base address is " |
| "not NULL.\n"); |
| return CFI_INVALID_EXTENT; |
| } |
| |
| for (int i = 0; i < rank; i++) |
| { |
| /* The standard requires all dimensions to be nonnegative. |
| Apparently you can have an extent-zero dimension but can't |
| construct an assumed-size array with -1 as the extent |
| of the last dimension. */ |
| if (unlikely (compile_options.bounds_check) && extents[i] < 0) |
| { |
| fprintf (stderr, "CFI_establish: Extents must be nonnegative " |
| "(extents[%d] = %" PRIiPTR ").\n", |
| i, (ptrdiff_t)extents[i]); |
| return CFI_INVALID_EXTENT; |
| } |
| dv->dim[i].lower_bound = 0; |
| dv->dim[i].extent = extents[i]; |
| if (i == 0) |
| dv->dim[i].sm = dv->elem_len; |
| else |
| { |
| CFI_index_t extents_product = 1; |
| for (int j = 0; j < i; j++) |
| extents_product *= extents[j]; |
| dv->dim[i].sm = (CFI_index_t)(dv->elem_len * extents_product); |
| } |
| } |
| } |
| |
| return CFI_SUCCESS; |
| } |
| |
| |
| int CFI_is_contiguous (const CFI_cdesc_t *dv) |
| { |
| if (unlikely (compile_options.bounds_check)) |
| { |
| /* C descriptor must not be NULL. */ |
| if (dv == NULL) |
| { |
| fprintf (stderr, "CFI_is_contiguous: C descriptor is NULL.\n"); |
| return 0; |
| } |
| |
| /* Base address must not be NULL. */ |
| if (dv->base_addr == NULL) |
| { |
| fprintf (stderr, "CFI_is_contiguous: Base address of C descriptor " |
| "is already NULL.\n"); |
| return 0; |
| } |
| |
| /* Must be an array. */ |
| if (dv->rank <= 0) |
| { |
| fprintf (stderr, "CFI_is_contiguous: C descriptor must describe " |
| "an array.\n"); |
| return 0; |
| } |
| } |
| |
| /* Assumed size arrays are always contiguous. */ |
| if (dv->rank > 0 && dv->dim[dv->rank - 1].extent == -1) |
| return 1; |
| |
| /* If an array is not contiguous the memory stride is different to |
| the element length. */ |
| for (int i = 0; i < dv->rank; i++) |
| { |
| if (i == 0 && dv->dim[i].sm == (CFI_index_t)dv->elem_len) |
| continue; |
| else if (i > 0 |
| && dv->dim[i].sm == (CFI_index_t)(dv->dim[i - 1].sm |
| * dv->dim[i - 1].extent)) |
| continue; |
| |
| return 0; |
| } |
| |
| /* Array sections are guaranteed to be contiguous by the previous test. */ |
| return 1; |
| } |
| |
| |
| int CFI_section (CFI_cdesc_t *result, const CFI_cdesc_t *source, |
| const CFI_index_t lower_bounds[], |
| const CFI_index_t upper_bounds[], const CFI_index_t strides[]) |
| { |
| /* Dimension information. */ |
| CFI_index_t lower[CFI_MAX_RANK]; |
| CFI_index_t upper[CFI_MAX_RANK]; |
| CFI_index_t stride[CFI_MAX_RANK]; |
| int zero_count = 0; |
| |
| if (unlikely (compile_options.bounds_check)) |
| { |
| /* C descriptors must not be NULL. */ |
| if (source == NULL) |
| { |
| fprintf (stderr, "CFI_section: Source must not be NULL.\n"); |
| return CFI_INVALID_DESCRIPTOR; |
| } |
| |
| if (result == NULL) |
| { |
| fprintf (stderr, "CFI_section: Result must not be NULL.\n"); |
| return CFI_INVALID_DESCRIPTOR; |
| } |
| |
| /* Base address of source must not be NULL. */ |
| if (source->base_addr == NULL) |
| { |
| fprintf (stderr, "CFI_section: Base address of source must " |
| "not be NULL.\n"); |
| return CFI_ERROR_BASE_ADDR_NULL; |
| } |
| |
| /* Result must not be an allocatable array. */ |
| if (result->attribute == CFI_attribute_allocatable) |
| { |
| fprintf (stderr, "CFI_section: Result must not describe an " |
| "allocatable array.\n"); |
| return CFI_INVALID_ATTRIBUTE; |
| } |
| |
| /* Source must be some form of array (nonallocatable nonpointer array, |
| allocated allocatable array or an associated pointer array). */ |
| if (source->rank <= 0) |
| { |
| fprintf (stderr, "CFI_section: Source must describe an array.\n"); |
| return CFI_INVALID_RANK; |
| } |
| |
| /* Element lengths of source and result must be equal. */ |
| if (result->elem_len != source->elem_len) |
| { |
| fprintf (stderr, "CFI_section: The element lengths of " |
| "source (source->elem_len = %" PRIiPTR ") and result " |
| "(result->elem_len = %" PRIiPTR ") must be equal.\n", |
| (ptrdiff_t)source->elem_len, (ptrdiff_t)result->elem_len); |
| return CFI_INVALID_ELEM_LEN; |
| } |
| |
| /* Types must be equal. */ |
| if (result->type != source->type) |
| { |
| fprintf (stderr, "CFI_section: Types of source " |
| "(source->type = %d) and result (result->type = %d) " |
| "must be equal.\n", source->type, result->type); |
| return CFI_INVALID_TYPE; |
| } |
| } |
| |
| /* Stride of zero in the i'th dimension means rank reduction in that |
| dimension. */ |
| for (int i = 0; i < source->rank; i++) |
| { |
| if (strides[i] == 0) |
| zero_count++; |
| } |
| |
| /* Rank of result must be equal the the rank of source minus the number of |
| * zeros in strides. */ |
| if (unlikely (compile_options.bounds_check) |
| && result->rank != source->rank - zero_count) |
| { |
| fprintf (stderr, "CFI_section: Rank of result must be equal to the " |
| "rank of source minus the number of zeros in strides " |
| "(result->rank = source->rank - zero_count, %d != %d " |
| "- %d).\n", result->rank, source->rank, zero_count); |
| return CFI_INVALID_RANK; |
| } |
| |
| /* Lower bounds. */ |
| if (lower_bounds == NULL) |
| { |
| for (int i = 0; i < source->rank; i++) |
| lower[i] = source->dim[i].lower_bound; |
| } |
| else |
| { |
| for (int i = 0; i < source->rank; i++) |
| lower[i] = lower_bounds[i]; |
| } |
| |
| /* Upper bounds. */ |
| if (upper_bounds == NULL) |
| { |
| if (unlikely (compile_options.bounds_check) |
| && source->dim[source->rank - 1].extent == -1) |
| { |
| fprintf (stderr, "CFI_section: Source must not be an assumed-size " |
| "array if upper_bounds is NULL.\n"); |
| return CFI_INVALID_EXTENT; |
| } |
| |
| for (int i = 0; i < source->rank; i++) |
| upper[i] = source->dim[i].lower_bound + source->dim[i].extent - 1; |
| } |
| else |
| { |
| for (int i = 0; i < source->rank; i++) |
| upper[i] = upper_bounds[i]; |
| } |
| |
| /* Stride */ |
| if (strides == NULL) |
| { |
| for (int i = 0; i < source->rank; i++) |
| stride[i] = 1; |
| } |
| else |
| { |
| for (int i = 0; i < source->rank; i++) |
| { |
| stride[i] = strides[i]; |
| /* If stride[i] == 0 then lower[i] and upper[i] must be equal. */ |
| if (unlikely (compile_options.bounds_check) |
| && stride[i] == 0 && lower[i] != upper[i]) |
| { |
| fprintf (stderr, "CFI_section: If strides[%d] = 0, then " |
| "lower_bounds[%d] = %" PRIiPTR " and " |
| "upper_bounds[%d] = %" PRIiPTR " must be equal.\n", |
| i, i, (ptrdiff_t)lower_bounds[i], i, |
| (ptrdiff_t)upper_bounds[i]); |
| return CFI_ERROR_OUT_OF_BOUNDS; |
| } |
| } |
| } |
| |
| /* Check that section upper and lower bounds are within the array bounds. */ |
| if (unlikely (compile_options.bounds_check)) |
| for (int i = 0; i < source->rank; i++) |
| { |
| bool assumed_size |
| = (i == source->rank - 1 && source->dim[i].extent == -1); |
| CFI_index_t ub |
| = source->dim[i].lower_bound + source->dim[i].extent - 1; |
| if (lower_bounds != NULL |
| && (lower[i] < source->dim[i].lower_bound |
| || (!assumed_size && lower[i] > ub))) |
| { |
| fprintf (stderr, "CFI_section: Lower bounds must be within " |
| "the bounds of the Fortran array " |
| "(source->dim[%d].lower_bound " |
| "<= lower_bounds[%d] <= source->dim[%d].lower_bound " |
| "+ source->dim[%d].extent - 1, " |
| "%" PRIiPTR " <= %" PRIiPTR " <= %" PRIiPTR ").\n", |
| i, i, i, i, |
| (ptrdiff_t)source->dim[i].lower_bound, |
| (ptrdiff_t)lower[i], |
| (ptrdiff_t)ub); |
| return CFI_ERROR_OUT_OF_BOUNDS; |
| } |
| |
| if (upper_bounds != NULL |
| && (upper[i] < source->dim[i].lower_bound |
| || (!assumed_size && upper[i] > ub))) |
| { |
| fprintf (stderr, "CFI_section: Upper bounds must be within " |
| "the bounds of the Fortran array " |
| "(source->dim[%d].lower_bound " |
| "<= upper_bounds[%d] <= source->dim[%d].lower_bound " |
| "+ source->dim[%d].extent - 1, " |
| "%" PRIiPTR " !<= %" PRIiPTR " !<= %" PRIiPTR ").\n", |
| i, i, i, i, |
| (ptrdiff_t)source->dim[i].lower_bound, |
| (ptrdiff_t)upper[i], |
| (ptrdiff_t)ub); |
| return CFI_ERROR_OUT_OF_BOUNDS; |
| } |
| |
| if (upper[i] < lower[i] && stride[i] >= 0) |
| { |
| fprintf (stderr, "CFI_section: If the upper bound is smaller than " |
| "the lower bound for a given dimension (upper[%d] < " |
| "lower[%d], %" PRIiPTR " < %" PRIiPTR "), then the " |
| "stride for said dimension must be negative " |
| "(stride[%d] < 0, %" PRIiPTR " < 0).\n", |
| i, i, (ptrdiff_t)upper[i], (ptrdiff_t)lower[i], |
| i, (ptrdiff_t)stride[i]); |
| return CFI_INVALID_STRIDE; |
| } |
| } |
| |
| /* Set the base address. We have to compute this first in the case |
| where source == result, before we overwrite the dimension data. */ |
| result->base_addr = CFI_address (source, lower); |
| |
| /* Set the appropriate dimension information that gives us access to the |
| * data. */ |
| for (int i = 0, o = 0; i < source->rank; i++) |
| { |
| if (stride[i] == 0) |
| continue; |
| result->dim[o].lower_bound = 0; |
| result->dim[o].extent = 1 + (upper[i] - lower[i])/stride[i]; |
| result->dim[o].sm = stride[i] * source->dim[i].sm; |
| o++; |
| } |
| |
| return CFI_SUCCESS; |
| } |
| |
| |
| int CFI_select_part (CFI_cdesc_t *result, const CFI_cdesc_t *source, |
| size_t displacement, size_t elem_len) |
| { |
| if (unlikely (compile_options.bounds_check)) |
| { |
| /* C descriptors must not be NULL. */ |
| if (source == NULL) |
| { |
| fprintf (stderr, "CFI_select_part: Source must not be NULL.\n"); |
| return CFI_INVALID_DESCRIPTOR; |
| } |
| |
| if (result == NULL) |
| { |
| fprintf (stderr, "CFI_select_part: Result must not be NULL.\n"); |
| return CFI_INVALID_DESCRIPTOR; |
| } |
| |
| /* Attribute of result will be CFI_attribute_other or |
| CFI_attribute_pointer. */ |
| if (result->attribute == CFI_attribute_allocatable) |
| { |
| fprintf (stderr, "CFI_select_part: Result must not describe an " |
| "allocatable object (result->attribute != %d).\n", |
| CFI_attribute_allocatable); |
| return CFI_INVALID_ATTRIBUTE; |
| } |
| |
| /* Base address of source must not be NULL. */ |
| if (source->base_addr == NULL) |
| { |
| fprintf (stderr, "CFI_select_part: Base address of source must " |
| "not be NULL.\n"); |
| return CFI_ERROR_BASE_ADDR_NULL; |
| } |
| |
| /* Source and result must have the same rank. */ |
| if (source->rank != result->rank) |
| { |
| fprintf (stderr, "CFI_select_part: Source and result must have " |
| "the same rank (source->rank = %d, result->rank = %d).\n", |
| (int)source->rank, (int)result->rank); |
| return CFI_INVALID_RANK; |
| } |
| |
| /* Nonallocatable nonpointer must not be an assumed size array. */ |
| if (source->rank > 0 && source->dim[source->rank - 1].extent == -1) |
| { |
| fprintf (stderr, "CFI_select_part: Source must not describe an " |
| "assumed size array (source->dim[%d].extent != -1).\n", |
| source->rank - 1); |
| return CFI_INVALID_DESCRIPTOR; |
| } |
| } |
| |
| /* Element length is ignored unless result->type specifies a Fortran |
| character type. */ |
| if (result->type == CFI_type_char || result->type == CFI_type_ucs4_char) |
| result->elem_len = elem_len; |
| |
| if (unlikely (compile_options.bounds_check)) |
| { |
| /* Ensure displacement is within the bounds of the element length |
| of source.*/ |
| if (displacement > source->elem_len - 1) |
| { |
| fprintf (stderr, "CFI_select_part: Displacement must be within the " |
| "bounds of source (0 <= displacement <= source->elem_len " |
| "- 1, 0 <= %" PRIiPTR " <= %" PRIiPTR ").\n", |
| (ptrdiff_t)displacement, |
| (ptrdiff_t)(source->elem_len - 1)); |
| return CFI_ERROR_OUT_OF_BOUNDS; |
| } |
| |
| /* Ensure displacement and element length of result are less than or |
| equal to the element length of source. */ |
| if (displacement + result->elem_len > source->elem_len) |
| { |
| fprintf (stderr, "CFI_select_part: Displacement plus the element " |
| "length of result must be less than or equal to the " |
| "element length of source (displacement + result->elem_len " |
| "<= source->elem_len, " |
| "%" PRIiPTR " + %" PRIiPTR " = %" PRIiPTR " <= %" PRIiPTR |
| ").\n", |
| (ptrdiff_t)displacement, (ptrdiff_t)result->elem_len, |
| (ptrdiff_t)(displacement + result->elem_len), |
| (ptrdiff_t)source->elem_len); |
| return CFI_ERROR_OUT_OF_BOUNDS; |
| } |
| } |
| |
| if (result->rank > 0) |
| { |
| for (int i = 0; i < result->rank; i++) |
| { |
| result->dim[i].lower_bound = source->dim[i].lower_bound; |
| result->dim[i].extent = source->dim[i].extent; |
| result->dim[i].sm = source->dim[i].sm; |
| } |
| } |
| |
| result->base_addr = (char *) source->base_addr + displacement; |
| return CFI_SUCCESS; |
| } |
| |
| |
| int CFI_setpointer (CFI_cdesc_t *result, CFI_cdesc_t *source, |
| const CFI_index_t lower_bounds[]) |
| { |
| /* Result must not be NULL and must be a Fortran pointer. */ |
| if (unlikely (compile_options.bounds_check)) |
| { |
| if (result == NULL) |
| { |
| fprintf (stderr, "CFI_setpointer: Result is NULL.\n"); |
| return CFI_INVALID_DESCRIPTOR; |
| } |
| |
| if (result->attribute != CFI_attribute_pointer) |
| { |
| fprintf (stderr, "CFI_setpointer: Result shall be the address of a " |
| "C descriptor for a Fortran pointer.\n"); |
| return CFI_INVALID_ATTRIBUTE; |
| } |
| } |
| |
| /* If source is NULL, the result is a C descriptor that describes a |
| * disassociated pointer. */ |
| if (source == NULL) |
| { |
| result->base_addr = NULL; |
| result->version = CFI_VERSION; |
| } |
| else |
| { |
| /* Check that the source is valid and that element lengths, ranks |
| and types of source and result are the same. */ |
| if (unlikely (compile_options.bounds_check)) |
| { |
| if (source->base_addr == NULL |
| && source->attribute == CFI_attribute_allocatable) |
| { |
| fprintf (stderr, "CFI_setpointer: The source is an " |
| "allocatable object but is not allocated.\n"); |
| return CFI_ERROR_BASE_ADDR_NULL; |
| } |
| if (source->rank > 0 |
| && source->dim[source->rank - 1].extent == -1) |
| { |
| fprintf (stderr, "CFI_setpointer: The source is an " |
| "assumed-size array.\n"); |
| return CFI_INVALID_EXTENT; |
| } |
| if (result->elem_len != source->elem_len) |
| { |
| fprintf (stderr, "CFI_setpointer: Element lengths of result " |
| "(result->elem_len = %" PRIiPTR ") and source " |
| "(source->elem_len = %" PRIiPTR ") " |
| " must be the same.\n", |
| (ptrdiff_t)result->elem_len, |
| (ptrdiff_t)source->elem_len); |
| return CFI_INVALID_ELEM_LEN; |
| } |
| |
| if (result->rank != source->rank) |
| { |
| fprintf (stderr, "CFI_setpointer: Ranks of result " |
| "(result->rank = %d) and source (source->rank = %d) " |
| "must be the same.\n", result->rank, source->rank); |
| return CFI_INVALID_RANK; |
| } |
| |
| if (result->type != source->type) |
| { |
| fprintf (stderr, "CFI_setpointer: Types of result " |
| "(result->type = %d) and source (source->type = %d) " |
| "must be the same.\n", result->type, source->type); |
| return CFI_INVALID_TYPE; |
| } |
| } |
| |
| /* If the source is a disassociated pointer, the result must also |
| describe a disassociated pointer. */ |
| if (source->base_addr == NULL |
| && source->attribute == CFI_attribute_pointer) |
| result->base_addr = NULL; |
| else |
| result->base_addr = source->base_addr; |
| |
| /* Assign components to result. */ |
| result->version = source->version; |
| |
| /* Dimension information. */ |
| for (int i = 0; i < source->rank; i++) |
| { |
| if (lower_bounds != NULL) |
| result->dim[i].lower_bound = lower_bounds[i]; |
| else |
| result->dim[i].lower_bound = source->dim[i].lower_bound; |
| |
| result->dim[i].extent = source->dim[i].extent; |
| result->dim[i].sm = source->dim[i].sm; |
| } |
| } |
| |
| return CFI_SUCCESS; |
| } |