
gcc/fortran/ChangeLog: * match.cc (match_exit_cycle): Allow to exit team block. (gfc_match_end_team): Create end_team node also without parameter list. * trans-intrinsic.cc (conv_stat_and_team): Team and team_number only need to be a single pointer. * trans-stmt.cc (trans_associate_var): Create a mapping coarray token for coarray associations or it is not addressed correctly. * trans.h (enum gfc_coarray_regtype): Add mapping mode to coarray register. libgfortran/ChangeLog: * caf/libcaf.h: Add mapping mode to coarray's register. * caf/single.c (_gfortran_caf_register): Create a token sharing another token's memory. (check_team): Check team parameters to coindexed expressions are valid. gcc/testsuite/ChangeLog: * gfortran.dg/coarray/coindexed_3.f08: Add minimal test for get_team(). * gfortran.dg/team_change_2.f90: Add test for change team with label and exiting out of it. * gfortran.dg/team_end_2.f90: Check parsing to labeled team blocks is correct now. * gfortran.dg/team_end_3.f90: Check that end_team call is generated for labeled end_teams, too. * gfortran.dg/coarray/coindexed_5.f90: New test.
1153 lines
30 KiB
C
1153 lines
30 KiB
C
/* 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. */
|
|
}
|