| /* Single-image implementation of GNU Fortran Coarray Library |
| Copyright (C) 2011-2025 Free Software Foundation, Inc. |
| Contributed by Tobias Burnus <burnus@net-b.de> |
| |
| This file is part of the GNU Fortran Coarray Runtime Library (libcaf). |
| |
| Libcaf 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, or (at your option) |
| any later version. |
| |
| Libcaf 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 "libcaf.h" |
| #include <stdio.h> /* For fputs and fprintf. */ |
| #include <stdlib.h> /* For exit and malloc. */ |
| #include <string.h> /* For memcpy and memset. */ |
| #include <stdarg.h> /* For variadic arguments. */ |
| #include <stdint.h> |
| #include <assert.h> |
| |
| /* Define GFC_CAF_CHECK to enable run-time checking. */ |
| /* #define GFC_CAF_CHECK 1 */ |
| |
| struct caf_single_token |
| { |
| /* The pointer to the memory registered. For arrays this is the data member |
| in the descriptor. For components it's the pure data pointer. */ |
| void *memptr; |
| /* The descriptor when this token is associated to an allocatable array. */ |
| gfc_descriptor_t *desc; |
| /* Set when the caf lib has allocated the memory in memptr and is responsible |
| for freeing it on deregister. */ |
| bool owning_memory; |
| }; |
| typedef struct caf_single_token *caf_single_token_t; |
| |
| #define TOKEN(X) ((caf_single_token_t) (X)) |
| #define MEMTOK(X) ((caf_single_token_t) (X))->memptr |
| |
| struct caf_single_team |
| { |
| struct caf_single_team *parent; |
| int team_no; |
| int index; |
| struct coarray_allocated |
| { |
| struct coarray_allocated *next; |
| caf_single_token_t token; |
| } *allocated; |
| }; |
| typedef struct caf_single_team *caf_single_team_t; |
| /* This points to the most current team. */ |
| static caf_single_team_t caf_team_stack = NULL, caf_initial_team; |
| static caf_single_team_t caf_teams_formed = NULL; |
| |
| /* Single-image implementation of the CAF library. |
| Note: For performance reasons -fcoarry=single should be used |
| rather than this library. */ |
| |
| /* Global variables. */ |
| caf_static_t *caf_static_list = NULL; |
| |
| typedef void (*getter_t) (void *, const int *, void **, int32_t *, void *, |
| caf_token_t, const size_t, size_t *, const size_t *); |
| typedef void (*is_present_t) (void *, const int *, int32_t *, void *, |
| caf_single_token_t, const size_t); |
| typedef void (*receiver_t) (void *, const int *, void *, const void *, |
| caf_token_t, const size_t, const size_t *, |
| const size_t *); |
| struct accessor_hash_t |
| { |
| int hash; |
| int pad; |
| union |
| { |
| getter_t getter; |
| is_present_t is_present; |
| receiver_t receiver; |
| } u; |
| }; |
| |
| static struct accessor_hash_t *accessor_hash_table = NULL; |
| static int aht_cap = 0; |
| static int aht_size = 0; |
| static enum { |
| AHT_UNINITIALIZED, |
| AHT_OPEN, |
| AHT_PREPARED |
| } accessor_hash_table_state |
| = AHT_UNINITIALIZED; |
| |
| /* Keep in sync with mpi.c. */ |
| static void |
| caf_runtime_error (const char *message, ...) |
| { |
| va_list ap; |
| fprintf (stderr, "Fortran runtime error: "); |
| va_start (ap, message); |
| vfprintf (stderr, message, ap); |
| va_end (ap); |
| fprintf (stderr, "\n"); |
| |
| /* FIXME: Shutdown the Fortran RTL to flush the buffer. PR 43849. */ |
| exit (EXIT_FAILURE); |
| } |
| |
| /* Error handling is similar everytime. */ |
| static void |
| caf_internal_error (const char *msg, int *stat, char *errmsg, |
| size_t errmsg_len, ...) |
| { |
| va_list args; |
| va_start (args, errmsg_len); |
| if (stat) |
| { |
| *stat = 1; |
| if (errmsg_len > 0) |
| { |
| int len = snprintf (errmsg, errmsg_len, msg, args); |
| if (len >= 0 && errmsg_len > (size_t) len) |
| memset (&errmsg[len], ' ', errmsg_len - len); |
| } |
| va_end (args); |
| return; |
| } |
| else |
| caf_runtime_error (msg, args); |
| va_end (args); |
| } |
| |
| static void |
| init_caf_team_stack (void) |
| { |
| caf_initial_team = caf_team_stack |
| = (caf_single_team_t) calloc (1, sizeof (struct caf_single_team)); |
| caf_initial_team->team_no = -1; |
| } |
| |
| void |
| _gfortran_caf_init (int *argc __attribute__ ((unused)), |
| char ***argv __attribute__ ((unused))) |
| { |
| if (likely (!caf_team_stack)) |
| init_caf_team_stack (); |
| } |
| |
| static void |
| free_team_list (caf_single_team_t l) |
| { |
| while (l != NULL) |
| { |
| caf_single_team_t p = l->parent; |
| struct coarray_allocated *ca = l->allocated; |
| while (ca) |
| { |
| struct coarray_allocated *nca = ca->next; |
| free (ca); |
| ca = nca; |
| } |
| free (l); |
| l = p; |
| } |
| } |
| |
| void |
| _gfortran_caf_finalize (void) |
| { |
| free (accessor_hash_table); |
| |
| while (caf_static_list != NULL) |
| { |
| caf_static_t *tmp = caf_static_list->prev; |
| free (((caf_single_token_t) caf_static_list->token)->memptr); |
| free (caf_static_list->token); |
| free (caf_static_list); |
| caf_static_list = tmp; |
| } |
| |
| free_team_list (caf_team_stack); |
| caf_initial_team = caf_team_stack = NULL; |
| free_team_list (caf_teams_formed); |
| caf_teams_formed = NULL; |
| } |
| |
| int |
| _gfortran_caf_this_image (caf_team_t team) |
| { |
| return team ? ((caf_single_team_t) team)->index : 1; |
| } |
| |
| int |
| _gfortran_caf_num_images (caf_team_t team __attribute__ ((unused)), |
| int32_t *team_number __attribute__ ((unused))) |
| { |
| return 1; |
| } |
| |
| |
| void |
| _gfortran_caf_register (size_t size, caf_register_t type, caf_token_t *token, |
| gfc_descriptor_t *data, int *stat, char *errmsg, |
| size_t errmsg_len) |
| { |
| const char alloc_fail_msg[] = "Failed to allocate coarray"; |
| void *local; |
| caf_single_token_t single_token; |
| |
| if (type == CAF_REGTYPE_LOCK_STATIC || type == CAF_REGTYPE_LOCK_ALLOC |
| || type == CAF_REGTYPE_CRITICAL) |
| local = calloc (size, sizeof (bool)); |
| else if (type == CAF_REGTYPE_EVENT_STATIC || type == CAF_REGTYPE_EVENT_ALLOC) |
| /* In the event_(wait|post) function the counter for events is a uint32, |
| so better allocate enough memory here. */ |
| local = calloc (size, sizeof (uint32_t)); |
| else if (type == CAF_REGTYPE_COARRAY_ALLOC_REGISTER_ONLY) |
| local = NULL; |
| else if (type == CAF_REGTYPE_COARRAY_MAP_EXISTING) |
| local = GFC_DESCRIPTOR_DATA (data); |
| else |
| local = malloc (size); |
| |
| if (type != CAF_REGTYPE_COARRAY_ALLOC_ALLOCATE_ONLY) |
| *token = malloc (sizeof (struct caf_single_token)); |
| |
| if (unlikely (*token == NULL |
| || (local == NULL |
| && type != CAF_REGTYPE_COARRAY_ALLOC_REGISTER_ONLY))) |
| { |
| /* Freeing the memory conditionally seems pointless, but |
| caf_internal_error () may return, when a stat is given and then the |
| memory may be lost. */ |
| free (local); |
| free (*token); |
| caf_internal_error (alloc_fail_msg, stat, errmsg, errmsg_len); |
| return; |
| } |
| |
| single_token = TOKEN (*token); |
| single_token->memptr = local; |
| single_token->owning_memory = type != CAF_REGTYPE_COARRAY_ALLOC_REGISTER_ONLY |
| && type != CAF_REGTYPE_COARRAY_MAP_EXISTING; |
| single_token->desc = GFC_DESCRIPTOR_RANK (data) > 0 ? data : NULL; |
| |
| if (unlikely (!caf_team_stack)) |
| init_caf_team_stack (); |
| |
| if (stat) |
| *stat = 0; |
| |
| if (type == CAF_REGTYPE_COARRAY_STATIC || type == CAF_REGTYPE_LOCK_STATIC |
| || type == CAF_REGTYPE_CRITICAL || type == CAF_REGTYPE_EVENT_STATIC |
| || type == CAF_REGTYPE_EVENT_ALLOC) |
| { |
| caf_static_t *tmp = malloc (sizeof (caf_static_t)); |
| tmp->prev = caf_static_list; |
| tmp->token = *token; |
| caf_static_list = tmp; |
| } |
| else |
| { |
| struct coarray_allocated *ca = caf_team_stack->allocated; |
| for (; ca && ca->token != single_token; ca = ca->next) |
| ; |
| if (!ca) |
| { |
| ca = (struct coarray_allocated *) malloc ( |
| sizeof (struct coarray_allocated)); |
| *ca = (struct coarray_allocated) {caf_team_stack->allocated, |
| single_token}; |
| caf_team_stack->allocated = ca; |
| } |
| } |
| GFC_DESCRIPTOR_DATA (data) = local; |
| } |
| |
| |
| void |
| _gfortran_caf_deregister (caf_token_t *token, caf_deregister_t type, int *stat, |
| char *errmsg __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| caf_single_token_t single_token = TOKEN (*token); |
| |
| if (single_token->owning_memory && single_token->memptr) |
| { |
| free (single_token->memptr); |
| if (single_token->desc) |
| GFC_DESCRIPTOR_DATA (single_token->desc) = NULL; |
| } |
| |
| if (type != CAF_DEREGTYPE_COARRAY_DEALLOCATE_ONLY) |
| { |
| struct coarray_allocated *ca = caf_team_stack->allocated; |
| if (ca && caf_team_stack->allocated->token == single_token) |
| caf_team_stack->allocated = ca->next; |
| else |
| { |
| struct coarray_allocated *pca = NULL; |
| for (; ca && ca->token != single_token; pca = ca, ca = ca->next) |
| ; |
| if (!ca) |
| caf_runtime_error ( |
| "Coarray token to be freeed is not in current team %d", type); |
| /* Unhook found coarray_allocated node from list... */ |
| pca->next = ca->next; |
| } |
| /* ... and free. */ |
| free (ca); |
| free (TOKEN (*token)); |
| *token = NULL; |
| } |
| else |
| { |
| single_token->memptr = NULL; |
| single_token->owning_memory = false; |
| } |
| |
| if (stat) |
| *stat = 0; |
| } |
| |
| |
| void |
| _gfortran_caf_sync_all (int *stat, |
| char *errmsg __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| __asm__ __volatile__ ("":::"memory"); |
| if (stat) |
| *stat = 0; |
| } |
| |
| |
| void |
| _gfortran_caf_sync_memory (int *stat, |
| char *errmsg __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| __asm__ __volatile__ ("":::"memory"); |
| if (stat) |
| *stat = 0; |
| } |
| |
| |
| void |
| _gfortran_caf_sync_images (int count __attribute__ ((unused)), |
| int images[] __attribute__ ((unused)), |
| int *stat, |
| char *errmsg __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| #ifdef GFC_CAF_CHECK |
| int i; |
| |
| for (i = 0; i < count; i++) |
| if (images[i] != 1) |
| { |
| fprintf (stderr, "COARRAY ERROR: Invalid image index %d to SYNC " |
| "IMAGES", images[i]); |
| exit (EXIT_FAILURE); |
| } |
| #endif |
| |
| __asm__ __volatile__ ("":::"memory"); |
| if (stat) |
| *stat = 0; |
| } |
| |
| extern void _gfortran_report_exception (void); |
| |
| void |
| _gfortran_caf_stop_numeric(int stop_code, bool quiet) |
| { |
| if (!quiet) |
| { |
| _gfortran_report_exception (); |
| fprintf (stderr, "STOP %d\n", stop_code); |
| } |
| exit (stop_code); |
| } |
| |
| |
| void |
| _gfortran_caf_stop_str(const char *string, size_t len, bool quiet) |
| { |
| if (!quiet) |
| { |
| _gfortran_report_exception (); |
| fputs ("STOP ", stderr); |
| while (len--) |
| fputc (*(string++), stderr); |
| fputs ("\n", stderr); |
| } |
| exit (0); |
| } |
| |
| |
| void |
| _gfortran_caf_error_stop_str (const char *string, size_t len, bool quiet) |
| { |
| if (!quiet) |
| { |
| _gfortran_report_exception (); |
| fputs ("ERROR STOP ", stderr); |
| while (len--) |
| fputc (*(string++), stderr); |
| fputs ("\n", stderr); |
| } |
| exit (1); |
| } |
| |
| |
| /* Reported that the program terminated because of a fail image issued. |
| Because this is a single image library, nothing else than aborting the whole |
| program can be done. */ |
| |
| void _gfortran_caf_fail_image (void) |
| { |
| fputs ("IMAGE FAILED!\n", stderr); |
| exit (0); |
| } |
| |
| |
| /* Get the status of image IMAGE. Because being the single image library all |
| other images are reported to be stopped. */ |
| |
| int _gfortran_caf_image_status (int image, |
| caf_team_t * team __attribute__ ((unused))) |
| { |
| if (image == 1) |
| return 0; |
| else |
| return CAF_STAT_STOPPED_IMAGE; |
| } |
| |
| |
| /* Single image library. There cannot be any failed images with only one |
| image. */ |
| |
| void |
| _gfortran_caf_failed_images (gfc_descriptor_t *array, |
| caf_team_t * team __attribute__ ((unused)), |
| int * kind) |
| { |
| int local_kind = kind != NULL ? *kind : 4; |
| |
| array->base_addr = NULL; |
| array->dtype.type = BT_INTEGER; |
| array->dtype.elem_len = local_kind; |
| /* Setting lower_bound higher then upper_bound is what the compiler does to |
| indicate an empty array. */ |
| array->dim[0].lower_bound = 0; |
| array->dim[0]._ubound = -1; |
| array->dim[0]._stride = 1; |
| array->offset = 0; |
| } |
| |
| |
| /* With only one image available no other images can be stopped. Therefore |
| return an empty array. */ |
| |
| void |
| _gfortran_caf_stopped_images (gfc_descriptor_t *array, |
| caf_team_t * team __attribute__ ((unused)), |
| int * kind) |
| { |
| int local_kind = kind != NULL ? *kind : 4; |
| |
| array->base_addr = NULL; |
| array->dtype.type = BT_INTEGER; |
| array->dtype.elem_len = local_kind; |
| /* Setting lower_bound higher then upper_bound is what the compiler does to |
| indicate an empty array. */ |
| array->dim[0].lower_bound = 0; |
| array->dim[0]._ubound = -1; |
| array->dim[0]._stride = 1; |
| array->offset = 0; |
| } |
| |
| |
| void |
| _gfortran_caf_error_stop (int error, bool quiet) |
| { |
| if (!quiet) |
| { |
| _gfortran_report_exception (); |
| fprintf (stderr, "ERROR STOP %d\n", error); |
| } |
| exit (error); |
| } |
| |
| |
| void |
| _gfortran_caf_co_broadcast (gfc_descriptor_t *a __attribute__ ((unused)), |
| int source_image __attribute__ ((unused)), |
| int *stat, char *errmsg __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| if (stat) |
| *stat = 0; |
| } |
| |
| void |
| _gfortran_caf_co_sum (gfc_descriptor_t *a __attribute__ ((unused)), |
| int result_image __attribute__ ((unused)), |
| int *stat, char *errmsg __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| if (stat) |
| *stat = 0; |
| } |
| |
| void |
| _gfortran_caf_co_min (gfc_descriptor_t *a __attribute__ ((unused)), |
| int result_image __attribute__ ((unused)), |
| int *stat, char *errmsg __attribute__ ((unused)), |
| int a_len __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| if (stat) |
| *stat = 0; |
| } |
| |
| void |
| _gfortran_caf_co_max (gfc_descriptor_t *a __attribute__ ((unused)), |
| int result_image __attribute__ ((unused)), |
| int *stat, char *errmsg __attribute__ ((unused)), |
| int a_len __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| if (stat) |
| *stat = 0; |
| } |
| |
| |
| void |
| _gfortran_caf_co_reduce (gfc_descriptor_t *a __attribute__ ((unused)), |
| void * (*opr) (void *, void *) |
| __attribute__ ((unused)), |
| int opr_flags __attribute__ ((unused)), |
| int result_image __attribute__ ((unused)), |
| int *stat, char *errmsg __attribute__ ((unused)), |
| int a_len __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| if (stat) |
| *stat = 0; |
| } |
| |
| |
| void |
| _gfortran_caf_register_accessor (const int hash, getter_t accessor) |
| { |
| if (accessor_hash_table_state == AHT_UNINITIALIZED) |
| { |
| aht_cap = 16; |
| accessor_hash_table = calloc (aht_cap, sizeof (struct accessor_hash_t)); |
| accessor_hash_table_state = AHT_OPEN; |
| } |
| if (aht_size == aht_cap) |
| { |
| aht_cap += 16; |
| accessor_hash_table = realloc (accessor_hash_table, |
| aht_cap * sizeof (struct accessor_hash_t)); |
| } |
| if (accessor_hash_table_state == AHT_PREPARED) |
| { |
| accessor_hash_table_state = AHT_OPEN; |
| } |
| accessor_hash_table[aht_size].hash = hash; |
| accessor_hash_table[aht_size].u.getter = accessor; |
| ++aht_size; |
| } |
| |
| static int |
| hash_compare (const struct accessor_hash_t *lhs, |
| const struct accessor_hash_t *rhs) |
| { |
| return lhs->hash < rhs->hash ? -1 : (lhs->hash > rhs->hash ? 1 : 0); |
| } |
| |
| void |
| _gfortran_caf_register_accessors_finish (void) |
| { |
| if (accessor_hash_table_state == AHT_PREPARED |
| || accessor_hash_table_state == AHT_UNINITIALIZED) |
| return; |
| |
| qsort (accessor_hash_table, aht_size, sizeof (struct accessor_hash_t), |
| (int (*) (const void *, const void *)) hash_compare); |
| accessor_hash_table_state = AHT_PREPARED; |
| } |
| |
| int |
| _gfortran_caf_get_remote_function_index (const int hash) |
| { |
| if (accessor_hash_table_state != AHT_PREPARED) |
| { |
| caf_runtime_error ("the accessor hash table is not prepared."); |
| } |
| |
| struct accessor_hash_t cand; |
| cand.hash = hash; |
| struct accessor_hash_t *f |
| = bsearch (&cand, accessor_hash_table, aht_size, |
| sizeof (struct accessor_hash_t), |
| (int (*) (const void *, const void *)) hash_compare); |
| |
| int index = f ? f - accessor_hash_table : -1; |
| return index; |
| } |
| |
| static bool |
| check_team (caf_team_t *team, int *team_number, int *stat) |
| { |
| if (team || team_number) |
| { |
| caf_single_team_t cur = caf_team_stack; |
| |
| if (team) |
| { |
| caf_single_team_t single_team = (caf_single_team_t) (*team); |
| while (cur && cur != single_team) |
| cur = cur->parent; |
| } |
| else |
| while (cur && cur->team_no != *team_number) |
| cur = cur->parent; |
| |
| if (!cur) |
| { |
| if (stat) |
| { |
| *stat = 1; |
| return false; |
| } |
| else |
| caf_runtime_error ("requested team not found"); |
| } |
| } |
| return true; |
| } |
| |
| void |
| _gfortran_caf_get_from_remote ( |
| caf_token_t token, const gfc_descriptor_t *opt_src_desc, |
| const size_t *opt_src_charlen, const int image_index, |
| const size_t dst_size __attribute__ ((unused)), void **dst_data, |
| size_t *opt_dst_charlen, gfc_descriptor_t *opt_dst_desc, |
| const bool may_realloc_dst, const int getter_index, void *add_data, |
| const size_t add_data_size __attribute__ ((unused)), int *stat, |
| caf_team_t *team, int *team_number) |
| { |
| caf_single_token_t single_token = TOKEN (token); |
| void *src_ptr = opt_src_desc ? (void *) opt_src_desc : single_token->memptr; |
| int32_t free_buffer; |
| void *dst_ptr = opt_dst_desc ? (void *)opt_dst_desc : dst_data; |
| void *old_dst_data_ptr = NULL; |
| struct caf_single_token cb_token; |
| cb_token.memptr = add_data; |
| cb_token.desc = NULL; |
| cb_token.owning_memory = false; |
| |
| if (stat) |
| *stat = 0; |
| |
| if (!check_team (team, team_number, stat)) |
| return; |
| |
| if (opt_dst_desc && !may_realloc_dst) |
| { |
| old_dst_data_ptr = opt_dst_desc->base_addr; |
| opt_dst_desc->base_addr = NULL; |
| } |
| |
| accessor_hash_table[getter_index].u.getter (add_data, &image_index, dst_ptr, |
| &free_buffer, src_ptr, &cb_token, |
| 0, opt_dst_charlen, |
| opt_src_charlen); |
| if (opt_dst_desc && old_dst_data_ptr && !may_realloc_dst |
| && opt_dst_desc->base_addr != old_dst_data_ptr) |
| { |
| size_t dsize = opt_dst_desc->span; |
| for (int i = 0; i < GFC_DESCRIPTOR_RANK (opt_dst_desc); ++i) |
| dsize *= GFC_DESCRIPTOR_EXTENT (opt_dst_desc, i); |
| memcpy (old_dst_data_ptr, opt_dst_desc->base_addr, dsize); |
| free (opt_dst_desc->base_addr); |
| opt_dst_desc->base_addr = old_dst_data_ptr; |
| } |
| } |
| |
| int32_t |
| _gfortran_caf_is_present_on_remote (caf_token_t token, const int image_index, |
| const int present_index, void *add_data, |
| const size_t add_data_size |
| __attribute__ ((unused))) |
| { |
| /* Unregistered tokens are always not present. */ |
| if (!token) |
| return 0; |
| |
| caf_single_token_t single_token = TOKEN (token); |
| int32_t result; |
| struct caf_single_token cb_token = {add_data, NULL, false}; |
| |
| accessor_hash_table[present_index].u.is_present ( |
| add_data, &image_index, &result, |
| single_token->desc ? single_token->desc : (void *) &single_token->memptr, |
| &cb_token, 0); |
| |
| return result; |
| } |
| |
| void |
| _gfortran_caf_send_to_remote ( |
| caf_token_t token, gfc_descriptor_t *opt_dst_desc, |
| const size_t *opt_dst_charlen, const int image_index, |
| const size_t src_size __attribute__ ((unused)), const void *src_data, |
| const size_t *opt_src_charlen, const gfc_descriptor_t *opt_src_desc, |
| const int accessor_index, void *add_data, |
| const size_t add_data_size __attribute__ ((unused)), int *stat, |
| caf_team_t *team, int *team_number) |
| { |
| caf_single_token_t single_token = TOKEN (token); |
| void *dst_ptr = opt_dst_desc ? (void *) opt_dst_desc : single_token->memptr; |
| const void *src_ptr = opt_src_desc ? (void *) opt_src_desc : src_data; |
| struct caf_single_token cb_token; |
| cb_token.memptr = add_data; |
| cb_token.desc = NULL; |
| cb_token.owning_memory = false; |
| |
| if (stat) |
| *stat = 0; |
| |
| if (!check_team (team, team_number, stat)) |
| return; |
| |
| accessor_hash_table[accessor_index].u.receiver (add_data, &image_index, |
| dst_ptr, src_ptr, &cb_token, |
| 0, opt_dst_charlen, |
| opt_src_charlen); |
| } |
| |
| void |
| _gfortran_caf_transfer_between_remotes ( |
| caf_token_t dst_token, gfc_descriptor_t *opt_dst_desc, |
| size_t *opt_dst_charlen, const int dst_image_index, |
| const int dst_access_index, void *dst_add_data, |
| const size_t dst_add_data_size __attribute__ ((unused)), |
| caf_token_t src_token, const gfc_descriptor_t *opt_src_desc, |
| const size_t *opt_src_charlen, const int src_image_index, |
| const int src_access_index, void *src_add_data, |
| const size_t src_add_data_size __attribute__ ((unused)), |
| const size_t src_size, const bool scalar_transfer, int *dst_stat, |
| int *src_stat, caf_team_t *dst_team, int *dst_team_number, |
| caf_team_t *src_team, int *src_team_number) |
| { |
| caf_single_token_t src_single_token = TOKEN (src_token), |
| dst_single_token = TOKEN (dst_token); |
| void *src_ptr |
| = opt_src_desc ? (void *) opt_src_desc : src_single_token->memptr; |
| int32_t free_buffer; |
| void *dst_ptr |
| = opt_dst_desc ? (void *) opt_dst_desc : dst_single_token->memptr; |
| void *transfer_ptr, *buffer; |
| GFC_FULL_ARRAY_DESCRIPTOR (GFC_MAX_DIMENSIONS, void) *transfer_desc = NULL; |
| struct caf_single_token cb_token; |
| cb_token.memptr = src_add_data; |
| cb_token.desc = NULL; |
| cb_token.owning_memory = false; |
| |
| if (src_stat) |
| *src_stat = 0; |
| |
| if (!check_team (src_team, src_team_number, src_stat)) |
| return; |
| |
| if (!scalar_transfer) |
| { |
| const size_t desc_size = sizeof (*transfer_desc); |
| transfer_desc = __builtin_alloca (desc_size); |
| memset (transfer_desc, 0, desc_size); |
| transfer_ptr = transfer_desc; |
| } |
| else if (opt_dst_charlen) |
| transfer_ptr = __builtin_alloca (*opt_dst_charlen * src_size); |
| else |
| { |
| buffer = NULL; |
| transfer_ptr = &buffer; |
| } |
| |
| accessor_hash_table[src_access_index].u.getter ( |
| src_add_data, &src_image_index, transfer_ptr, &free_buffer, src_ptr, |
| &cb_token, 0, opt_dst_charlen, opt_src_charlen); |
| |
| if (dst_stat) |
| *dst_stat = 0; |
| |
| if (!check_team (dst_team, dst_team_number, dst_stat)) |
| return; |
| |
| if (scalar_transfer) |
| transfer_ptr = *(void **) transfer_ptr; |
| |
| cb_token.memptr = dst_add_data; |
| accessor_hash_table[dst_access_index].u.receiver (dst_add_data, |
| &dst_image_index, dst_ptr, |
| transfer_ptr, &cb_token, 0, |
| opt_dst_charlen, |
| opt_src_charlen); |
| |
| if (free_buffer) |
| free (transfer_desc ? transfer_desc->base_addr : transfer_ptr); |
| } |
| |
| void |
| _gfortran_caf_atomic_define (caf_token_t token, size_t offset, |
| int image_index __attribute__ ((unused)), |
| void *value, int *stat, |
| int type __attribute__ ((unused)), int kind) |
| { |
| assert(kind == 4); |
| |
| uint32_t *atom = (uint32_t *) ((char *) MEMTOK (token) + offset); |
| |
| __atomic_store (atom, (uint32_t *) value, __ATOMIC_RELAXED); |
| |
| if (stat) |
| *stat = 0; |
| } |
| |
| void |
| _gfortran_caf_atomic_ref (caf_token_t token, size_t offset, |
| int image_index __attribute__ ((unused)), |
| void *value, int *stat, |
| int type __attribute__ ((unused)), int kind) |
| { |
| assert(kind == 4); |
| |
| uint32_t *atom = (uint32_t *) ((char *) MEMTOK (token) + offset); |
| |
| __atomic_load (atom, (uint32_t *) value, __ATOMIC_RELAXED); |
| |
| if (stat) |
| *stat = 0; |
| } |
| |
| |
| void |
| _gfortran_caf_atomic_cas (caf_token_t token, size_t offset, |
| int image_index __attribute__ ((unused)), |
| void *old, void *compare, void *new_val, int *stat, |
| int type __attribute__ ((unused)), int kind) |
| { |
| assert(kind == 4); |
| |
| uint32_t *atom = (uint32_t *) ((char *) MEMTOK (token) + offset); |
| |
| *(uint32_t *) old = *(uint32_t *) compare; |
| (void) __atomic_compare_exchange_n (atom, (uint32_t *) old, |
| *(uint32_t *) new_val, false, |
| __ATOMIC_RELAXED, __ATOMIC_RELAXED); |
| if (stat) |
| *stat = 0; |
| } |
| |
| |
| void |
| _gfortran_caf_atomic_op (int op, caf_token_t token, size_t offset, |
| int image_index __attribute__ ((unused)), |
| void *value, void *old, int *stat, |
| int type __attribute__ ((unused)), int kind) |
| { |
| assert(kind == 4); |
| |
| uint32_t res; |
| uint32_t *atom = (uint32_t *) ((char *) MEMTOK (token) + offset); |
| |
| switch (op) |
| { |
| case GFC_CAF_ATOMIC_ADD: |
| res = __atomic_fetch_add (atom, *(uint32_t *) value, __ATOMIC_RELAXED); |
| break; |
| case GFC_CAF_ATOMIC_AND: |
| res = __atomic_fetch_and (atom, *(uint32_t *) value, __ATOMIC_RELAXED); |
| break; |
| case GFC_CAF_ATOMIC_OR: |
| res = __atomic_fetch_or (atom, *(uint32_t *) value, __ATOMIC_RELAXED); |
| break; |
| case GFC_CAF_ATOMIC_XOR: |
| res = __atomic_fetch_xor (atom, *(uint32_t *) value, __ATOMIC_RELAXED); |
| break; |
| default: |
| __builtin_unreachable(); |
| } |
| |
| if (old) |
| *(uint32_t *) old = res; |
| |
| if (stat) |
| *stat = 0; |
| } |
| |
| void |
| _gfortran_caf_event_post (caf_token_t token, size_t index, |
| int image_index __attribute__ ((unused)), |
| int *stat, char *errmsg __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| uint32_t value = 1; |
| uint32_t *event = (uint32_t *) ((char *) MEMTOK (token) + index |
| * sizeof (uint32_t)); |
| __atomic_fetch_add (event, (uint32_t) value, __ATOMIC_RELAXED); |
| |
| if(stat) |
| *stat = 0; |
| } |
| |
| void |
| _gfortran_caf_event_wait (caf_token_t token, size_t index, |
| int until_count, int *stat, |
| char *errmsg __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| uint32_t *event = (uint32_t *) ((char *) MEMTOK (token) + index |
| * sizeof (uint32_t)); |
| uint32_t value = (uint32_t)-until_count; |
| __atomic_fetch_add (event, (uint32_t) value, __ATOMIC_RELAXED); |
| |
| if(stat) |
| *stat = 0; |
| } |
| |
| void |
| _gfortran_caf_event_query (caf_token_t token, size_t index, |
| int image_index __attribute__ ((unused)), |
| int *count, int *stat) |
| { |
| uint32_t *event = (uint32_t *) ((char *) MEMTOK (token) + index |
| * sizeof (uint32_t)); |
| __atomic_load (event, (uint32_t *) count, __ATOMIC_RELAXED); |
| |
| if(stat) |
| *stat = 0; |
| } |
| |
| void |
| _gfortran_caf_lock (caf_token_t token, size_t index, |
| int image_index __attribute__ ((unused)), |
| int *acquired_lock, int *stat, char *errmsg, |
| size_t errmsg_len) |
| { |
| const char *msg = "Already locked"; |
| bool *lock = &((bool *) MEMTOK (token))[index]; |
| |
| if (!*lock) |
| { |
| *lock = true; |
| if (acquired_lock) |
| *acquired_lock = (int) true; |
| if (stat) |
| *stat = 0; |
| return; |
| } |
| |
| if (acquired_lock) |
| { |
| *acquired_lock = (int) false; |
| if (stat) |
| *stat = GFC_STAT_LOCKED; |
| return; |
| } |
| |
| |
| if (stat) |
| { |
| *stat = GFC_STAT_LOCKED; |
| if (errmsg_len > 0) |
| { |
| size_t len = (sizeof (msg) > errmsg_len) ? errmsg_len |
| : sizeof (msg); |
| memcpy (errmsg, msg, len); |
| if (errmsg_len > len) |
| memset (&errmsg[len], ' ', errmsg_len-len); |
| } |
| return; |
| } |
| _gfortran_caf_error_stop_str (msg, strlen (msg), false); |
| } |
| |
| |
| void |
| _gfortran_caf_unlock (caf_token_t token, size_t index, |
| int image_index __attribute__ ((unused)), |
| int *stat, char *errmsg, size_t errmsg_len) |
| { |
| const char *msg = "Variable is not locked"; |
| bool *lock = &((bool *) MEMTOK (token))[index]; |
| |
| if (*lock) |
| { |
| *lock = false; |
| if (stat) |
| *stat = 0; |
| return; |
| } |
| |
| if (stat) |
| { |
| *stat = GFC_STAT_UNLOCKED; |
| if (errmsg_len > 0) |
| { |
| size_t len = (sizeof (msg) > errmsg_len) ? errmsg_len |
| : sizeof (msg); |
| memcpy (errmsg, msg, len); |
| if (errmsg_len > len) |
| memset (&errmsg[len], ' ', errmsg_len-len); |
| } |
| return; |
| } |
| _gfortran_caf_error_stop_str (msg, strlen (msg), false); |
| } |
| |
| |
| /* Reference the libraries implementation. */ |
| extern void _gfortran_random_init (int32_t, int32_t, int32_t); |
| |
| void _gfortran_caf_random_init (bool repeatable, bool image_distinct) |
| { |
| /* In a single image implementation always forward to the gfortran |
| routine. */ |
| _gfortran_random_init (repeatable, image_distinct, 1); |
| } |
| |
| void |
| _gfortran_caf_form_team (int team_no, caf_team_t *team, int *new_index, |
| int *stat, char *errmsg __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| const char alloc_fail_msg[] = "Failed to allocate team"; |
| caf_single_team_t t; |
| if (stat) |
| *stat = 0; |
| |
| *team = malloc (sizeof (struct caf_single_team)); |
| if (unlikely (*team == NULL)) |
| { |
| caf_internal_error (alloc_fail_msg, stat, errmsg, errmsg_len); |
| return; |
| } |
| t = *((caf_single_team_t *) team); |
| t->parent = caf_teams_formed; |
| t->team_no = team_no; |
| t->index = new_index ? *new_index : 1; |
| t->allocated = NULL; |
| caf_teams_formed = t; |
| } |
| |
| void |
| _gfortran_caf_change_team (caf_team_t team, int *stat, |
| char *errmsg __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| caf_single_team_t t = (caf_single_team_t) team; |
| |
| if (stat) |
| *stat = 0; |
| |
| if (t == caf_teams_formed) |
| caf_teams_formed = t->parent; |
| else |
| for (caf_single_team_t p = caf_teams_formed; p; p = p->parent) |
| if (p->parent == t) |
| { |
| p->parent = t->parent; |
| break; |
| } |
| |
| t->parent = caf_team_stack; |
| caf_team_stack = t; |
| } |
| |
| void |
| _gfortran_caf_end_team (int *stat, char *errmsg, size_t errmsg_len) |
| { |
| caf_single_team_t t = caf_team_stack; |
| |
| if (stat) |
| *stat = 0; |
| |
| caf_team_stack = caf_team_stack->parent; |
| for (struct coarray_allocated *ca = t->allocated; ca;) |
| { |
| struct coarray_allocated *nca = ca->next; |
| _gfortran_caf_deregister ((caf_token_t *) &ca->token, |
| CAF_DEREGTYPE_COARRAY_DEALLOCATE_ONLY, stat, |
| errmsg, errmsg_len); |
| free (ca); |
| ca = nca; |
| } |
| t->allocated = NULL; |
| t->parent = caf_teams_formed; |
| caf_teams_formed = t; |
| } |
| |
| void |
| _gfortran_caf_sync_team (caf_team_t team __attribute__ ((unused)), int *stat, |
| char *errmsg __attribute__ ((unused)), |
| size_t errmsg_len __attribute__ ((unused))) |
| { |
| if (stat) |
| *stat = 0; |
| } |
| |
| int |
| _gfortran_caf_team_number (caf_team_t team) |
| { |
| return ((caf_single_team_t) team)->team_no; |
| } |
| |
| caf_team_t |
| _gfortran_caf_get_team (int32_t *level) |
| { |
| if (!level) |
| return caf_team_stack; |
| |
| switch ((caf_team_level_t) *level) |
| { |
| case CAF_INITIAL_TEAM: |
| return caf_initial_team; |
| case CAF_PARENT_TEAM: |
| return caf_team_stack->parent ? caf_team_stack->parent : caf_team_stack; |
| case CAF_CURRENT_TEAM: |
| return caf_team_stack; |
| default: |
| caf_runtime_error ("Illegal value for GET_TEAM"); |
| } |
| return NULL; /* To prevent any warnings. */ |
| } |