| /* 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 |
| |
| /* 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); |
| } |
| |
| |
| void |
| _gfortran_caf_init (int *argc __attribute__ ((unused)), |
| char ***argv __attribute__ ((unused))) |
| { |
| } |
| |
| |
| 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; |
| } |
| } |
| |
| |
| int |
| _gfortran_caf_this_image (int distance __attribute__ ((unused))) |
| { |
| return 1; |
| } |
| |
| |
| int |
| _gfortran_caf_num_images (int distance __attribute__ ((unused)), |
| int failed __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 |
| 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; |
| single_token->desc = GFC_DESCRIPTOR_RANK (data) > 0 ? data : NULL; |
| |
| |
| 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; |
| } |
| 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 (type != CAF_DEREGTYPE_COARRAY_DEALLOCATE_ONLY) |
| { |
| 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; |
| } |
| |
| 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 __attribute__ ((unused)), |
| int *team_number __attribute__ ((unused))) |
| { |
| 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 (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->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 __attribute__ ((unused)), |
| int *team_number __attribute__ ((unused))) |
| { |
| 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; |
| |
| 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 __attribute__ ((unused)), |
| int *dst_team_number __attribute__ ((unused)), |
| caf_team_t *src_team __attribute__ ((unused)), |
| int *src_team_number __attribute__ ((unused))) |
| { |
| 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 (!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 (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 = 0; |
| return; |
| } |
| |
| |
| if (stat) |
| { |
| *stat = 1; |
| 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 = 1; |
| 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); |
| } |