remove OpenMP Runtime, move runtime to an own package

This commit is contained in:
Stefan Lankes 2018-07-21 22:18:33 +00:00 committed by Jens Breitbart
parent 748744cdf9
commit f8f43cc82b
44 changed files with 1 additions and 11186 deletions

4
.gitmodules vendored
View File

@ -2,10 +2,6 @@
path = lwip
url = https://github.com/hermitcore/LwIP.git
branch = hermit
[submodule "usr/libomp"]
path = usr/libomp
url = https://github.com/hermitcore/libomp_oss.git
branch = hermit
[submodule "caves"]
path = caves
url = https://github.com/hermitcore/hermit-caves.git

View File

@ -159,16 +159,6 @@ if("${TARGET_ARCH}" STREQUAL "x86_64-hermit")
build_external(arch_x86_loader ${HERMIT_ROOT}/arch/x86_64/loader "")
## Intel's OpenMP runtime for x86 (libomp)
build_external(libiomp ${HERMIT_ROOT}/usr/libomp ""
-DHERMIT=1
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}/${TARGET_ARCH})
# libomp is part of HermitCore's runtime and should be available before any
# application will link
add_dependencies(hermit libiomp)
## iRCCE
build_external(ircce ${HERMIT_ROOT}/usr/ircce "")
add_dependencies(hermit ircce)
@ -181,11 +171,9 @@ add_dependencies(hermit xray)
endif()
## Tests and benchmarks
if("${TARGET_ARCH}" STREQUAL "x86_64-hermit")
build_external(tests ${HERMIT_ROOT}/usr/tests hermit)
build_external(benchmarks ${HERMIT_ROOT}/usr/benchmarks hermit)
build_external(openmpbench ${HERMIT_ROOT}/usr/openmpbench hermit)
endif()
## relocate the local prefix to our install destination
install(DIRECTORY ${LOCAL_PREFIX_DIR}/

View File

@ -36,7 +36,7 @@ apt-get install -y qemu-system-x86 cmake wget curl gnupg checkinstall gawk dialo
echo "deb [trusted=yes] https://dl.bintray.com/hermitcore/ubuntu bionic main" | tee -a /etc/apt/sources.list
apt-get -qq update
apt-get install -y --allow-unauthenticated binutils-hermit newlib-hermit pte-hermit gcc-hermit #gcc-hermit-bootstrap
apt-get install -y --allow-unauthenticated -o Dpkg::Options::="--force-overwrite" binutils-hermit newlib-hermit pte-hermit gcc-hermit libomp-hermit #gcc-hermit-bootstrap
export PATH=/opt/hermit/bin:$PATH
mkdir -p build
@ -46,7 +46,6 @@ make hermit-bootstrap
make hermit-bootstrap-install
rm -rf *
cmake -DTOOLCHAIN_BIN_DIR=/opt/hermit/bin -DCMAKE_INSTALL_PREFIX=/opt/hermit ..
install -m 644 ../usr/libomp/libgomp.spec /opt/hermit/x86_64-hermit/lib
make -j1 package
cd ..

View File

@ -1,44 +0,0 @@
NEWLIB = ../x86/x86_64-hermit
MAKE = make
ARFLAGS_FOR_TARGET = rsv
CP = cp
C_source = $(wildcard *.c)
NAME = libgomp.a
OBJS = $(C_source:.c=.o)
#
# Prettify output
V = 0
ifeq ($V,0)
Q = @
P = > /dev/null
endif
# other implicit rules
%.o : %.c
@echo [CC] $@
$Q$(CC_FOR_TARGET) -c $(CFLAGS_FOR_TARGET) -o $@ $<
default: all
all: $(NAME)
$(NAME): $(OBJS)
$Q$(AR_FOR_TARGET) $(ARFLAGS_FOR_TARGET) $@ $(OBJS)
$Q$(CP) $@ $(NEWLIB)/lib
$Q$(CP) libgomp.spec $(NEWLIB)/lib
$Q$(CP) omp.h $(NEWLIB)/include
clean:
@echo Cleaning examples
$Q$(RM) $(NAME) *.o *~
veryclean:
@echo Propper cleaning examples
$Q$(RM) $(NAME) *.o *~
depend:
$Q$(CC_FOR_TARGET) -MM $(CFLAGS_FOR_TARGET) *.c > Makefile.dep
-include Makefile.dep
# DO NOT DELETE

View File

@ -1,116 +0,0 @@
/* Copyright (C) 2006-2015 Free Software Foundation, Inc.
Contributed by Jakub Jelinek <jakub@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This is a generic stub implementation of a CPU affinity setting. */
#include "libgomp.h"
void
gomp_init_affinity (void)
{
}
void
gomp_init_thread_affinity (pthread_attr_t *attr, unsigned int place)
{
(void) attr;
(void) place;
}
void **
gomp_affinity_alloc (unsigned long count, bool quiet)
{
(void) count;
if (!quiet)
gomp_error ("Affinity not supported on this configuration");
return NULL;
}
void
gomp_affinity_init_place (void *p)
{
(void) p;
}
bool
gomp_affinity_add_cpus (void *p, unsigned long num,
unsigned long len, long stride, bool quiet)
{
(void) p;
(void) num;
(void) len;
(void) stride;
(void) quiet;
return false;
}
bool
gomp_affinity_remove_cpu (void *p, unsigned long num)
{
(void) p;
(void) num;
return false;
}
bool
gomp_affinity_copy_place (void *p, void *q, long stride)
{
(void) p;
(void) q;
(void) stride;
return false;
}
bool
gomp_affinity_same_place (void *p, void *q)
{
(void) p;
(void) q;
return false;
}
bool
gomp_affinity_finalize_place_list (bool quiet)
{
(void) quiet;
return false;
}
bool
gomp_affinity_init_level (int level, unsigned long count, bool quiet)
{
(void) level;
(void) count;
(void) quiet;
if (!quiet)
gomp_error ("Affinity not supported on this configuration");
return NULL;
}
void
gomp_affinity_print_place (void *p)
{
(void) p;
}

View File

@ -1,59 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file contains wrappers for the system allocation routines. Most
places in the OpenMP API do not make any provision for failure, so in
general we cannot allow memory allocation to fail. */
#include "libgomp.h"
#include <stdlib.h>
void *
gomp_malloc (size_t size)
{
void *ret = malloc (size);
if (ret == NULL)
gomp_fatal ("Out of memory allocating %lu bytes", (unsigned long) size);
return ret;
}
void *
gomp_malloc_cleared (size_t size)
{
void *ret = calloc (1, size);
if (ret == NULL)
gomp_fatal ("Out of memory allocating %lu bytes", (unsigned long) size);
return ret;
}
void *
gomp_realloc (void *old, size_t size)
{
void *ret = realloc (old, size);
if (ret == NULL)
gomp_fatal ("Out of memory allocating %lu bytes", (unsigned long) size);
return ret;
}

View File

@ -1,299 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This is the default implementation of a barrier synchronization mechanism
for libgomp. This type is private to the library. Note that we rely on
being able to adjust the barrier count while threads are blocked, so the
POSIX pthread_barrier_t won't work. */
#include "libgomp.h"
void
gomp_barrier_init (gomp_barrier_t *bar, unsigned count)
{
gomp_mutex_init (&bar->mutex1);
#ifndef HAVE_SYNC_BUILTINS
gomp_mutex_init (&bar->mutex2);
#endif
gomp_sem_init (&bar->sem1, 0);
gomp_sem_init (&bar->sem2, 0);
bar->total = count;
bar->arrived = 0;
bar->generation = 0;
bar->cancellable = false;
}
void
gomp_barrier_destroy (gomp_barrier_t *bar)
{
/* Before destroying, make sure all threads have left the barrier. */
gomp_mutex_lock (&bar->mutex1);
gomp_mutex_unlock (&bar->mutex1);
gomp_mutex_destroy (&bar->mutex1);
#ifndef HAVE_SYNC_BUILTINS
gomp_mutex_destroy (&bar->mutex2);
#endif
gomp_sem_destroy (&bar->sem1);
gomp_sem_destroy (&bar->sem2);
}
void
gomp_barrier_reinit (gomp_barrier_t *bar, unsigned count)
{
gomp_mutex_lock (&bar->mutex1);
bar->total = count;
gomp_mutex_unlock (&bar->mutex1);
}
void
gomp_barrier_wait_end (gomp_barrier_t *bar, gomp_barrier_state_t state)
{
unsigned int n;
if (state & BAR_WAS_LAST)
{
n = --bar->arrived;
if (n > 0)
{
do
gomp_sem_post (&bar->sem1);
while (--n != 0);
gomp_sem_wait (&bar->sem2);
}
gomp_mutex_unlock (&bar->mutex1);
}
else
{
gomp_mutex_unlock (&bar->mutex1);
gomp_sem_wait (&bar->sem1);
#ifdef HAVE_SYNC_BUILTINS
n = __sync_add_and_fetch (&bar->arrived, -1);
#else
gomp_mutex_lock (&bar->mutex2);
n = --bar->arrived;
gomp_mutex_unlock (&bar->mutex2);
#endif
if (n == 0)
gomp_sem_post (&bar->sem2);
}
}
void
gomp_barrier_wait (gomp_barrier_t *barrier)
{
gomp_barrier_wait_end (barrier, gomp_barrier_wait_start (barrier));
}
void
gomp_team_barrier_wait_end (gomp_barrier_t *bar, gomp_barrier_state_t state)
{
unsigned int n;
state &= ~BAR_CANCELLED;
if (state & BAR_WAS_LAST)
{
n = --bar->arrived;
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
team->work_share_cancelled = 0;
if (team->task_count)
{
gomp_barrier_handle_tasks (state);
if (n > 0)
gomp_sem_wait (&bar->sem2);
gomp_mutex_unlock (&bar->mutex1);
return;
}
bar->generation = state + BAR_INCR - BAR_WAS_LAST;
if (n > 0)
{
do
gomp_sem_post (&bar->sem1);
while (--n != 0);
gomp_sem_wait (&bar->sem2);
}
gomp_mutex_unlock (&bar->mutex1);
}
else
{
gomp_mutex_unlock (&bar->mutex1);
int gen;
do
{
gomp_sem_wait (&bar->sem1);
gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
if (gen & BAR_TASK_PENDING)
{
gomp_barrier_handle_tasks (state);
gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
}
}
while (gen != state + BAR_INCR);
#ifdef HAVE_SYNC_BUILTINS
n = __sync_add_and_fetch (&bar->arrived, -1);
#else
gomp_mutex_lock (&bar->mutex2);
n = --bar->arrived;
gomp_mutex_unlock (&bar->mutex2);
#endif
if (n == 0)
gomp_sem_post (&bar->sem2);
}
}
bool
gomp_team_barrier_wait_cancel_end (gomp_barrier_t *bar,
gomp_barrier_state_t state)
{
unsigned int n;
if (state & BAR_WAS_LAST)
{
bar->cancellable = false;
n = --bar->arrived;
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
team->work_share_cancelled = 0;
if (team->task_count)
{
gomp_barrier_handle_tasks (state);
if (n > 0)
gomp_sem_wait (&bar->sem2);
gomp_mutex_unlock (&bar->mutex1);
return false;
}
bar->generation = state + BAR_INCR - BAR_WAS_LAST;
if (n > 0)
{
do
gomp_sem_post (&bar->sem1);
while (--n != 0);
gomp_sem_wait (&bar->sem2);
}
gomp_mutex_unlock (&bar->mutex1);
}
else
{
if (state & BAR_CANCELLED)
{
gomp_mutex_unlock (&bar->mutex1);
return true;
}
bar->cancellable = true;
gomp_mutex_unlock (&bar->mutex1);
int gen;
do
{
gomp_sem_wait (&bar->sem1);
gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
if (gen & BAR_CANCELLED)
break;
if (gen & BAR_TASK_PENDING)
{
gomp_barrier_handle_tasks (state);
gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
if (gen & BAR_CANCELLED)
break;
}
}
while (gen != state + BAR_INCR);
#ifdef HAVE_SYNC_BUILTINS
n = __sync_add_and_fetch (&bar->arrived, -1);
#else
gomp_mutex_lock (&bar->mutex2);
n = --bar->arrived;
gomp_mutex_unlock (&bar->mutex2);
#endif
if (n == 0)
gomp_sem_post (&bar->sem2);
if (gen & BAR_CANCELLED)
return true;
}
return false;
}
void
gomp_team_barrier_wait (gomp_barrier_t *barrier)
{
gomp_team_barrier_wait_end (barrier, gomp_barrier_wait_start (barrier));
}
void
gomp_team_barrier_wake (gomp_barrier_t *bar, int count)
{
if (count == 0)
count = bar->total - 1;
while (count-- > 0)
gomp_sem_post (&bar->sem1);
}
bool
gomp_team_barrier_wait_cancel (gomp_barrier_t *bar)
{
gomp_barrier_state_t state = gomp_barrier_wait_cancel_start (bar);
return gomp_team_barrier_wait_cancel_end (bar, state);
}
void
gomp_team_barrier_cancel (struct gomp_team *team)
{
if (team->barrier.generation & BAR_CANCELLED)
return;
gomp_mutex_lock (&team->barrier.mutex1);
gomp_mutex_lock (&team->task_lock);
if (team->barrier.generation & BAR_CANCELLED)
{
gomp_mutex_unlock (&team->task_lock);
gomp_mutex_unlock (&team->barrier.mutex1);
return;
}
team->barrier.generation |= BAR_CANCELLED;
gomp_mutex_unlock (&team->task_lock);
if (team->barrier.cancellable)
{
int n = team->barrier.arrived;
if (n > 0)
{
do
gomp_sem_post (&team->barrier.sem1);
while (--n != 0);
gomp_sem_wait (&team->barrier.sem2);
}
team->barrier.cancellable = false;
}
gomp_mutex_unlock (&team->barrier.mutex1);
}

View File

@ -1,158 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This is the default implementation of a barrier synchronization mechanism
for libgomp. This type is private to the library. Note that we rely on
being able to adjust the barrier count while threads are blocked, so the
POSIX pthread_barrier_t won't work. */
#ifndef GOMP_BARRIER_H
#define GOMP_BARRIER_H 1
#include <pthread.h>
typedef struct
{
gomp_mutex_t mutex1;
#ifndef HAVE_SYNC_BUILTINS
gomp_mutex_t mutex2;
#endif
gomp_sem_t sem1;
gomp_sem_t sem2;
unsigned total;
unsigned arrived;
unsigned generation;
bool cancellable;
} gomp_barrier_t;
typedef unsigned int gomp_barrier_state_t;
/* The generation field contains a counter in the high bits, with a few
low bits dedicated to flags. Note that TASK_PENDING and WAS_LAST can
share space because WAS_LAST is never stored back to generation. */
#define BAR_TASK_PENDING 1
#define BAR_WAS_LAST 1
#define BAR_WAITING_FOR_TASK 2
#define BAR_CANCELLED 4
#define BAR_INCR 8
extern void gomp_barrier_init (gomp_barrier_t *, unsigned);
extern void gomp_barrier_reinit (gomp_barrier_t *, unsigned);
extern void gomp_barrier_destroy (gomp_barrier_t *);
extern void gomp_barrier_wait (gomp_barrier_t *);
extern void gomp_barrier_wait_end (gomp_barrier_t *, gomp_barrier_state_t);
extern void gomp_team_barrier_wait (gomp_barrier_t *);
extern void gomp_team_barrier_wait_end (gomp_barrier_t *,
gomp_barrier_state_t);
extern bool gomp_team_barrier_wait_cancel (gomp_barrier_t *);
extern bool gomp_team_barrier_wait_cancel_end (gomp_barrier_t *,
gomp_barrier_state_t);
extern void gomp_team_barrier_wake (gomp_barrier_t *, int);
struct gomp_team;
extern void gomp_team_barrier_cancel (struct gomp_team *);
static inline gomp_barrier_state_t
gomp_barrier_wait_start (gomp_barrier_t *bar)
{
unsigned int ret;
gomp_mutex_lock (&bar->mutex1);
ret = bar->generation & (-BAR_INCR | BAR_CANCELLED);
if (++bar->arrived == bar->total)
ret |= BAR_WAS_LAST;
return ret;
}
static inline gomp_barrier_state_t
gomp_barrier_wait_cancel_start (gomp_barrier_t *bar)
{
unsigned int ret;
gomp_mutex_lock (&bar->mutex1);
ret = bar->generation & (-BAR_INCR | BAR_CANCELLED);
if (ret & BAR_CANCELLED)
return ret;
if (++bar->arrived == bar->total)
ret |= BAR_WAS_LAST;
return ret;
}
static inline void
gomp_team_barrier_wait_final (gomp_barrier_t *bar)
{
gomp_team_barrier_wait (bar);
}
static inline bool
gomp_barrier_last_thread (gomp_barrier_state_t state)
{
return state & BAR_WAS_LAST;
}
static inline void
gomp_barrier_wait_last (gomp_barrier_t *bar)
{
gomp_barrier_wait (bar);
}
/* All the inlines below must be called with team->task_lock
held. */
static inline void
gomp_team_barrier_set_task_pending (gomp_barrier_t *bar)
{
bar->generation |= BAR_TASK_PENDING;
}
static inline void
gomp_team_barrier_clear_task_pending (gomp_barrier_t *bar)
{
bar->generation &= ~BAR_TASK_PENDING;
}
static inline void
gomp_team_barrier_set_waiting_for_tasks (gomp_barrier_t *bar)
{
bar->generation |= BAR_WAITING_FOR_TASK;
}
static inline bool
gomp_team_barrier_waiting_for_tasks (gomp_barrier_t *bar)
{
return (bar->generation & BAR_WAITING_FOR_TASK) != 0;
}
static inline bool
gomp_team_barrier_cancelled (gomp_barrier_t *bar)
{
return __builtin_expect ((bar->generation & BAR_CANCELLED) != 0, 0);
}
static inline void
gomp_team_barrier_done (gomp_barrier_t *bar, gomp_barrier_state_t state)
{
bar->generation = (state & -BAR_INCR) + BAR_INCR;
}
#endif /* GOMP_BARRIER_H */

View File

@ -1,54 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file handles the BARRIER construct. */
#include "libgomp.h"
void
GOMP_barrier (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
/* It is legal to have orphaned barriers. */
if (team == NULL)
return;
gomp_team_barrier_wait (&team->barrier);
}
bool
GOMP_barrier_cancel (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
/* The compiler transforms to barrier_cancel when it sees that the
barrier is within a construct that can cancel. Thus we should
never have an orphaned cancellable barrier. */
return gomp_team_barrier_wait_cancel (&team->barrier);
}

View File

@ -1,154 +0,0 @@
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if the target assembler supports .symver directive. */
#define HAVE_AS_SYMVER_DIRECTIVE 1
/* Define to 1 if the target supports __attribute__((alias(...))). */
#define HAVE_ATTRIBUTE_ALIAS 1
/* Define to 1 if the target supports __attribute__((dllexport)). */
/* #undef HAVE_ATTRIBUTE_DLLEXPORT */
/* Define to 1 if the target supports __attribute__((visibility(...))). */
#define HAVE_ATTRIBUTE_VISIBILITY 1
/* Define if the POSIX Semaphores do not work on your system. */
/* #undef HAVE_BROKEN_POSIX_SEMAPHORES */
/* Define to 1 if the target assembler supports thread-local storage. */
#define def HAVE_CC_TLS 1
/* Define to 1 if you have the `clock_gettime' function. */
#define HAVE_CLOCK_GETTIME 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the `getloadavg' function. */
#undef HAVE_GETLOADAVG
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `dl' library (-ldl). */
#undef HAVE_LIBDL
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define if pthread_{,attr_}{g,s}etaffinity_np is supported. */
#define HAVE_PTHREAD_AFFINITY_NP 1
/* Define to 1 if you have the <pthread.h> header file. */
#define HAVE_PTHREAD_H 1
/* Define to 1 if you have the <semaphore.h> header file. */
#define HAVE_SEMAPHORE_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the `strtoull' function. */
#define HAVE_STRTOULL 1
/* Define to 1 if the target runtime linker supports binding the same symbol
to different versions. */
/* #undef HAVE_SYMVER_SYMBOL_RENAMING_RUNTIME_SUPPORT */
/* Define to 1 if the target supports __sync_*_compare_and_swap */
#define HAVE_SYNC_BUILTINS 1
/* Define to 1 if you have the <sys/loadavg.h> header file. */
/* #undef HAVE_SYS_LOADAVG_H */
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if the target supports thread-local storage. */
#define HAVE_TLS 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to 1 if GNU symbol versioning is used for libgomp. */
/* #undef LIBGOMP_GNU_SYMBOL_VERSIONING */
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#define LT_OBJDIR ".libs/"
/* Define to hold the list of target names suitable for offloading. */
#define OFFLOAD_TARGETS "host_nonshm"
/* Name of package */
#define PACKAGE "libgomp"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""
/* Define to the full name of this package. */
#define PACKAGE_NAME "GNU Offloading and Multi Processing Runtime Library"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "GNU Offloading and Multi Processing Runtime Library 1.0"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libgomp"
/* Define to the home page for this package. */
#define PACKAGE_URL "http://www.gnu.org/software/libgomp/"
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.0"
/* Define to 1 if the NVIDIA plugin is built, 0 if not. */
#define PLUGIN_NVPTX 0
/* Define if all infrastructure, needed for plugins, is supported. */
#define PLUGIN_SUPPORT 1
/* The size of `char', as computed by sizeof. */
/* #undef SIZEOF_CHAR */
/* The size of `int', as computed by sizeof. */
/* #undef SIZEOF_INT */
/* The size of `long', as computed by sizeof. */
/* #undef SIZEOF_LONG */
/* The size of `short', as computed by sizeof. */
/* #undef SIZEOF_SHORT */
/* The size of `void *', as computed by sizeof. */
/* #undef SIZEOF_VOID_P */
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define if you can safely include both <string.h> and <strings.h>. */
#define STRING_WITH_STRINGS 1
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1
/* Define to 1 if the target use emutls for thread-local storage. */
/* #undef USE_EMUTLS */
/* Version number of package */
#define VERSION "1.0"

View File

@ -1,149 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file handles the CRITICAL construct. */
#include "libgomp.h"
#include <stdlib.h>
static gomp_mutex_t default_lock;
void
GOMP_critical_start (void)
{
/* There is an implicit flush on entry to a critical region. */
__atomic_thread_fence (MEMMODEL_RELEASE);
gomp_mutex_lock (&default_lock);
}
void
GOMP_critical_end (void)
{
gomp_mutex_unlock (&default_lock);
}
#ifndef HAVE_SYNC_BUILTINS
static gomp_mutex_t create_lock_lock;
#endif
void
GOMP_critical_name_start (void **pptr)
{
gomp_mutex_t *plock;
/* If a mutex fits within the space for a pointer, and is zero initialized,
then use the pointer space directly. */
if (GOMP_MUTEX_INIT_0
&& sizeof (gomp_mutex_t) <= sizeof (void *)
&& __alignof (gomp_mutex_t) <= sizeof (void *))
plock = (gomp_mutex_t *)pptr;
/* Otherwise we have to be prepared to malloc storage. */
else
{
plock = *pptr;
if (plock == NULL)
{
#ifdef HAVE_SYNC_BUILTINS
gomp_mutex_t *nlock = gomp_malloc (sizeof (gomp_mutex_t));
gomp_mutex_init (nlock);
plock = __sync_val_compare_and_swap (pptr, NULL, nlock);
if (plock != NULL)
{
gomp_mutex_destroy (nlock);
free (nlock);
}
else
plock = nlock;
#else
gomp_mutex_lock (&create_lock_lock);
plock = *pptr;
if (plock == NULL)
{
plock = gomp_malloc (sizeof (gomp_mutex_t));
gomp_mutex_init (plock);
__sync_synchronize ();
*pptr = plock;
}
gomp_mutex_unlock (&create_lock_lock);
#endif
}
}
gomp_mutex_lock (plock);
}
void
GOMP_critical_name_end (void **pptr)
{
gomp_mutex_t *plock;
/* If a mutex fits within the space for a pointer, and is zero initialized,
then use the pointer space directly. */
if (GOMP_MUTEX_INIT_0
&& sizeof (gomp_mutex_t) <= sizeof (void *)
&& __alignof (gomp_mutex_t) <= sizeof (void *))
plock = (gomp_mutex_t *)pptr;
else
plock = *pptr;
gomp_mutex_unlock (plock);
}
/* This mutex is used when atomic operations don't exist for the target
in the mode requested. The result is not globally atomic, but works so
long as all parallel references are within #pragma omp atomic directives.
According to responses received from omp@openmp.org, appears to be within
spec. Which makes sense, since that's how several other compilers
handle this situation as well. */
static gomp_mutex_t atomic_lock;
void
GOMP_atomic_start (void)
{
gomp_mutex_lock (&atomic_lock);
}
void
GOMP_atomic_end (void)
{
gomp_mutex_unlock (&atomic_lock);
}
#if !GOMP_MUTEX_INIT_0
static void __attribute__((constructor))
initialize_critical (void)
{
gomp_mutex_init (&default_lock);
gomp_mutex_init (&atomic_lock);
#ifndef HAVE_SYNC_BUILTINS
gomp_mutex_init (&create_lock_lock);
#endif
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,91 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file contains routines used to signal errors. Most places in the
OpenMP API do not make any provision for failure, so we can't just
defer the decision on reporting the problem to the user; we must do it
ourselves or not at all. */
/* ??? Is this about what other implementations do? Assume stderr hasn't
been pointed somewhere unsafe? */
#include "libgomp.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#undef gomp_vdebug
void
gomp_vdebug (int kind __attribute__ ((unused)), const char *msg, va_list list)
{
if (gomp_debug_var)
vfprintf (stderr, msg, list);
}
#undef gomp_debug
void
gomp_debug (int kind, const char *msg, ...)
{
va_list list;
va_start (list, msg);
gomp_vdebug (kind, msg, list);
va_end (list);
}
void
gomp_verror (const char *fmt, va_list list)
{
fputs ("\nlibgomp: ", stderr);
vfprintf (stderr, fmt, list);
fputc ('\n', stderr);
}
void
gomp_error (const char *fmt, ...)
{
va_list list;
va_start (list, fmt);
gomp_verror (fmt, list);
va_end (list);
}
void
gomp_vfatal (const char *fmt, va_list list)
{
gomp_verror (fmt, list);
exit (EXIT_FAILURE);
}
void
gomp_fatal (const char *fmt, ...)
{
va_list list;
va_start (list, fmt);
gomp_vfatal (fmt, list);
va_end (list);
}

View File

@ -1,495 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Jakub Jelinek <jakub@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file contains Fortran wrapper routines. */
#include "libgomp.h"
#include "libgomp_f.h"
#include <stdlib.h>
#include <limits.h>
#ifdef HAVE_ATTRIBUTE_ALIAS
/* Use internal aliases if possible. */
# ifndef LIBGOMP_GNU_SYMBOL_VERSIONING
ialias_redirect (omp_init_lock)
ialias_redirect (omp_init_nest_lock)
ialias_redirect (omp_destroy_lock)
ialias_redirect (omp_destroy_nest_lock)
ialias_redirect (omp_set_lock)
ialias_redirect (omp_set_nest_lock)
ialias_redirect (omp_unset_lock)
ialias_redirect (omp_unset_nest_lock)
ialias_redirect (omp_test_lock)
ialias_redirect (omp_test_nest_lock)
# endif
ialias_redirect (omp_set_dynamic)
ialias_redirect (omp_set_nested)
ialias_redirect (omp_set_num_threads)
ialias_redirect (omp_get_dynamic)
ialias_redirect (omp_get_nested)
ialias_redirect (omp_in_parallel)
ialias_redirect (omp_get_max_threads)
ialias_redirect (omp_get_num_procs)
ialias_redirect (omp_get_num_threads)
ialias_redirect (omp_get_thread_num)
ialias_redirect (omp_get_wtick)
ialias_redirect (omp_get_wtime)
ialias_redirect (omp_set_schedule)
ialias_redirect (omp_get_schedule)
ialias_redirect (omp_get_thread_limit)
ialias_redirect (omp_set_max_active_levels)
ialias_redirect (omp_get_max_active_levels)
ialias_redirect (omp_get_level)
ialias_redirect (omp_get_ancestor_thread_num)
ialias_redirect (omp_get_team_size)
ialias_redirect (omp_get_active_level)
ialias_redirect (omp_in_final)
ialias_redirect (omp_get_cancellation)
ialias_redirect (omp_get_proc_bind)
ialias_redirect (omp_set_default_device)
ialias_redirect (omp_get_default_device)
ialias_redirect (omp_get_num_devices)
ialias_redirect (omp_get_num_teams)
ialias_redirect (omp_get_team_num)
ialias_redirect (omp_is_initial_device)
#endif
#ifndef LIBGOMP_GNU_SYMBOL_VERSIONING
# define gomp_init_lock__30 omp_init_lock_
# define gomp_destroy_lock__30 omp_destroy_lock_
# define gomp_set_lock__30 omp_set_lock_
# define gomp_unset_lock__30 omp_unset_lock_
# define gomp_test_lock__30 omp_test_lock_
# define gomp_init_nest_lock__30 omp_init_nest_lock_
# define gomp_destroy_nest_lock__30 omp_destroy_nest_lock_
# define gomp_set_nest_lock__30 omp_set_nest_lock_
# define gomp_unset_nest_lock__30 omp_unset_nest_lock_
# define gomp_test_nest_lock__30 omp_test_nest_lock_
#endif
void
gomp_init_lock__30 (omp_lock_arg_t lock)
{
#ifndef OMP_LOCK_DIRECT
omp_lock_arg (lock) = malloc (sizeof (omp_lock_t));
#endif
gomp_init_lock_30 (omp_lock_arg (lock));
}
void
gomp_init_nest_lock__30 (omp_nest_lock_arg_t lock)
{
#ifndef OMP_NEST_LOCK_DIRECT
omp_nest_lock_arg (lock) = malloc (sizeof (omp_nest_lock_t));
#endif
gomp_init_nest_lock_30 (omp_nest_lock_arg (lock));
}
void
gomp_destroy_lock__30 (omp_lock_arg_t lock)
{
gomp_destroy_lock_30 (omp_lock_arg (lock));
#ifndef OMP_LOCK_DIRECT
free (omp_lock_arg (lock));
omp_lock_arg (lock) = NULL;
#endif
}
void
gomp_destroy_nest_lock__30 (omp_nest_lock_arg_t lock)
{
gomp_destroy_nest_lock_30 (omp_nest_lock_arg (lock));
#ifndef OMP_NEST_LOCK_DIRECT
free (omp_nest_lock_arg (lock));
omp_nest_lock_arg (lock) = NULL;
#endif
}
void
gomp_set_lock__30 (omp_lock_arg_t lock)
{
gomp_set_lock_30 (omp_lock_arg (lock));
}
void
gomp_set_nest_lock__30 (omp_nest_lock_arg_t lock)
{
gomp_set_nest_lock_30 (omp_nest_lock_arg (lock));
}
void
gomp_unset_lock__30 (omp_lock_arg_t lock)
{
gomp_unset_lock_30 (omp_lock_arg (lock));
}
void
gomp_unset_nest_lock__30 (omp_nest_lock_arg_t lock)
{
gomp_unset_nest_lock_30 (omp_nest_lock_arg (lock));
}
int32_t
gomp_test_lock__30 (omp_lock_arg_t lock)
{
return gomp_test_lock_30 (omp_lock_arg (lock));
}
int32_t
gomp_test_nest_lock__30 (omp_nest_lock_arg_t lock)
{
return gomp_test_nest_lock_30 (omp_nest_lock_arg (lock));
}
#ifdef LIBGOMP_GNU_SYMBOL_VERSIONING
void
gomp_init_lock__25 (omp_lock_25_arg_t lock)
{
#ifndef OMP_LOCK_25_DIRECT
omp_lock_25_arg (lock) = malloc (sizeof (omp_lock_25_t));
#endif
gomp_init_lock_25 (omp_lock_25_arg (lock));
}
void
gomp_init_nest_lock__25 (omp_nest_lock_25_arg_t lock)
{
#ifndef OMP_NEST_LOCK_25_DIRECT
omp_nest_lock_25_arg (lock) = malloc (sizeof (omp_nest_lock_25_t));
#endif
gomp_init_nest_lock_25 (omp_nest_lock_25_arg (lock));
}
void
gomp_destroy_lock__25 (omp_lock_25_arg_t lock)
{
gomp_destroy_lock_25 (omp_lock_25_arg (lock));
#ifndef OMP_LOCK_25_DIRECT
free (omp_lock_25_arg (lock));
omp_lock_25_arg (lock) = NULL;
#endif
}
void
gomp_destroy_nest_lock__25 (omp_nest_lock_25_arg_t lock)
{
gomp_destroy_nest_lock_25 (omp_nest_lock_25_arg (lock));
#ifndef OMP_NEST_LOCK_25_DIRECT
free (omp_nest_lock_25_arg (lock));
omp_nest_lock_25_arg (lock) = NULL;
#endif
}
void
gomp_set_lock__25 (omp_lock_25_arg_t lock)
{
gomp_set_lock_25 (omp_lock_25_arg (lock));
}
void
gomp_set_nest_lock__25 (omp_nest_lock_25_arg_t lock)
{
gomp_set_nest_lock_25 (omp_nest_lock_25_arg (lock));
}
void
gomp_unset_lock__25 (omp_lock_25_arg_t lock)
{
gomp_unset_lock_25 (omp_lock_25_arg (lock));
}
void
gomp_unset_nest_lock__25 (omp_nest_lock_25_arg_t lock)
{
gomp_unset_nest_lock_25 (omp_nest_lock_25_arg (lock));
}
int32_t
gomp_test_lock__25 (omp_lock_25_arg_t lock)
{
return gomp_test_lock_25 (omp_lock_25_arg (lock));
}
int32_t
gomp_test_nest_lock__25 (omp_nest_lock_25_arg_t lock)
{
return gomp_test_nest_lock_25 (omp_nest_lock_25_arg (lock));
}
omp_lock_symver (omp_init_lock_)
omp_lock_symver (omp_destroy_lock_)
omp_lock_symver (omp_set_lock_)
omp_lock_symver (omp_unset_lock_)
omp_lock_symver (omp_test_lock_)
omp_lock_symver (omp_init_nest_lock_)
omp_lock_symver (omp_destroy_nest_lock_)
omp_lock_symver (omp_set_nest_lock_)
omp_lock_symver (omp_unset_nest_lock_)
omp_lock_symver (omp_test_nest_lock_)
#endif
#define TO_INT(x) ((x) > INT_MIN ? (x) < INT_MAX ? (x) : INT_MAX : INT_MIN)
void
omp_set_dynamic_ (const int32_t *set)
{
omp_set_dynamic (*set);
}
void
omp_set_dynamic_8_ (const int64_t *set)
{
omp_set_dynamic (!!*set);
}
void
omp_set_nested_ (const int32_t *set)
{
omp_set_nested (*set);
}
void
omp_set_nested_8_ (const int64_t *set)
{
omp_set_nested (!!*set);
}
void
omp_set_num_threads_ (const int32_t *set)
{
omp_set_num_threads (*set);
}
void
omp_set_num_threads_8_ (const int64_t *set)
{
omp_set_num_threads (TO_INT (*set));
}
int32_t
omp_get_dynamic_ (void)
{
return omp_get_dynamic ();
}
int32_t
omp_get_nested_ (void)
{
return omp_get_nested ();
}
int32_t
omp_in_parallel_ (void)
{
return omp_in_parallel ();
}
int32_t
omp_get_max_threads_ (void)
{
return omp_get_max_threads ();
}
int32_t
omp_get_num_procs_ (void)
{
return omp_get_num_procs ();
}
int32_t
omp_get_num_threads_ (void)
{
return omp_get_num_threads ();
}
int32_t
omp_get_thread_num_ (void)
{
return omp_get_thread_num ();
}
double
omp_get_wtick_ (void)
{
return omp_get_wtick ();
}
double
omp_get_wtime_ (void)
{
return omp_get_wtime ();
}
void
omp_set_schedule_ (const int32_t *kind, const int32_t *modifier)
{
omp_set_schedule (*kind, *modifier);
}
void
omp_set_schedule_8_ (const int32_t *kind, const int64_t *modifier)
{
omp_set_schedule (*kind, TO_INT (*modifier));
}
void
omp_get_schedule_ (int32_t *kind, int32_t *modifier)
{
omp_sched_t k;
int m;
omp_get_schedule (&k, &m);
*kind = k;
*modifier = m;
}
void
omp_get_schedule_8_ (int32_t *kind, int64_t *modifier)
{
omp_sched_t k;
int m;
omp_get_schedule (&k, &m);
*kind = k;
*modifier = m;
}
int32_t
omp_get_thread_limit_ (void)
{
return omp_get_thread_limit ();
}
void
omp_set_max_active_levels_ (const int32_t *levels)
{
omp_set_max_active_levels (*levels);
}
void
omp_set_max_active_levels_8_ (const int64_t *levels)
{
omp_set_max_active_levels (TO_INT (*levels));
}
int32_t
omp_get_max_active_levels_ (void)
{
return omp_get_max_active_levels ();
}
int32_t
omp_get_level_ (void)
{
return omp_get_level ();
}
int32_t
omp_get_ancestor_thread_num_ (const int32_t *level)
{
return omp_get_ancestor_thread_num (*level);
}
int32_t
omp_get_ancestor_thread_num_8_ (const int64_t *level)
{
return omp_get_ancestor_thread_num (TO_INT (*level));
}
int32_t
omp_get_team_size_ (const int32_t *level)
{
return omp_get_team_size (*level);
}
int32_t
omp_get_team_size_8_ (const int64_t *level)
{
return omp_get_team_size (TO_INT (*level));
}
int32_t
omp_get_active_level_ (void)
{
return omp_get_active_level ();
}
int32_t
omp_in_final_ (void)
{
return omp_in_final ();
}
int32_t
omp_get_cancellation_ (void)
{
return omp_get_cancellation ();
}
int32_t
omp_get_proc_bind_ (void)
{
return omp_get_proc_bind ();
}
void
omp_set_default_device_ (const int32_t *device_num)
{
return omp_set_default_device (*device_num);
}
void
omp_set_default_device_8_ (const int64_t *device_num)
{
return omp_set_default_device (TO_INT (*device_num));
}
int32_t
omp_get_default_device_ (void)
{
return omp_get_default_device ();
}
int32_t
omp_get_num_devices_ (void)
{
return omp_get_num_devices ();
}
int32_t
omp_get_num_teams_ (void)
{
return omp_get_num_teams ();
}
int32_t
omp_get_team_num_ (void)
{
return omp_get_team_num ();
}
int32_t
omp_is_initial_device_ (void)
{
return omp_is_initial_device ();
}

View File

@ -1,442 +0,0 @@
/* An expandable hash tables datatype.
Copyright (C) 1999-2015 Free Software Foundation, Inc.
Contributed by Vladimir Makarov <vmakarov@cygnus.com>.
This program 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 2 of the License, or
(at your option) any later version.
This program 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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
/* The hash table code copied from include/hashtab.[hc] and adjusted,
so that the hash table entries are in the flexible array at the end
of the control structure, no callbacks are used and the elements in the
table are of the hash_entry_type type.
Before including this file, define hash_entry_type type and
htab_alloc and htab_free functions. After including it, define
htab_hash and htab_eq inline functions. */
/* This package implements basic hash table functionality. It is possible
to search for an entry, create an entry and destroy an entry.
Elements in the table are generic pointers.
The size of the table is not fixed; if the occupancy of the table
grows too high the hash table will be expanded.
The abstract data implementation is based on generalized Algorithm D
from Knuth's book "The art of computer programming". Hash table is
expanded by creation of new hash table and transferring elements from
the old table to the new table. */
/* The type for a hash code. */
typedef unsigned int hashval_t;
static inline hashval_t htab_hash (hash_entry_type);
static inline bool htab_eq (hash_entry_type, hash_entry_type);
/* This macro defines reserved value for empty table entry. */
#define HTAB_EMPTY_ENTRY ((hash_entry_type) 0)
/* This macro defines reserved value for table entry which contained
a deleted element. */
#define HTAB_DELETED_ENTRY ((hash_entry_type) 1)
/* Hash tables are of the following type. The structure
(implementation) of this type is not needed for using the hash
tables. All work with hash table should be executed only through
functions mentioned below. The size of this structure is subject to
change. */
struct htab {
/* Current size (in entries) of the hash table. */
size_t size;
/* Current number of elements including also deleted elements. */
size_t n_elements;
/* Current number of deleted elements in the table. */
size_t n_deleted;
/* Current size (in entries) of the hash table, as an index into the
table of primes. */
unsigned int size_prime_index;
/* Table itself. */
hash_entry_type entries[];
};
typedef struct htab *htab_t;
/* An enum saying whether we insert into the hash table or not. */
enum insert_option {NO_INSERT, INSERT};
/* Table of primes and multiplicative inverses.
Note that these are not minimally reduced inverses. Unlike when generating
code to divide by a constant, we want to be able to use the same algorithm
all the time. All of these inverses (are implied to) have bit 32 set.
For the record, the function that computed the table is in
libiberty/hashtab.c. */
struct prime_ent
{
hashval_t prime;
hashval_t inv;
hashval_t inv_m2; /* inverse of prime-2 */
hashval_t shift;
};
static struct prime_ent const prime_tab[] = {
{ 7, 0x24924925, 0x9999999b, 2 },
{ 13, 0x3b13b13c, 0x745d1747, 3 },
{ 31, 0x08421085, 0x1a7b9612, 4 },
{ 61, 0x0c9714fc, 0x15b1e5f8, 5 },
{ 127, 0x02040811, 0x0624dd30, 6 },
{ 251, 0x05197f7e, 0x073260a5, 7 },
{ 509, 0x01824366, 0x02864fc8, 8 },
{ 1021, 0x00c0906d, 0x014191f7, 9 },
{ 2039, 0x0121456f, 0x0161e69e, 10 },
{ 4093, 0x00300902, 0x00501908, 11 },
{ 8191, 0x00080041, 0x00180241, 12 },
{ 16381, 0x000c0091, 0x00140191, 13 },
{ 32749, 0x002605a5, 0x002a06e6, 14 },
{ 65521, 0x000f00e2, 0x00110122, 15 },
{ 131071, 0x00008001, 0x00018003, 16 },
{ 262139, 0x00014002, 0x0001c004, 17 },
{ 524287, 0x00002001, 0x00006001, 18 },
{ 1048573, 0x00003001, 0x00005001, 19 },
{ 2097143, 0x00004801, 0x00005801, 20 },
{ 4194301, 0x00000c01, 0x00001401, 21 },
{ 8388593, 0x00001e01, 0x00002201, 22 },
{ 16777213, 0x00000301, 0x00000501, 23 },
{ 33554393, 0x00001381, 0x00001481, 24 },
{ 67108859, 0x00000141, 0x000001c1, 25 },
{ 134217689, 0x000004e1, 0x00000521, 26 },
{ 268435399, 0x00000391, 0x000003b1, 27 },
{ 536870909, 0x00000019, 0x00000029, 28 },
{ 1073741789, 0x0000008d, 0x00000095, 29 },
{ 2147483647, 0x00000003, 0x00000007, 30 },
/* Avoid "decimal constant so large it is unsigned" for 4294967291. */
{ 0xfffffffb, 0x00000006, 0x00000008, 31 }
};
/* The following function returns an index into the above table of the
nearest prime number which is greater than N, and near a power of two. */
static unsigned int
higher_prime_index (unsigned long n)
{
unsigned int low = 0;
unsigned int high = sizeof(prime_tab) / sizeof(prime_tab[0]);
while (low != high)
{
unsigned int mid = low + (high - low) / 2;
if (n > prime_tab[mid].prime)
low = mid + 1;
else
high = mid;
}
/* If we've run out of primes, abort. */
if (n > prime_tab[low].prime)
abort ();
return low;
}
/* Return the current size of given hash table. */
static inline size_t
htab_size (htab_t htab)
{
return htab->size;
}
/* Return the current number of elements in given hash table. */
static inline size_t
htab_elements (htab_t htab)
{
return htab->n_elements - htab->n_deleted;
}
/* Return X % Y. */
static inline hashval_t
htab_mod_1 (hashval_t x, hashval_t y, hashval_t inv, int shift)
{
/* The multiplicative inverses computed above are for 32-bit types, and
requires that we be able to compute a highpart multiply. */
if (sizeof (hashval_t) * __CHAR_BIT__ <= 32)
{
hashval_t t1, t2, t3, t4, q, r;
t1 = ((unsigned long long)x * inv) >> 32;
t2 = x - t1;
t3 = t2 >> 1;
t4 = t1 + t3;
q = t4 >> shift;
r = x - (q * y);
return r;
}
/* Otherwise just use the native division routines. */
return x % y;
}
/* Compute the primary hash for HASH given HTAB's current size. */
static inline hashval_t
htab_mod (hashval_t hash, htab_t htab)
{
const struct prime_ent *p = &prime_tab[htab->size_prime_index];
return htab_mod_1 (hash, p->prime, p->inv, p->shift);
}
/* Compute the secondary hash for HASH given HTAB's current size. */
static inline hashval_t
htab_mod_m2 (hashval_t hash, htab_t htab)
{
const struct prime_ent *p = &prime_tab[htab->size_prime_index];
return 1 + htab_mod_1 (hash, p->prime - 2, p->inv_m2, p->shift);
}
/* Create hash table of size SIZE. */
static htab_t
htab_create (size_t size)
{
htab_t result;
unsigned int size_prime_index;
size_prime_index = higher_prime_index (size);
size = prime_tab[size_prime_index].prime;
result = (htab_t) htab_alloc (sizeof (struct htab)
+ size * sizeof (hash_entry_type));
result->size = size;
result->n_elements = 0;
result->n_deleted = 0;
result->size_prime_index = size_prime_index;
memset (result->entries, 0, size * sizeof (hash_entry_type));
return result;
}
/* Similar to htab_find_slot, but without several unwanted side effects:
- Does not call htab_eq when it finds an existing entry.
- Does not change the count of elements in the hash table.
This function also assumes there are no deleted entries in the table.
HASH is the hash value for the element to be inserted. */
static hash_entry_type *
find_empty_slot_for_expand (htab_t htab, hashval_t hash)
{
hashval_t index = htab_mod (hash, htab);
size_t size = htab_size (htab);
hash_entry_type *slot = htab->entries + index;
hashval_t hash2;
if (*slot == HTAB_EMPTY_ENTRY)
return slot;
else if (*slot == HTAB_DELETED_ENTRY)
abort ();
hash2 = htab_mod_m2 (hash, htab);
for (;;)
{
index += hash2;
if (index >= size)
index -= size;
slot = htab->entries + index;
if (*slot == HTAB_EMPTY_ENTRY)
return slot;
else if (*slot == HTAB_DELETED_ENTRY)
abort ();
}
}
/* The following function changes size of memory allocated for the
entries and repeatedly inserts the table elements. The occupancy
of the table after the call will be about 50%. Naturally the hash
table must already exist. Remember also that the place of the
table entries is changed. */
static htab_t
htab_expand (htab_t htab)
{
htab_t nhtab;
hash_entry_type *olimit;
hash_entry_type *p;
size_t osize, elts;
osize = htab->size;
olimit = htab->entries + osize;
elts = htab_elements (htab);
/* Resize only when table after removal of unused elements is either
too full or too empty. */
if (elts * 2 > osize || (elts * 8 < osize && osize > 32))
nhtab = htab_create (elts * 2);
else
nhtab = htab_create (osize - 1);
nhtab->n_elements = htab->n_elements - htab->n_deleted;
p = htab->entries;
do
{
hash_entry_type x = *p;
if (x != HTAB_EMPTY_ENTRY && x != HTAB_DELETED_ENTRY)
*find_empty_slot_for_expand (nhtab, htab_hash (x)) = x;
p++;
}
while (p < olimit);
htab_free (htab);
return nhtab;
}
/* This function searches for a hash table entry equal to the given
element. It cannot be used to insert or delete an element. */
static hash_entry_type
htab_find (htab_t htab, const hash_entry_type element)
{
hashval_t index, hash2, hash = htab_hash (element);
size_t size;
hash_entry_type entry;
size = htab_size (htab);
index = htab_mod (hash, htab);
entry = htab->entries[index];
if (entry == HTAB_EMPTY_ENTRY
|| (entry != HTAB_DELETED_ENTRY && htab_eq (entry, element)))
return entry;
hash2 = htab_mod_m2 (hash, htab);
for (;;)
{
index += hash2;
if (index >= size)
index -= size;
entry = htab->entries[index];
if (entry == HTAB_EMPTY_ENTRY
|| (entry != HTAB_DELETED_ENTRY && htab_eq (entry, element)))
return entry;
}
}
/* This function searches for a hash table slot containing an entry
equal to the given element. To delete an entry, call this with
insert=NO_INSERT, then call htab_clear_slot on the slot returned
(possibly after doing some checks). To insert an entry, call this
with insert=INSERT, then write the value you want into the returned
slot. */
static hash_entry_type *
htab_find_slot (htab_t *htabp, const hash_entry_type element,
enum insert_option insert)
{
hash_entry_type *first_deleted_slot;
hashval_t index, hash2, hash = htab_hash (element);
size_t size;
hash_entry_type entry;
htab_t htab = *htabp;
size = htab_size (htab);
if (insert == INSERT && size * 3 <= htab->n_elements * 4)
{
htab = *htabp = htab_expand (htab);
size = htab_size (htab);
}
index = htab_mod (hash, htab);
first_deleted_slot = NULL;
entry = htab->entries[index];
if (entry == HTAB_EMPTY_ENTRY)
goto empty_entry;
else if (entry == HTAB_DELETED_ENTRY)
first_deleted_slot = &htab->entries[index];
else if (htab_eq (entry, element))
return &htab->entries[index];
hash2 = htab_mod_m2 (hash, htab);
for (;;)
{
index += hash2;
if (index >= size)
index -= size;
entry = htab->entries[index];
if (entry == HTAB_EMPTY_ENTRY)
goto empty_entry;
else if (entry == HTAB_DELETED_ENTRY)
{
if (!first_deleted_slot)
first_deleted_slot = &htab->entries[index];
}
else if (htab_eq (entry, element))
return &htab->entries[index];
}
empty_entry:
if (insert == NO_INSERT)
return NULL;
if (first_deleted_slot)
{
htab->n_deleted--;
*first_deleted_slot = HTAB_EMPTY_ENTRY;
return first_deleted_slot;
}
htab->n_elements++;
return &htab->entries[index];
}
/* This function clears a specified slot in a hash table. It is
useful when you've already done the lookup and don't want to do it
again. */
static inline void
htab_clear_slot (htab_t htab, hash_entry_type *slot)
{
if (slot < htab->entries || slot >= htab->entries + htab_size (htab)
|| *slot == HTAB_EMPTY_ENTRY || *slot == HTAB_DELETED_ENTRY)
abort ();
*slot = HTAB_DELETED_ENTRY;
htab->n_deleted++;
}
/* Returns a hash code for pointer P. Simplified version of evahash */
static inline hashval_t
hash_pointer (const void *p)
{
uintptr_t v = (uintptr_t) p;
if (sizeof (v) > sizeof (hashval_t))
v ^= v >> (sizeof (uintptr_t) / 2 * __CHAR_BIT__);
return v;
}

View File

@ -1,338 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file contains routines for managing work-share iteration, both
for loops and sections. */
#include "libgomp.h"
#include <stdlib.h>
/* This function implements the STATIC scheduling method. The caller should
iterate *pstart <= x < *pend. Return zero if there are more iterations
to perform; nonzero if not. Return less than 0 if this thread had
received the absolutely last iteration. */
int
gomp_iter_static_next (long *pstart, long *pend)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
struct gomp_work_share *ws = thr->ts.work_share;
unsigned long nthreads = team ? team->nthreads : 1;
if (thr->ts.static_trip == -1)
return -1;
/* Quick test for degenerate teams and orphaned constructs. */
if (nthreads == 1)
{
*pstart = ws->next;
*pend = ws->end;
thr->ts.static_trip = -1;
return ws->next == ws->end;
}
/* We interpret chunk_size zero as "unspecified", which means that we
should break up the iterations such that each thread makes only one
trip through the outer loop. */
if (ws->chunk_size == 0)
{
unsigned long n, q, i, t;
unsigned long s0, e0;
long s, e;
if (thr->ts.static_trip > 0)
return 1;
/* Compute the total number of iterations. */
s = ws->incr + (ws->incr > 0 ? -1 : 1);
n = (ws->end - ws->next + s) / ws->incr;
i = thr->ts.team_id;
/* Compute the "zero-based" start and end points. That is, as
if the loop began at zero and incremented by one. */
q = n / nthreads;
t = n % nthreads;
if (i < t)
{
t = 0;
q++;
}
s0 = q * i + t;
e0 = s0 + q;
/* Notice when no iterations allocated for this thread. */
if (s0 >= e0)
{
thr->ts.static_trip = 1;
return 1;
}
/* Transform these to the actual start and end numbers. */
s = (long)s0 * ws->incr + ws->next;
e = (long)e0 * ws->incr + ws->next;
*pstart = s;
*pend = e;
thr->ts.static_trip = (e0 == n ? -1 : 1);
return 0;
}
else
{
unsigned long n, s0, e0, i, c;
long s, e;
/* Otherwise, each thread gets exactly chunk_size iterations
(if available) each time through the loop. */
s = ws->incr + (ws->incr > 0 ? -1 : 1);
n = (ws->end - ws->next + s) / ws->incr;
i = thr->ts.team_id;
c = ws->chunk_size;
/* Initial guess is a C sized chunk positioned nthreads iterations
in, offset by our thread number. */
s0 = (thr->ts.static_trip * nthreads + i) * c;
e0 = s0 + c;
/* Detect overflow. */
if (s0 >= n)
return 1;
if (e0 > n)
e0 = n;
/* Transform these to the actual start and end numbers. */
s = (long)s0 * ws->incr + ws->next;
e = (long)e0 * ws->incr + ws->next;
*pstart = s;
*pend = e;
if (e0 == n)
thr->ts.static_trip = -1;
else
thr->ts.static_trip++;
return 0;
}
}
/* This function implements the DYNAMIC scheduling method. Arguments are
as for gomp_iter_static_next. This function must be called with ws->lock
held. */
bool
gomp_iter_dynamic_next_locked (long *pstart, long *pend)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_work_share *ws = thr->ts.work_share;
long start, end, chunk, left;
start = ws->next;
if (start == ws->end)
return false;
chunk = ws->chunk_size;
left = ws->end - start;
if (ws->incr < 0)
{
if (chunk < left)
chunk = left;
}
else
{
if (chunk > left)
chunk = left;
}
end = start + chunk;
ws->next = end;
*pstart = start;
*pend = end;
return true;
}
#ifdef HAVE_SYNC_BUILTINS
/* Similar, but doesn't require the lock held, and uses compare-and-swap
instead. Note that the only memory value that changes is ws->next. */
bool
gomp_iter_dynamic_next (long *pstart, long *pend)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_work_share *ws = thr->ts.work_share;
long start, end, nend, chunk, incr;
end = ws->end;
incr = ws->incr;
chunk = ws->chunk_size;
if (__builtin_expect (ws->mode, 1))
{
long tmp = __sync_fetch_and_add (&ws->next, chunk);
if (incr > 0)
{
if (tmp >= end)
return false;
nend = tmp + chunk;
if (nend > end)
nend = end;
*pstart = tmp;
*pend = nend;
return true;
}
else
{
if (tmp <= end)
return false;
nend = tmp + chunk;
if (nend < end)
nend = end;
*pstart = tmp;
*pend = nend;
return true;
}
}
start = ws->next;
while (1)
{
long left = end - start;
long tmp;
if (start == end)
return false;
if (incr < 0)
{
if (chunk < left)
chunk = left;
}
else
{
if (chunk > left)
chunk = left;
}
nend = start + chunk;
tmp = __sync_val_compare_and_swap (&ws->next, start, nend);
if (__builtin_expect (tmp == start, 1))
break;
start = tmp;
}
*pstart = start;
*pend = nend;
return true;
}
#endif /* HAVE_SYNC_BUILTINS */
/* This function implements the GUIDED scheduling method. Arguments are
as for gomp_iter_static_next. This function must be called with the
work share lock held. */
bool
gomp_iter_guided_next_locked (long *pstart, long *pend)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_work_share *ws = thr->ts.work_share;
struct gomp_team *team = thr->ts.team;
unsigned long nthreads = team ? team->nthreads : 1;
unsigned long n, q;
long start, end;
if (ws->next == ws->end)
return false;
start = ws->next;
n = (ws->end - start) / ws->incr;
q = (n + nthreads - 1) / nthreads;
if (q < ws->chunk_size)
q = ws->chunk_size;
if (q <= n)
end = start + q * ws->incr;
else
end = ws->end;
ws->next = end;
*pstart = start;
*pend = end;
return true;
}
#ifdef HAVE_SYNC_BUILTINS
/* Similar, but doesn't require the lock held, and uses compare-and-swap
instead. Note that the only memory value that changes is ws->next. */
bool
gomp_iter_guided_next (long *pstart, long *pend)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_work_share *ws = thr->ts.work_share;
struct gomp_team *team = thr->ts.team;
unsigned long nthreads = team ? team->nthreads : 1;
long start, end, nend, incr;
unsigned long chunk_size;
start = ws->next;
end = ws->end;
incr = ws->incr;
chunk_size = ws->chunk_size;
while (1)
{
unsigned long n, q;
long tmp;
if (start == end)
return false;
n = (end - start) / incr;
q = (n + nthreads - 1) / nthreads;
if (q < chunk_size)
q = chunk_size;
if (__builtin_expect (q <= n, 1))
nend = start + q * incr;
else
nend = end;
tmp = __sync_val_compare_and_swap (&ws->next, start, nend);
if (__builtin_expect (tmp == start, 1))
break;
start = tmp;
}
*pstart = start;
*pend = nend;
return true;
}
#endif /* HAVE_SYNC_BUILTINS */

View File

@ -1,345 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file contains routines for managing work-share iteration, both
for loops and sections. */
#include "libgomp.h"
#include <stdlib.h>
typedef unsigned long long gomp_ull;
/* This function implements the STATIC scheduling method. The caller should
iterate *pstart <= x < *pend. Return zero if there are more iterations
to perform; nonzero if not. Return less than 0 if this thread had
received the absolutely last iteration. */
int
gomp_iter_ull_static_next (gomp_ull *pstart, gomp_ull *pend)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
struct gomp_work_share *ws = thr->ts.work_share;
unsigned long nthreads = team ? team->nthreads : 1;
if (thr->ts.static_trip == -1)
return -1;
/* Quick test for degenerate teams and orphaned constructs. */
if (nthreads == 1)
{
*pstart = ws->next_ull;
*pend = ws->end_ull;
thr->ts.static_trip = -1;
return ws->next_ull == ws->end_ull;
}
/* We interpret chunk_size zero as "unspecified", which means that we
should break up the iterations such that each thread makes only one
trip through the outer loop. */
if (ws->chunk_size_ull == 0)
{
gomp_ull n, q, i, t, s0, e0, s, e;
if (thr->ts.static_trip > 0)
return 1;
/* Compute the total number of iterations. */
if (__builtin_expect (ws->mode, 0) == 0)
n = (ws->end_ull - ws->next_ull + ws->incr_ull - 1) / ws->incr_ull;
else
n = (ws->next_ull - ws->end_ull - ws->incr_ull - 1) / -ws->incr_ull;
i = thr->ts.team_id;
/* Compute the "zero-based" start and end points. That is, as
if the loop began at zero and incremented by one. */
q = n / nthreads;
t = n % nthreads;
if (i < t)
{
t = 0;
q++;
}
s0 = q * i + t;
e0 = s0 + q;
/* Notice when no iterations allocated for this thread. */
if (s0 >= e0)
{
thr->ts.static_trip = 1;
return 1;
}
/* Transform these to the actual start and end numbers. */
s = s0 * ws->incr_ull + ws->next_ull;
e = e0 * ws->incr_ull + ws->next_ull;
*pstart = s;
*pend = e;
thr->ts.static_trip = (e0 == n ? -1 : 1);
return 0;
}
else
{
gomp_ull n, s0, e0, i, c, s, e;
/* Otherwise, each thread gets exactly chunk_size iterations
(if available) each time through the loop. */
if (__builtin_expect (ws->mode, 0) == 0)
n = (ws->end_ull - ws->next_ull + ws->incr_ull - 1) / ws->incr_ull;
else
n = (ws->next_ull - ws->end_ull - ws->incr_ull - 1) / -ws->incr_ull;
i = thr->ts.team_id;
c = ws->chunk_size_ull;
/* Initial guess is a C sized chunk positioned nthreads iterations
in, offset by our thread number. */
s0 = (thr->ts.static_trip * (gomp_ull) nthreads + i) * c;
e0 = s0 + c;
/* Detect overflow. */
if (s0 >= n)
return 1;
if (e0 > n)
e0 = n;
/* Transform these to the actual start and end numbers. */
s = s0 * ws->incr_ull + ws->next_ull;
e = e0 * ws->incr_ull + ws->next_ull;
*pstart = s;
*pend = e;
if (e0 == n)
thr->ts.static_trip = -1;
else
thr->ts.static_trip++;
return 0;
}
}
/* This function implements the DYNAMIC scheduling method. Arguments are
as for gomp_iter_ull_static_next. This function must be called with
ws->lock held. */
bool
gomp_iter_ull_dynamic_next_locked (gomp_ull *pstart, gomp_ull *pend)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_work_share *ws = thr->ts.work_share;
gomp_ull start, end, chunk, left;
start = ws->next_ull;
if (start == ws->end_ull)
return false;
chunk = ws->chunk_size_ull;
left = ws->end_ull - start;
if (__builtin_expect (ws->mode & 2, 0))
{
if (chunk < left)
chunk = left;
}
else
{
if (chunk > left)
chunk = left;
}
end = start + chunk;
ws->next_ull = end;
*pstart = start;
*pend = end;
return true;
}
#if defined HAVE_SYNC_BUILTINS && defined __LP64__
/* Similar, but doesn't require the lock held, and uses compare-and-swap
instead. Note that the only memory value that changes is ws->next_ull. */
bool
gomp_iter_ull_dynamic_next (gomp_ull *pstart, gomp_ull *pend)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_work_share *ws = thr->ts.work_share;
gomp_ull start, end, nend, chunk;
end = ws->end_ull;
chunk = ws->chunk_size_ull;
if (__builtin_expect (ws->mode & 1, 1))
{
gomp_ull tmp = __sync_fetch_and_add (&ws->next_ull, chunk);
if (__builtin_expect (ws->mode & 2, 0) == 0)
{
if (tmp >= end)
return false;
nend = tmp + chunk;
if (nend > end)
nend = end;
*pstart = tmp;
*pend = nend;
return true;
}
else
{
if (tmp <= end)
return false;
nend = tmp + chunk;
if (nend < end)
nend = end;
*pstart = tmp;
*pend = nend;
return true;
}
}
start = ws->next_ull;
while (1)
{
gomp_ull left = end - start;
gomp_ull tmp;
if (start == end)
return false;
if (__builtin_expect (ws->mode & 2, 0))
{
if (chunk < left)
chunk = left;
}
else
{
if (chunk > left)
chunk = left;
}
nend = start + chunk;
tmp = __sync_val_compare_and_swap (&ws->next_ull, start, nend);
if (__builtin_expect (tmp == start, 1))
break;
start = tmp;
}
*pstart = start;
*pend = nend;
return true;
}
#endif /* HAVE_SYNC_BUILTINS */
/* This function implements the GUIDED scheduling method. Arguments are
as for gomp_iter_ull_static_next. This function must be called with the
work share lock held. */
bool
gomp_iter_ull_guided_next_locked (gomp_ull *pstart, gomp_ull *pend)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_work_share *ws = thr->ts.work_share;
struct gomp_team *team = thr->ts.team;
gomp_ull nthreads = team ? team->nthreads : 1;
gomp_ull n, q;
gomp_ull start, end;
if (ws->next_ull == ws->end_ull)
return false;
start = ws->next_ull;
if (__builtin_expect (ws->mode, 0) == 0)
n = (ws->end_ull - start) / ws->incr_ull;
else
n = (start - ws->end_ull) / -ws->incr_ull;
q = (n + nthreads - 1) / nthreads;
if (q < ws->chunk_size_ull)
q = ws->chunk_size_ull;
if (q <= n)
end = start + q * ws->incr_ull;
else
end = ws->end_ull;
ws->next_ull = end;
*pstart = start;
*pend = end;
return true;
}
#if defined HAVE_SYNC_BUILTINS && defined __LP64__
/* Similar, but doesn't require the lock held, and uses compare-and-swap
instead. Note that the only memory value that changes is ws->next_ull. */
bool
gomp_iter_ull_guided_next (gomp_ull *pstart, gomp_ull *pend)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_work_share *ws = thr->ts.work_share;
struct gomp_team *team = thr->ts.team;
gomp_ull nthreads = team ? team->nthreads : 1;
gomp_ull start, end, nend, incr;
gomp_ull chunk_size;
start = ws->next_ull;
end = ws->end_ull;
incr = ws->incr_ull;
chunk_size = ws->chunk_size_ull;
while (1)
{
gomp_ull n, q;
gomp_ull tmp;
if (start == end)
return false;
if (__builtin_expect (ws->mode, 0) == 0)
n = (end - start) / incr;
else
n = (start - end) / -incr;
q = (n + nthreads - 1) / nthreads;
if (q < chunk_size)
q = chunk_size;
if (__builtin_expect (q <= n, 1))
nend = start + q * incr;
else
nend = end;
tmp = __sync_val_compare_and_swap (&ws->next_ull, start, nend);
if (__builtin_expect (tmp == start, 1))
break;
start = tmp;
}
*pstart = start;
*pend = nend;
return true;
}
#endif /* HAVE_SYNC_BUILTINS */

View File

@ -1,887 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file contains data types and function declarations that are not
part of the official OpenACC or OpenMP user interfaces. There are
declarations in here that are part of the GNU Offloading and Multi
Processing ABI, in that the compiler is required to know about them
and use them.
The convention is that the all caps prefix "GOMP" is used group items
that are part of the external ABI, and the lower case prefix "gomp"
is used group items that are completely private to the library. */
#ifndef LIBGOMP_H
#define LIBGOMP_H 1
#include "config.h"
//#include "gstdint.h"
//#include "libgomp-plugin.h"
#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#ifdef HAVE_ATTRIBUTE_VISIBILITY
# pragma GCC visibility push(hidden)
#endif
/* If we were a C++ library, we'd get this from <std/atomic>. */
enum memmodel
{
MEMMODEL_RELAXED = 0,
MEMMODEL_CONSUME = 1,
MEMMODEL_ACQUIRE = 2,
MEMMODEL_RELEASE = 3,
MEMMODEL_ACQ_REL = 4,
MEMMODEL_SEQ_CST = 5
};
#include "sem.h"
#include "mutex.h"
#include "bar.h"
#include "ptrlock.h"
/* This structure contains the data to control one work-sharing construct,
either a LOOP (FOR/DO) or a SECTIONS. */
enum gomp_schedule_type
{
GFS_RUNTIME,
GFS_STATIC,
GFS_DYNAMIC,
GFS_GUIDED,
GFS_AUTO
};
struct gomp_work_share
{
/* This member records the SCHEDULE clause to be used for this construct.
The user specification of "runtime" will already have been resolved.
If this is a SECTIONS construct, this value will always be DYNAMIC. */
enum gomp_schedule_type sched;
int mode;
union {
struct {
/* This is the chunk_size argument to the SCHEDULE clause. */
long chunk_size;
/* This is the iteration end point. If this is a SECTIONS construct,
this is the number of contained sections. */
long end;
/* This is the iteration step. If this is a SECTIONS construct, this
is always 1. */
long incr;
};
struct {
/* The same as above, but for the unsigned long long loop variants. */
unsigned long long chunk_size_ull;
unsigned long long end_ull;
unsigned long long incr_ull;
};
};
/* This is a circular queue that details which threads will be allowed
into the ordered region and in which order. When a thread allocates
iterations on which it is going to work, it also registers itself at
the end of the array. When a thread reaches the ordered region, it
checks to see if it is the one at the head of the queue. If not, it
blocks on its RELEASE semaphore. */
unsigned *ordered_team_ids;
/* This is the number of threads that have registered themselves in
the circular queue ordered_team_ids. */
unsigned ordered_num_used;
/* This is the team_id of the currently acknowledged owner of the ordered
section, or -1u if the ordered section has not been acknowledged by
any thread. This is distinguished from the thread that is *allowed*
to take the section next. */
unsigned ordered_owner;
/* This is the index into the circular queue ordered_team_ids of the
current thread that's allowed into the ordered reason. */
unsigned ordered_cur;
/* This is a chain of allocated gomp_work_share blocks, valid only
in the first gomp_work_share struct in the block. */
struct gomp_work_share *next_alloc;
/* The above fields are written once during workshare initialization,
or related to ordered worksharing. Make sure the following fields
are in a different cache line. */
/* This lock protects the update of the following members. */
gomp_mutex_t lock __attribute__((aligned (64)));
/* This is the count of the number of threads that have exited the work
share construct. If the construct was marked nowait, they have moved on
to other work; otherwise they're blocked on a barrier. The last member
of the team to exit the work share construct must deallocate it. */
unsigned threads_completed;
union {
/* This is the next iteration value to be allocated. In the case of
GFS_STATIC loops, this the iteration start point and never changes. */
long next;
/* The same, but with unsigned long long type. */
unsigned long long next_ull;
/* This is the returned data structure for SINGLE COPYPRIVATE. */
void *copyprivate;
};
union {
/* Link to gomp_work_share struct for next work sharing construct
encountered after this one. */
gomp_ptrlock_t next_ws;
/* gomp_work_share structs are chained in the free work share cache
through this. */
struct gomp_work_share *next_free;
};
/* If only few threads are in the team, ordered_team_ids can point
to this array which fills the padding at the end of this struct. */
unsigned inline_ordered_team_ids[0];
};
/* This structure contains all of the thread-local data associated with
a thread team. This is the data that must be saved when a thread
encounters a nested PARALLEL construct. */
struct gomp_team_state
{
/* This is the team of which the thread is currently a member. */
struct gomp_team *team;
/* This is the work share construct which this thread is currently
processing. Recall that with NOWAIT, not all threads may be
processing the same construct. */
struct gomp_work_share *work_share;
/* This is the previous work share construct or NULL if there wasn't any.
When all threads are done with the current work sharing construct,
the previous one can be freed. The current one can't, as its
next_ws field is used. */
struct gomp_work_share *last_work_share;
/* This is the ID of this thread within the team. This value is
guaranteed to be between 0 and N-1, where N is the number of
threads in the team. */
unsigned team_id;
/* Nesting level. */
unsigned level;
/* Active nesting level. Only active parallel regions are counted. */
unsigned active_level;
/* Place-partition-var, offset and length into gomp_places_list array. */
unsigned place_partition_off;
unsigned place_partition_len;
#ifdef HAVE_SYNC_BUILTINS
/* Number of single stmts encountered. */
unsigned long single_count;
#endif
/* For GFS_RUNTIME loops that resolved to GFS_STATIC, this is the
trip number through the loop. So first time a particular loop
is encountered this number is 0, the second time through the loop
is 1, etc. This is unused when the compiler knows in advance that
the loop is statically scheduled. */
unsigned long static_trip;
};
struct target_mem_desc;
/* These are the OpenMP 4.0 Internal Control Variables described in
section 2.3.1. Those described as having one copy per task are
stored within the structure; those described as having one copy
for the whole program are (naturally) global variables. */
struct gomp_task_icv
{
unsigned long nthreads_var;
enum gomp_schedule_type run_sched_var;
int run_sched_modifier;
int default_device_var;
unsigned int thread_limit_var;
bool dyn_var;
bool nest_var;
char bind_var;
/* Internal ICV. */
struct target_mem_desc *target_data;
};
extern struct gomp_task_icv gomp_global_icv;
#ifndef HAVE_SYNC_BUILTINS
extern gomp_mutex_t gomp_managed_threads_lock;
#endif
extern unsigned long gomp_max_active_levels_var;
extern bool gomp_cancel_var;
extern unsigned long long gomp_spin_count_var, gomp_throttled_spin_count_var;
extern unsigned long gomp_available_cpus, gomp_managed_threads;
extern unsigned long *gomp_nthreads_var_list, gomp_nthreads_var_list_len;
extern char *gomp_bind_var_list;
extern unsigned long gomp_bind_var_list_len;
extern void **gomp_places_list;
extern unsigned long gomp_places_list_len;
extern int gomp_debug_var;
extern int goacc_device_num;
extern char *goacc_device_type;
enum gomp_task_kind
{
GOMP_TASK_IMPLICIT,
GOMP_TASK_IFFALSE,
GOMP_TASK_WAITING,
GOMP_TASK_TIED
};
struct gomp_task;
struct gomp_taskgroup;
struct htab;
struct gomp_task_depend_entry
{
void *addr;
struct gomp_task_depend_entry *next;
struct gomp_task_depend_entry *prev;
struct gomp_task *task;
bool is_in;
bool redundant;
bool redundant_out;
};
struct gomp_dependers_vec
{
size_t n_elem;
size_t allocated;
struct gomp_task *elem[];
};
/* Used when in GOMP_taskwait or in gomp_task_maybe_wait_for_dependencies. */
struct gomp_taskwait
{
bool in_taskwait;
bool in_depend_wait;
size_t n_depend;
struct gomp_task *last_parent_depends_on;
gomp_sem_t taskwait_sem;
};
/* This structure describes a "task" to be run by a thread. */
struct gomp_task
{
struct gomp_task *parent;
struct gomp_task *children;
struct gomp_task *next_child;
struct gomp_task *prev_child;
struct gomp_task *next_queue;
struct gomp_task *prev_queue;
struct gomp_task *next_taskgroup;
struct gomp_task *prev_taskgroup;
struct gomp_taskgroup *taskgroup;
struct gomp_dependers_vec *dependers;
struct htab *depend_hash;
struct gomp_taskwait *taskwait;
size_t depend_count;
size_t num_dependees;
struct gomp_task_icv icv;
void (*fn) (void *);
void *fn_data;
enum gomp_task_kind kind;
bool in_tied_task;
bool final_task;
bool copy_ctors_done;
bool parent_depends_on;
struct gomp_task_depend_entry depend[];
};
struct gomp_taskgroup
{
struct gomp_taskgroup *prev;
struct gomp_task *children;
bool in_taskgroup_wait;
bool cancelled;
gomp_sem_t taskgroup_sem;
size_t num_children;
};
/* This structure describes a "team" of threads. These are the threads
that are spawned by a PARALLEL constructs, as well as the work sharing
constructs that the team encounters. */
struct gomp_team
{
/* This is the number of threads in the current team. */
unsigned nthreads;
/* This is number of gomp_work_share structs that have been allocated
as a block last time. */
unsigned work_share_chunk;
/* This is the saved team state that applied to a master thread before
the current thread was created. */
struct gomp_team_state prev_ts;
/* This semaphore should be used by the master thread instead of its
"native" semaphore in the thread structure. Required for nested
parallels, as the master is a member of two teams. */
gomp_sem_t master_release;
/* This points to an array with pointers to the release semaphore
of the threads in the team. */
gomp_sem_t **ordered_release;
/* List of work shares on which gomp_fini_work_share hasn't been
called yet. If the team hasn't been cancelled, this should be
equal to each thr->ts.work_share, but otherwise it can be a possibly
long list of workshares. */
struct gomp_work_share *work_shares_to_free;
/* List of gomp_work_share structs chained through next_free fields.
This is populated and taken off only by the first thread in the
team encountering a new work sharing construct, in a critical
section. */
struct gomp_work_share *work_share_list_alloc;
/* List of gomp_work_share structs freed by free_work_share. New
entries are atomically added to the start of the list, and
alloc_work_share can safely only move all but the first entry
to work_share_list alloc, as free_work_share can happen concurrently
with alloc_work_share. */
struct gomp_work_share *work_share_list_free;
#ifdef HAVE_SYNC_BUILTINS
/* Number of simple single regions encountered by threads in this
team. */
unsigned long single_count;
#else
/* Mutex protecting addition of workshares to work_share_list_free. */
gomp_mutex_t work_share_list_free_lock;
#endif
/* This barrier is used for most synchronization of the team. */
gomp_barrier_t barrier;
/* Initial work shares, to avoid allocating any gomp_work_share
structs in the common case. */
struct gomp_work_share work_shares[8];
gomp_mutex_t task_lock;
struct gomp_task *task_queue;
/* Number of all GOMP_TASK_{WAITING,TIED} tasks in the team. */
unsigned int task_count;
/* Number of GOMP_TASK_WAITING tasks currently waiting to be scheduled. */
unsigned int task_queued_count;
/* Number of GOMP_TASK_{WAITING,TIED} tasks currently running
directly in gomp_barrier_handle_tasks; tasks spawned
from e.g. GOMP_taskwait or GOMP_taskgroup_end don't count, even when
that is called from a task run from gomp_barrier_handle_tasks.
task_running_count should be always <= team->nthreads,
and if current task isn't in_tied_task, then it will be
even < team->nthreads. */
unsigned int task_running_count;
int work_share_cancelled;
int team_cancelled;
/* This array contains structures for implicit tasks. */
struct gomp_task implicit_task[];
};
/* This structure contains all data that is private to libgomp and is
allocated per thread. */
struct gomp_thread
{
/* This is the function that the thread should run upon launch. */
void (*fn) (void *data);
void *data;
/* This is the current team state for this thread. The ts.team member
is NULL only if the thread is idle. */
struct gomp_team_state ts;
/* This is the task that the thread is currently executing. */
struct gomp_task *task;
/* This semaphore is used for ordered loops. */
gomp_sem_t release;
/* Place this thread is bound to plus one, or zero if not bound
to any place. */
unsigned int place;
/* User pthread thread pool */
struct gomp_thread_pool *thread_pool;
} __attribute__ ((aligned (64)));
struct gomp_thread_pool
{
/* This array manages threads spawned from the top level, which will
return to the idle loop once the current PARALLEL construct ends. */
struct gomp_thread **threads;
unsigned threads_size;
unsigned threads_used;
struct gomp_team *last_team;
/* Number of threads running in this contention group. */
unsigned long threads_busy;
/* This barrier holds and releases threads waiting in threads. */
gomp_barrier_t threads_dock;
};
enum gomp_cancel_kind
{
GOMP_CANCEL_PARALLEL = 1,
GOMP_CANCEL_LOOP = 2,
GOMP_CANCEL_FOR = GOMP_CANCEL_LOOP,
GOMP_CANCEL_DO = GOMP_CANCEL_LOOP,
GOMP_CANCEL_SECTIONS = 4,
GOMP_CANCEL_TASKGROUP = 8
};
/* ... and here is that TLS data. */
#if defined HAVE_TLS || defined USE_EMUTLS
extern __thread struct gomp_thread* gomp_tls_data;
static inline struct gomp_thread *gomp_thread (void)
{
return gomp_tls_data;
}
#else
extern pthread_key_t gomp_tls_key;
static inline struct gomp_thread *gomp_thread (void)
{
return pthread_getspecific (gomp_tls_key);
}
#endif
extern struct gomp_task_icv *gomp_new_icv (void);
/* Here's how to access the current copy of the ICVs. */
static inline struct gomp_task_icv *gomp_icv (bool write)
{
struct gomp_task *task = gomp_thread ()->task;
if (task)
return &task->icv;
else if (write)
return gomp_new_icv ();
else
return &gomp_global_icv;
}
/* The attributes to be used during thread creation. */
extern pthread_attr_t gomp_thread_attr;
/* Function prototypes. */
/* affinity.c */
extern void gomp_init_affinity (void);
extern void gomp_init_thread_affinity (pthread_attr_t *, unsigned int);
extern void **gomp_affinity_alloc (unsigned long, bool);
extern void gomp_affinity_init_place (void *);
extern bool gomp_affinity_add_cpus (void *, unsigned long, unsigned long,
long, bool);
extern bool gomp_affinity_remove_cpu (void *, unsigned long);
extern bool gomp_affinity_copy_place (void *, void *, long);
extern bool gomp_affinity_same_place (void *, void *);
extern bool gomp_affinity_finalize_place_list (bool);
extern bool gomp_affinity_init_level (int, unsigned long, bool);
extern void gomp_affinity_print_place (void *);
/* alloc.c */
extern void *gomp_malloc (size_t) __attribute__((malloc));
extern void *gomp_malloc_cleared (size_t) __attribute__((malloc));
extern void *gomp_realloc (void *, size_t);
/* Avoid conflicting prototypes of alloca() in system headers by using
GCC's builtin alloca(). */
#define gomp_alloca(x) __builtin_alloca(x)
/* error.c */
extern void gomp_vdebug (int, const char *, va_list);
extern void gomp_debug (int, const char *, ...)
__attribute__ ((format (printf, 2, 3)));
#define gomp_vdebug(KIND, FMT, VALIST) \
do { \
if (__builtin_expect (gomp_debug_var, 0)) \
(gomp_vdebug) ((KIND), (FMT), (VALIST)); \
} while (0)
#define gomp_debug(KIND, ...) \
do { \
if (__builtin_expect (gomp_debug_var, 0)) \
(gomp_debug) ((KIND), __VA_ARGS__); \
} while (0)
extern void gomp_verror (const char *, va_list);
extern void gomp_error (const char *, ...)
__attribute__ ((format (printf, 1, 2)));
extern void gomp_vfatal (const char *, va_list)
__attribute__ ((noreturn));
extern void gomp_fatal (const char *, ...)
__attribute__ ((noreturn, format (printf, 1, 2)));
/* iter.c */
extern int gomp_iter_static_next (long *, long *);
extern bool gomp_iter_dynamic_next_locked (long *, long *);
extern bool gomp_iter_guided_next_locked (long *, long *);
#ifdef HAVE_SYNC_BUILTINS
extern bool gomp_iter_dynamic_next (long *, long *);
extern bool gomp_iter_guided_next (long *, long *);
#endif
/* iter_ull.c */
extern int gomp_iter_ull_static_next (unsigned long long *,
unsigned long long *);
extern bool gomp_iter_ull_dynamic_next_locked (unsigned long long *,
unsigned long long *);
extern bool gomp_iter_ull_guided_next_locked (unsigned long long *,
unsigned long long *);
#if defined HAVE_SYNC_BUILTINS && defined __LP64__
extern bool gomp_iter_ull_dynamic_next (unsigned long long *,
unsigned long long *);
extern bool gomp_iter_ull_guided_next (unsigned long long *,
unsigned long long *);
#endif
/* ordered.c */
extern void gomp_ordered_first (void);
extern void gomp_ordered_last (void);
extern void gomp_ordered_next (void);
extern void gomp_ordered_static_init (void);
extern void gomp_ordered_static_next (void);
extern void gomp_ordered_sync (void);
/* parallel.c */
extern unsigned gomp_resolve_num_threads (unsigned, unsigned);
/* proc.c (in config/) */
extern void gomp_init_num_threads (void);
extern unsigned gomp_dynamic_max_threads (void);
/* task.c */
extern void gomp_init_task (struct gomp_task *, struct gomp_task *,
struct gomp_task_icv *);
extern void gomp_end_task (void);
extern void gomp_barrier_handle_tasks (gomp_barrier_state_t);
static void inline
gomp_finish_task (struct gomp_task *task)
{
if (__builtin_expect (task->depend_hash != NULL, 0))
free (task->depend_hash);
}
/* team.c */
extern struct gomp_team *gomp_new_team (unsigned);
extern void gomp_team_start (void (*) (void *), void *, unsigned,
unsigned, struct gomp_team *);
extern void gomp_team_end (void);
extern void gomp_free_thread (void *);
/* target.c */
extern void gomp_init_targets_once (void);
extern int gomp_get_num_devices (void);
typedef struct splay_tree_node_s *splay_tree_node;
typedef struct splay_tree_s *splay_tree;
typedef struct splay_tree_key_s *splay_tree_key;
struct target_mem_desc {
/* Reference count. */
uintptr_t refcount;
/* All the splay nodes allocated together. */
splay_tree_node array;
/* Start of the target region. */
uintptr_t tgt_start;
/* End of the targer region. */
uintptr_t tgt_end;
/* Handle to free. */
void *to_free;
/* Previous target_mem_desc. */
struct target_mem_desc *prev;
/* Number of items in following list. */
size_t list_count;
/* Corresponding target device descriptor. */
struct gomp_device_descr *device_descr;
/* List of splay keys to remove (or decrease refcount)
at the end of region. */
splay_tree_key list[];
};
struct splay_tree_key_s {
/* Address of the host object. */
uintptr_t host_start;
/* Address immediately after the host object. */
uintptr_t host_end;
/* Descriptor of the target memory. */
struct target_mem_desc *tgt;
/* Offset from tgt->tgt_start to the start of the target object. */
uintptr_t tgt_offset;
/* Reference count. */
uintptr_t refcount;
/* Asynchronous reference count. */
uintptr_t async_refcount;
/* True if data should be copied from device to host at the end. */
bool copy_from;
};
#if 0
#include "splay-tree.h"
typedef struct acc_dispatch_t
{
/* This is a linked list of data mapped using the
acc_map_data/acc_unmap_data or "acc enter data"/"acc exit data" pragmas.
Unlike mapped_data in the goacc_thread struct, unmapping can
happen out-of-order with respect to mapping. */
/* This is guarded by the lock in the "outer" struct gomp_device_descr. */
struct target_mem_desc *data_environ;
/* Execute. */
void (*exec_func) (void (*) (void *), size_t, void **, void **, size_t *,
unsigned short *, int, int, int, int, void *);
/* Async cleanup callback registration. */
void (*register_async_cleanup_func) (void *);
/* Asynchronous routines. */
int (*async_test_func) (int);
int (*async_test_all_func) (void);
void (*async_wait_func) (int);
void (*async_wait_async_func) (int, int);
void (*async_wait_all_func) (void);
void (*async_wait_all_async_func) (int);
void (*async_set_async_func) (int);
/* Create/destroy TLS data. */
void *(*create_thread_data_func) (int);
void (*destroy_thread_data_func) (void *);
/* NVIDIA target specific routines. */
struct {
void *(*get_current_device_func) (void);
void *(*get_current_context_func) (void);
void *(*get_stream_func) (int);
int (*set_stream_func) (int, void *);
} cuda;
} acc_dispatch_t;
/* This structure describes accelerator device.
It contains name of the corresponding libgomp plugin, function handlers for
interaction with the device, ID-number of the device, and information about
mapped memory. */
struct gomp_device_descr
{
/* Immutable data, which is only set during initialization, and which is not
guarded by the lock. */
/* The name of the device. */
const char *name;
/* Capabilities of device (supports OpenACC, OpenMP). */
unsigned int capabilities;
/* This is the ID number of device among devices of the same type. */
int target_id;
/* This is the TYPE of device. */
enum offload_target_type type;
/* Function handlers. */
const char *(*get_name_func) (void);
unsigned int (*get_caps_func) (void);
int (*get_type_func) (void);
int (*get_num_devices_func) (void);
void (*init_device_func) (int);
void (*fini_device_func) (int);
int (*load_image_func) (int, void *, struct addr_pair **);
void (*unload_image_func) (int, void *);
void *(*alloc_func) (int, size_t);
void (*free_func) (int, void *);
void *(*dev2host_func) (int, void *, const void *, size_t);
void *(*host2dev_func) (int, void *, const void *, size_t);
void (*run_func) (int, void *, void *);
/* Splay tree containing information about mapped memory regions. */
struct splay_tree_s mem_map;
/* Mutex for the mutable data. */
gomp_mutex_t lock;
/* Set to true when device is initialized. */
bool is_initialized;
/* OpenACC-specific data and functions. */
/* This is mutable because of its mutable data_environ and target_data
members. */
acc_dispatch_t openacc;
};
#endif
extern void gomp_acc_insert_pointer (size_t, void **, size_t *, void *);
extern void gomp_acc_remove_pointer (void *, bool, int, int);
extern struct target_mem_desc *gomp_map_vars (struct gomp_device_descr *,
size_t, void **, void **,
size_t *, void *, bool, bool);
extern void gomp_copy_from_async (struct target_mem_desc *);
extern void gomp_unmap_vars (struct target_mem_desc *, bool);
extern void gomp_init_device (struct gomp_device_descr *);
extern void gomp_free_memmap (struct splay_tree_s *);
extern void gomp_fini_device (struct gomp_device_descr *);
/* work.c */
extern void gomp_init_work_share (struct gomp_work_share *, bool, unsigned);
extern void gomp_fini_work_share (struct gomp_work_share *);
extern bool gomp_work_share_start (bool);
extern void gomp_work_share_end (void);
extern bool gomp_work_share_end_cancel (void);
extern void gomp_work_share_end_nowait (void);
static inline void
gomp_work_share_init_done (void)
{
struct gomp_thread *thr = gomp_thread ();
if (__builtin_expect (thr->ts.last_work_share != NULL, 1))
gomp_ptrlock_set (&thr->ts.last_work_share->next_ws, thr->ts.work_share);
}
#ifdef HAVE_ATTRIBUTE_VISIBILITY
# pragma GCC visibility pop
#endif
/* Now that we're back to default visibility, include the globals. */
#include "libgomp_g.h"
/* Include omp.h by parts. */
#include "omp-lock.h"
#define _LIBGOMP_OMP_LOCK_DEFINED 1
//#include "omp.h.in"
#include "omp.h"
#if !defined (HAVE_ATTRIBUTE_VISIBILITY) \
|| !defined (HAVE_ATTRIBUTE_ALIAS) \
|| !defined (HAVE_AS_SYMVER_DIRECTIVE) \
|| !defined (PIC) \
|| !defined (HAVE_SYMVER_SYMBOL_RENAMING_RUNTIME_SUPPORT)
# undef LIBGOMP_GNU_SYMBOL_VERSIONING
#endif
#ifdef LIBGOMP_GNU_SYMBOL_VERSIONING
extern void gomp_init_lock_30 (omp_lock_t *) __GOMP_NOTHROW;
extern void gomp_destroy_lock_30 (omp_lock_t *) __GOMP_NOTHROW;
extern void gomp_set_lock_30 (omp_lock_t *) __GOMP_NOTHROW;
extern void gomp_unset_lock_30 (omp_lock_t *) __GOMP_NOTHROW;
extern int gomp_test_lock_30 (omp_lock_t *) __GOMP_NOTHROW;
extern void gomp_init_nest_lock_30 (omp_nest_lock_t *) __GOMP_NOTHROW;
extern void gomp_destroy_nest_lock_30 (omp_nest_lock_t *) __GOMP_NOTHROW;
extern void gomp_set_nest_lock_30 (omp_nest_lock_t *) __GOMP_NOTHROW;
extern void gomp_unset_nest_lock_30 (omp_nest_lock_t *) __GOMP_NOTHROW;
extern int gomp_test_nest_lock_30 (omp_nest_lock_t *) __GOMP_NOTHROW;
extern void gomp_init_lock_25 (omp_lock_25_t *) __GOMP_NOTHROW;
extern void gomp_destroy_lock_25 (omp_lock_25_t *) __GOMP_NOTHROW;
extern void gomp_set_lock_25 (omp_lock_25_t *) __GOMP_NOTHROW;
extern void gomp_unset_lock_25 (omp_lock_25_t *) __GOMP_NOTHROW;
extern int gomp_test_lock_25 (omp_lock_25_t *) __GOMP_NOTHROW;
extern void gomp_init_nest_lock_25 (omp_nest_lock_25_t *) __GOMP_NOTHROW;
extern void gomp_destroy_nest_lock_25 (omp_nest_lock_25_t *) __GOMP_NOTHROW;
extern void gomp_set_nest_lock_25 (omp_nest_lock_25_t *) __GOMP_NOTHROW;
extern void gomp_unset_nest_lock_25 (omp_nest_lock_25_t *) __GOMP_NOTHROW;
extern int gomp_test_nest_lock_25 (omp_nest_lock_25_t *) __GOMP_NOTHROW;
# define strong_alias(fn, al) \
extern __typeof (fn) al __attribute__ ((alias (#fn)));
# define omp_lock_symver(fn) \
__asm (".symver g" #fn "_30, " #fn "@@OMP_3.0"); \
__asm (".symver g" #fn "_25, " #fn "@OMP_1.0");
#else
# define gomp_init_lock_30 omp_init_lock
# define gomp_destroy_lock_30 omp_destroy_lock
# define gomp_set_lock_30 omp_set_lock
# define gomp_unset_lock_30 omp_unset_lock
# define gomp_test_lock_30 omp_test_lock
# define gomp_init_nest_lock_30 omp_init_nest_lock
# define gomp_destroy_nest_lock_30 omp_destroy_nest_lock
# define gomp_set_nest_lock_30 omp_set_nest_lock
# define gomp_unset_nest_lock_30 omp_unset_nest_lock
# define gomp_test_nest_lock_30 omp_test_nest_lock
#endif
#ifdef HAVE_ATTRIBUTE_VISIBILITY
# define attribute_hidden __attribute__ ((visibility ("hidden")))
#else
# define attribute_hidden
#endif
#ifdef HAVE_ATTRIBUTE_ALIAS
# define ialias_ulp ialias_str1(__USER_LABEL_PREFIX__)
# define ialias_str1(x) ialias_str2(x)
# define ialias_str2(x) #x
# define ialias(fn) \
extern __typeof (fn) gomp_ialias_##fn \
__attribute__ ((alias (#fn))) attribute_hidden;
# define ialias_redirect(fn) \
extern __typeof (fn) fn __asm__ (ialias_ulp "gomp_ialias_" #fn) attribute_hidden;
# define ialias_call(fn) gomp_ialias_ ## fn
#else
# define ialias(fn)
# define ialias_redirect(fn)
# define ialias_call(fn) fn
#endif
#endif /* LIBGOMP_H */

View File

@ -1,3 +0,0 @@
# This spec file is read by gcc when linking. It is used to specify the
# standard libraries we need in order to link with libgomp.
*link_gomp: -lgomp

View File

@ -1,96 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Jakub Jelinek <jakub@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file contains prototypes of functions in the external ABI.
This file is included by files in the testsuite. */
#ifndef LIBGOMP_F_H
#define LIBGOMP_F_H 1
#include "libgomp.h"
#if (32 == 8) \
&& (8 <= 32)
# define OMP_LOCK_DIRECT
typedef omp_lock_t *omp_lock_arg_t;
# define omp_lock_arg(arg) (arg)
#else
typedef union { omp_lock_t *lock; uint64_t u; } *omp_lock_arg_t;
# define omp_lock_arg(arg) ((arg)->lock)
# endif
#if (48 == 8) \
&& (8 <= 48)
# define OMP_NEST_LOCK_DIRECT
typedef omp_nest_lock_t *omp_nest_lock_arg_t;
# define omp_nest_lock_arg(arg) (arg)
#else
typedef union { omp_nest_lock_t *lock; uint64_t u; } *omp_nest_lock_arg_t;
# define omp_nest_lock_arg(arg) ((arg)->lock)
# endif
#if (40 == 8) \
&& (8 <= 40)
# define OMP_LOCK_25_DIRECT
typedef omp_lock_25_t *omp_lock_25_arg_t;
# define omp_lock_25_arg(arg) (arg)
#else
typedef union { omp_lock_25_t *lock; uint64_t u; } *omp_lock_25_arg_t;
# define omp_lock_25_arg(arg) ((arg)->lock)
# endif
#if (48 == 8) \
&& (8 <= 48)
# define OMP_NEST_LOCK_25_DIRECT
typedef omp_nest_lock_25_t *omp_nest_lock_25_arg_t;
# define omp_nest_lock_25_arg(arg) (arg)
#else
typedef union { omp_nest_lock_25_t *lock; uint64_t u; } *omp_nest_lock_25_arg_t;
# define omp_nest_lock_25_arg(arg) ((arg)->lock)
# endif
#ifndef __hermit__
static inline void
omp_check_defines (void)
{
char test[(32 != sizeof (omp_lock_t)
|| 8 != __alignof (omp_lock_t)
|| 48 != sizeof (omp_nest_lock_t)
|| 8 != __alignof (omp_nest_lock_t)
|| 8 != sizeof (*(omp_lock_arg_t) 0)
|| 8 != sizeof (*(omp_nest_lock_arg_t) 0))
? -1 : 1] __attribute__ ((__unused__));
char test2[(40 != sizeof (omp_lock_25_t)
|| 8 != __alignof (omp_lock_25_t)
|| 48 != sizeof (omp_nest_lock_25_t)
|| 8 != __alignof (omp_nest_lock_25_t)
|| 8 != sizeof (*(omp_lock_25_arg_t) 0)
|| 8
!= sizeof (*(omp_nest_lock_25_arg_t) 0))
? -1 : 1] __attribute__ ((__unused__));
}
#endif
#endif /* LIBGOMP_F_H */

View File

@ -1,234 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file contains prototypes of functions in the external ABI.
This file is included by files in the testsuite. */
#ifndef LIBGOMP_G_H
#define LIBGOMP_G_H 1
#include <stdbool.h>
#include <stddef.h>
/* barrier.c */
extern void GOMP_barrier (void);
extern bool GOMP_barrier_cancel (void);
/* critical.c */
extern void GOMP_critical_start (void);
extern void GOMP_critical_end (void);
extern void GOMP_critical_name_start (void **);
extern void GOMP_critical_name_end (void **);
extern void GOMP_atomic_start (void);
extern void GOMP_atomic_end (void);
/* loop.c */
extern bool GOMP_loop_static_start (long, long, long, long, long *, long *);
extern bool GOMP_loop_dynamic_start (long, long, long, long, long *, long *);
extern bool GOMP_loop_guided_start (long, long, long, long, long *, long *);
extern bool GOMP_loop_runtime_start (long, long, long, long *, long *);
extern bool GOMP_loop_ordered_static_start (long, long, long, long,
long *, long *);
extern bool GOMP_loop_ordered_dynamic_start (long, long, long, long,
long *, long *);
extern bool GOMP_loop_ordered_guided_start (long, long, long, long,
long *, long *);
extern bool GOMP_loop_ordered_runtime_start (long, long, long, long *, long *);
extern bool GOMP_loop_static_next (long *, long *);
extern bool GOMP_loop_dynamic_next (long *, long *);
extern bool GOMP_loop_guided_next (long *, long *);
extern bool GOMP_loop_runtime_next (long *, long *);
extern bool GOMP_loop_ordered_static_next (long *, long *);
extern bool GOMP_loop_ordered_dynamic_next (long *, long *);
extern bool GOMP_loop_ordered_guided_next (long *, long *);
extern bool GOMP_loop_ordered_runtime_next (long *, long *);
extern void GOMP_parallel_loop_static_start (void (*)(void *), void *,
unsigned, long, long, long, long);
extern void GOMP_parallel_loop_dynamic_start (void (*)(void *), void *,
unsigned, long, long, long, long);
extern void GOMP_parallel_loop_guided_start (void (*)(void *), void *,
unsigned, long, long, long, long);
extern void GOMP_parallel_loop_runtime_start (void (*)(void *), void *,
unsigned, long, long, long);
extern void GOMP_parallel_loop_static (void (*)(void *), void *,
unsigned, long, long, long, long,
unsigned);
extern void GOMP_parallel_loop_dynamic (void (*)(void *), void *,
unsigned, long, long, long, long,
unsigned);
extern void GOMP_parallel_loop_guided (void (*)(void *), void *,
unsigned, long, long, long, long,
unsigned);
extern void GOMP_parallel_loop_runtime (void (*)(void *), void *,
unsigned, long, long, long,
unsigned);
extern void GOMP_loop_end (void);
extern void GOMP_loop_end_nowait (void);
extern bool GOMP_loop_end_cancel (void);
/* loop_ull.c */
extern bool GOMP_loop_ull_static_start (bool, unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_dynamic_start (bool, unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_guided_start (bool, unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_runtime_start (bool, unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_ordered_static_start (bool, unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_ordered_dynamic_start (bool, unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_ordered_guided_start (bool, unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_ordered_runtime_start (bool, unsigned long long,
unsigned long long,
unsigned long long,
unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_static_next (unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_dynamic_next (unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_guided_next (unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_runtime_next (unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_ordered_static_next (unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_ordered_dynamic_next (unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_ordered_guided_next (unsigned long long *,
unsigned long long *);
extern bool GOMP_loop_ull_ordered_runtime_next (unsigned long long *,
unsigned long long *);
/* ordered.c */
extern void GOMP_ordered_start (void);
extern void GOMP_ordered_end (void);
/* parallel.c */
extern void GOMP_parallel_start (void (*) (void *), void *, unsigned);
extern void GOMP_parallel_end (void);
extern void GOMP_parallel (void (*) (void *), void *, unsigned, unsigned);
extern bool GOMP_cancel (int, bool);
extern bool GOMP_cancellation_point (int);
/* task.c */
extern void GOMP_task (void (*) (void *), void *, void (*) (void *, void *),
long, long, bool, unsigned, void **);
extern void GOMP_taskwait (void);
extern void GOMP_taskyield (void);
extern void GOMP_taskgroup_start (void);
extern void GOMP_taskgroup_end (void);
/* sections.c */
extern unsigned GOMP_sections_start (unsigned);
extern unsigned GOMP_sections_next (void);
extern void GOMP_parallel_sections_start (void (*) (void *), void *,
unsigned, unsigned);
extern void GOMP_parallel_sections (void (*) (void *), void *,
unsigned, unsigned, unsigned);
extern void GOMP_sections_end (void);
extern void GOMP_sections_end_nowait (void);
extern bool GOMP_sections_end_cancel (void);
/* single.c */
extern bool GOMP_single_start (void);
extern void *GOMP_single_copy_start (void);
extern void GOMP_single_copy_end (void *);
/* target.c */
extern void GOMP_target (int, void (*) (void *), const void *,
size_t, void **, size_t *, unsigned char *);
extern void GOMP_target_data (int, const void *,
size_t, void **, size_t *, unsigned char *);
extern void GOMP_target_end_data (void);
extern void GOMP_target_update (int, const void *,
size_t, void **, size_t *, unsigned char *);
extern void GOMP_teams (unsigned int, unsigned int);
/* oacc-parallel.c */
extern void GOACC_data_start (int, size_t, void **, size_t *,
unsigned short *);
extern void GOACC_data_end (void);
extern void GOACC_enter_exit_data (int, size_t, void **,
size_t *, unsigned short *, int, int, ...);
extern void GOACC_parallel (int, void (*) (void *), size_t,
void **, size_t *, unsigned short *, int, int, int,
int, int, ...);
extern void GOACC_update (int, size_t, void **, size_t *,
unsigned short *, int, int, ...);
extern void GOACC_wait (int, int, ...);
extern int GOACC_get_num_threads (void);
extern int GOACC_get_thread_num (void);
#endif /* LIBGOMP_G_H */

View File

@ -1,305 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This is the default PTHREADS implementation of the public OpenMP
locking primitives.
Because OpenMP uses different entry points for normal and recursive
locks, and pthreads uses only one entry point, a system may be able
to do better and streamline the locking as well as reduce the size
of the types exported. */
/* We need UNIX98/XPG5 extensions to get recursive locks. Request XPG6 since
Solaris requires this for C99 and later. */
#define _XOPEN_SOURCE 600
#include "libgomp.h"
#ifdef HAVE_BROKEN_POSIX_SEMAPHORES
void
gomp_init_lock_30 (omp_lock_t *lock)
{
pthread_mutex_init (lock, NULL);
}
void
gomp_destroy_lock_30 (omp_lock_t *lock)
{
pthread_mutex_destroy (lock);
}
void
gomp_set_lock_30 (omp_lock_t *lock)
{
pthread_mutex_lock (lock);
}
void
gomp_unset_lock_30 (omp_lock_t *lock)
{
pthread_mutex_unlock (lock);
}
int
gomp_test_lock_30 (omp_lock_t *lock)
{
return pthread_mutex_trylock (lock) == 0;
}
void
gomp_init_nest_lock_30 (omp_nest_lock_t *lock)
{
pthread_mutex_init (&lock->lock, NULL);
lock->count = 0;
lock->owner = NULL;
}
void
gomp_destroy_nest_lock_30 (omp_nest_lock_t *lock)
{
pthread_mutex_destroy (&lock->lock);
}
void
gomp_set_nest_lock_30 (omp_nest_lock_t *lock)
{
void *me = gomp_icv (true);
if (lock->owner != me)
{
pthread_mutex_lock (&lock->lock);
lock->owner = me;
}
lock->count++;
}
void
gomp_unset_nest_lock_30 (omp_nest_lock_t *lock)
{
if (--lock->count == 0)
{
lock->owner = NULL;
pthread_mutex_unlock (&lock->lock);
}
}
int
gomp_test_nest_lock_30 (omp_nest_lock_t *lock)
{
void *me = gomp_icv (true);
if (lock->owner != me)
{
if (pthread_mutex_trylock (&lock->lock) != 0)
return 0;
lock->owner = me;
}
return ++lock->count;
}
#else
void
gomp_init_lock_30 (omp_lock_t *lock)
{
sem_init (lock, 0, 1);
}
void
gomp_destroy_lock_30 (omp_lock_t *lock)
{
sem_destroy (lock);
}
void
gomp_set_lock_30 (omp_lock_t *lock)
{
while (sem_wait (lock) != 0)
;
}
void
gomp_unset_lock_30 (omp_lock_t *lock)
{
sem_post (lock);
}
int
gomp_test_lock_30 (omp_lock_t *lock)
{
return sem_trywait (lock) == 0;
}
void
gomp_init_nest_lock_30 (omp_nest_lock_t *lock)
{
sem_init (&lock->lock, 0, 1);
lock->count = 0;
lock->owner = NULL;
}
void
gomp_destroy_nest_lock_30 (omp_nest_lock_t *lock)
{
sem_destroy (&lock->lock);
}
void
gomp_set_nest_lock_30 (omp_nest_lock_t *lock)
{
void *me = gomp_icv (true);
if (lock->owner != me)
{
while (sem_wait (&lock->lock) != 0)
;
lock->owner = me;
}
lock->count++;
}
void
gomp_unset_nest_lock_30 (omp_nest_lock_t *lock)
{
if (--lock->count == 0)
{
lock->owner = NULL;
sem_post (&lock->lock);
}
}
int
gomp_test_nest_lock_30 (omp_nest_lock_t *lock)
{
void *me = gomp_icv (true);
if (lock->owner != me)
{
if (sem_trywait (&lock->lock) != 0)
return 0;
lock->owner = me;
}
return ++lock->count;
}
#endif
#ifdef LIBGOMP_GNU_SYMBOL_VERSIONING
void
gomp_init_lock_25 (omp_lock_25_t *lock)
{
pthread_mutex_init (lock, NULL);
}
void
gomp_destroy_lock_25 (omp_lock_25_t *lock)
{
pthread_mutex_destroy (lock);
}
void
gomp_set_lock_25 (omp_lock_25_t *lock)
{
pthread_mutex_lock (lock);
}
void
gomp_unset_lock_25 (omp_lock_25_t *lock)
{
pthread_mutex_unlock (lock);
}
int
gomp_test_lock_25 (omp_lock_25_t *lock)
{
return pthread_mutex_trylock (lock) == 0;
}
void
gomp_init_nest_lock_25 (omp_nest_lock_25_t *lock)
{
pthread_mutexattr_t attr;
pthread_mutexattr_init (&attr);
pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init (&lock->lock, &attr);
lock->count = 0;
pthread_mutexattr_destroy (&attr);
}
void
gomp_destroy_nest_lock_25 (omp_nest_lock_25_t *lock)
{
pthread_mutex_destroy (&lock->lock);
}
void
gomp_set_nest_lock_25 (omp_nest_lock_25_t *lock)
{
pthread_mutex_lock (&lock->lock);
lock->count++;
}
void
gomp_unset_nest_lock_25 (omp_nest_lock_25_t *lock)
{
lock->count--;
pthread_mutex_unlock (&lock->lock);
}
int
gomp_test_nest_lock_25 (omp_nest_lock_25_t *lock)
{
if (pthread_mutex_trylock (&lock->lock) == 0)
return ++lock->count;
return 0;
}
omp_lock_symver (omp_init_lock)
omp_lock_symver (omp_destroy_lock)
omp_lock_symver (omp_set_lock)
omp_lock_symver (omp_unset_lock)
omp_lock_symver (omp_test_lock)
omp_lock_symver (omp_init_nest_lock)
omp_lock_symver (omp_destroy_nest_lock)
omp_lock_symver (omp_set_nest_lock)
omp_lock_symver (omp_unset_nest_lock)
omp_lock_symver (omp_test_nest_lock)
#else
ialias (omp_init_lock)
ialias (omp_init_nest_lock)
ialias (omp_destroy_lock)
ialias (omp_destroy_nest_lock)
ialias (omp_set_lock)
ialias (omp_set_nest_lock)
ialias (omp_unset_lock)
ialias (omp_unset_nest_lock)
ialias (omp_test_lock)
ialias (omp_test_nest_lock)
#endif

View File

@ -1,675 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file handles the LOOP (FOR/DO) construct. */
#include <limits.h>
#include <stdlib.h>
#include "libgomp.h"
/* Initialize the given work share construct from the given arguments. */
static inline void
gomp_loop_init (struct gomp_work_share *ws, long start, long end, long incr,
enum gomp_schedule_type sched, long chunk_size)
{
ws->sched = sched;
ws->chunk_size = chunk_size;
/* Canonicalize loops that have zero iterations to ->next == ->end. */
ws->end = ((incr > 0 && start > end) || (incr < 0 && start < end))
? start : end;
ws->incr = incr;
ws->next = start;
if (sched == GFS_DYNAMIC)
{
ws->chunk_size *= incr;
#ifdef HAVE_SYNC_BUILTINS
{
/* For dynamic scheduling prepare things to make each iteration
faster. */
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
long nthreads = team ? team->nthreads : 1;
if (__builtin_expect (incr > 0, 1))
{
/* Cheap overflow protection. */
if (__builtin_expect ((nthreads | ws->chunk_size)
>= 1UL << (sizeof (long)
* __CHAR_BIT__ / 2 - 1), 0))
ws->mode = 0;
else
ws->mode = ws->end < (LONG_MAX
- (nthreads + 1) * ws->chunk_size);
}
/* Cheap overflow protection. */
else if (__builtin_expect ((nthreads | -ws->chunk_size)
>= 1UL << (sizeof (long)
* __CHAR_BIT__ / 2 - 1), 0))
ws->mode = 0;
else
ws->mode = ws->end > (nthreads + 1) * -ws->chunk_size - LONG_MAX;
}
#endif
}
}
/* The *_start routines are called when first encountering a loop construct
that is not bound directly to a parallel construct. The first thread
that arrives will create the work-share construct; subsequent threads
will see the construct exists and allocate work from it.
START, END, INCR are the bounds of the loop; due to the restrictions of
OpenMP, these values must be the same in every thread. This is not
verified (nor is it entirely verifiable, since START is not necessarily
retained intact in the work-share data structure). CHUNK_SIZE is the
scheduling parameter; again this must be identical in all threads.
Returns true if there's any work for this thread to perform. If so,
*ISTART and *IEND are filled with the bounds of the iteration block
allocated to this thread. Returns false if all work was assigned to
other threads prior to this thread's arrival. */
static bool
gomp_loop_static_start (long start, long end, long incr, long chunk_size,
long *istart, long *iend)
{
struct gomp_thread *thr = gomp_thread ();
thr->ts.static_trip = 0;
if (gomp_work_share_start (false))
{
gomp_loop_init (thr->ts.work_share, start, end, incr,
GFS_STATIC, chunk_size);
gomp_work_share_init_done ();
}
return !gomp_iter_static_next (istart, iend);
}
static bool
gomp_loop_dynamic_start (long start, long end, long incr, long chunk_size,
long *istart, long *iend)
{
struct gomp_thread *thr = gomp_thread ();
bool ret;
if (gomp_work_share_start (false))
{
gomp_loop_init (thr->ts.work_share, start, end, incr,
GFS_DYNAMIC, chunk_size);
gomp_work_share_init_done ();
}
#ifdef HAVE_SYNC_BUILTINS
ret = gomp_iter_dynamic_next (istart, iend);
#else
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_dynamic_next_locked (istart, iend);
gomp_mutex_unlock (&thr->ts.work_share->lock);
#endif
return ret;
}
static bool
gomp_loop_guided_start (long start, long end, long incr, long chunk_size,
long *istart, long *iend)
{
struct gomp_thread *thr = gomp_thread ();
bool ret;
if (gomp_work_share_start (false))
{
gomp_loop_init (thr->ts.work_share, start, end, incr,
GFS_GUIDED, chunk_size);
gomp_work_share_init_done ();
}
#ifdef HAVE_SYNC_BUILTINS
ret = gomp_iter_guided_next (istart, iend);
#else
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_guided_next_locked (istart, iend);
gomp_mutex_unlock (&thr->ts.work_share->lock);
#endif
return ret;
}
bool
GOMP_loop_runtime_start (long start, long end, long incr,
long *istart, long *iend)
{
struct gomp_task_icv *icv = gomp_icv (false);
switch (icv->run_sched_var)
{
case GFS_STATIC:
return gomp_loop_static_start (start, end, incr, icv->run_sched_modifier,
istart, iend);
case GFS_DYNAMIC:
return gomp_loop_dynamic_start (start, end, incr, icv->run_sched_modifier,
istart, iend);
case GFS_GUIDED:
return gomp_loop_guided_start (start, end, incr, icv->run_sched_modifier,
istart, iend);
case GFS_AUTO:
/* For now map to schedule(static), later on we could play with feedback
driven choice. */
return gomp_loop_static_start (start, end, incr, 0, istart, iend);
default:
abort ();
}
}
/* The *_ordered_*_start routines are similar. The only difference is that
this work-share construct is initialized to expect an ORDERED section. */
static bool
gomp_loop_ordered_static_start (long start, long end, long incr,
long chunk_size, long *istart, long *iend)
{
struct gomp_thread *thr = gomp_thread ();
thr->ts.static_trip = 0;
if (gomp_work_share_start (true))
{
gomp_loop_init (thr->ts.work_share, start, end, incr,
GFS_STATIC, chunk_size);
gomp_ordered_static_init ();
gomp_work_share_init_done ();
}
return !gomp_iter_static_next (istart, iend);
}
static bool
gomp_loop_ordered_dynamic_start (long start, long end, long incr,
long chunk_size, long *istart, long *iend)
{
struct gomp_thread *thr = gomp_thread ();
bool ret;
if (gomp_work_share_start (true))
{
gomp_loop_init (thr->ts.work_share, start, end, incr,
GFS_DYNAMIC, chunk_size);
gomp_mutex_lock (&thr->ts.work_share->lock);
gomp_work_share_init_done ();
}
else
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_dynamic_next_locked (istart, iend);
if (ret)
gomp_ordered_first ();
gomp_mutex_unlock (&thr->ts.work_share->lock);
return ret;
}
static bool
gomp_loop_ordered_guided_start (long start, long end, long incr,
long chunk_size, long *istart, long *iend)
{
struct gomp_thread *thr = gomp_thread ();
bool ret;
if (gomp_work_share_start (true))
{
gomp_loop_init (thr->ts.work_share, start, end, incr,
GFS_GUIDED, chunk_size);
gomp_mutex_lock (&thr->ts.work_share->lock);
gomp_work_share_init_done ();
}
else
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_guided_next_locked (istart, iend);
if (ret)
gomp_ordered_first ();
gomp_mutex_unlock (&thr->ts.work_share->lock);
return ret;
}
bool
GOMP_loop_ordered_runtime_start (long start, long end, long incr,
long *istart, long *iend)
{
struct gomp_task_icv *icv = gomp_icv (false);
switch (icv->run_sched_var)
{
case GFS_STATIC:
return gomp_loop_ordered_static_start (start, end, incr,
icv->run_sched_modifier,
istart, iend);
case GFS_DYNAMIC:
return gomp_loop_ordered_dynamic_start (start, end, incr,
icv->run_sched_modifier,
istart, iend);
case GFS_GUIDED:
return gomp_loop_ordered_guided_start (start, end, incr,
icv->run_sched_modifier,
istart, iend);
case GFS_AUTO:
/* For now map to schedule(static), later on we could play with feedback
driven choice. */
return gomp_loop_ordered_static_start (start, end, incr,
0, istart, iend);
default:
abort ();
}
}
/* The *_next routines are called when the thread completes processing of
the iteration block currently assigned to it. If the work-share
construct is bound directly to a parallel construct, then the iteration
bounds may have been set up before the parallel. In which case, this
may be the first iteration for the thread.
Returns true if there is work remaining to be performed; *ISTART and
*IEND are filled with a new iteration block. Returns false if all work
has been assigned. */
static bool
gomp_loop_static_next (long *istart, long *iend)
{
return !gomp_iter_static_next (istart, iend);
}
static bool
gomp_loop_dynamic_next (long *istart, long *iend)
{
bool ret;
#ifdef HAVE_SYNC_BUILTINS
ret = gomp_iter_dynamic_next (istart, iend);
#else
struct gomp_thread *thr = gomp_thread ();
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_dynamic_next_locked (istart, iend);
gomp_mutex_unlock (&thr->ts.work_share->lock);
#endif
return ret;
}
static bool
gomp_loop_guided_next (long *istart, long *iend)
{
bool ret;
#ifdef HAVE_SYNC_BUILTINS
ret = gomp_iter_guided_next (istart, iend);
#else
struct gomp_thread *thr = gomp_thread ();
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_guided_next_locked (istart, iend);
gomp_mutex_unlock (&thr->ts.work_share->lock);
#endif
return ret;
}
bool
GOMP_loop_runtime_next (long *istart, long *iend)
{
struct gomp_thread *thr = gomp_thread ();
switch (thr->ts.work_share->sched)
{
case GFS_STATIC:
case GFS_AUTO:
return gomp_loop_static_next (istart, iend);
case GFS_DYNAMIC:
return gomp_loop_dynamic_next (istart, iend);
case GFS_GUIDED:
return gomp_loop_guided_next (istart, iend);
default:
abort ();
}
}
/* The *_ordered_*_next routines are called when the thread completes
processing of the iteration block currently assigned to it.
Returns true if there is work remaining to be performed; *ISTART and
*IEND are filled with a new iteration block. Returns false if all work
has been assigned. */
static bool
gomp_loop_ordered_static_next (long *istart, long *iend)
{
struct gomp_thread *thr = gomp_thread ();
int test;
gomp_ordered_sync ();
gomp_mutex_lock (&thr->ts.work_share->lock);
test = gomp_iter_static_next (istart, iend);
if (test >= 0)
gomp_ordered_static_next ();
gomp_mutex_unlock (&thr->ts.work_share->lock);
return test == 0;
}
static bool
gomp_loop_ordered_dynamic_next (long *istart, long *iend)
{
struct gomp_thread *thr = gomp_thread ();
bool ret;
gomp_ordered_sync ();
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_dynamic_next_locked (istart, iend);
if (ret)
gomp_ordered_next ();
else
gomp_ordered_last ();
gomp_mutex_unlock (&thr->ts.work_share->lock);
return ret;
}
static bool
gomp_loop_ordered_guided_next (long *istart, long *iend)
{
struct gomp_thread *thr = gomp_thread ();
bool ret;
gomp_ordered_sync ();
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_guided_next_locked (istart, iend);
if (ret)
gomp_ordered_next ();
else
gomp_ordered_last ();
gomp_mutex_unlock (&thr->ts.work_share->lock);
return ret;
}
bool
GOMP_loop_ordered_runtime_next (long *istart, long *iend)
{
struct gomp_thread *thr = gomp_thread ();
switch (thr->ts.work_share->sched)
{
case GFS_STATIC:
case GFS_AUTO:
return gomp_loop_ordered_static_next (istart, iend);
case GFS_DYNAMIC:
return gomp_loop_ordered_dynamic_next (istart, iend);
case GFS_GUIDED:
return gomp_loop_ordered_guided_next (istart, iend);
default:
abort ();
}
}
/* The GOMP_parallel_loop_* routines pre-initialize a work-share construct
to avoid one synchronization once we get into the loop. */
static void
gomp_parallel_loop_start (void (*fn) (void *), void *data,
unsigned num_threads, long start, long end,
long incr, enum gomp_schedule_type sched,
long chunk_size, unsigned int flags)
{
struct gomp_team *team;
num_threads = gomp_resolve_num_threads (num_threads, 0);
team = gomp_new_team (num_threads);
gomp_loop_init (&team->work_shares[0], start, end, incr, sched, chunk_size);
gomp_team_start (fn, data, num_threads, flags, team);
}
void
GOMP_parallel_loop_static_start (void (*fn) (void *), void *data,
unsigned num_threads, long start, long end,
long incr, long chunk_size)
{
gomp_parallel_loop_start (fn, data, num_threads, start, end, incr,
GFS_STATIC, chunk_size, 0);
}
void
GOMP_parallel_loop_dynamic_start (void (*fn) (void *), void *data,
unsigned num_threads, long start, long end,
long incr, long chunk_size)
{
gomp_parallel_loop_start (fn, data, num_threads, start, end, incr,
GFS_DYNAMIC, chunk_size, 0);
}
void
GOMP_parallel_loop_guided_start (void (*fn) (void *), void *data,
unsigned num_threads, long start, long end,
long incr, long chunk_size)
{
gomp_parallel_loop_start (fn, data, num_threads, start, end, incr,
GFS_GUIDED, chunk_size, 0);
}
void
GOMP_parallel_loop_runtime_start (void (*fn) (void *), void *data,
unsigned num_threads, long start, long end,
long incr)
{
struct gomp_task_icv *icv = gomp_icv (false);
gomp_parallel_loop_start (fn, data, num_threads, start, end, incr,
icv->run_sched_var, icv->run_sched_modifier, 0);
}
ialias_redirect (GOMP_parallel_end)
void
GOMP_parallel_loop_static (void (*fn) (void *), void *data,
unsigned num_threads, long start, long end,
long incr, long chunk_size, unsigned flags)
{
gomp_parallel_loop_start (fn, data, num_threads, start, end, incr,
GFS_STATIC, chunk_size, flags);
fn (data);
GOMP_parallel_end ();
}
void
GOMP_parallel_loop_dynamic (void (*fn) (void *), void *data,
unsigned num_threads, long start, long end,
long incr, long chunk_size, unsigned flags)
{
gomp_parallel_loop_start (fn, data, num_threads, start, end, incr,
GFS_DYNAMIC, chunk_size, flags);
fn (data);
GOMP_parallel_end ();
}
void
GOMP_parallel_loop_guided (void (*fn) (void *), void *data,
unsigned num_threads, long start, long end,
long incr, long chunk_size, unsigned flags)
{
gomp_parallel_loop_start (fn, data, num_threads, start, end, incr,
GFS_GUIDED, chunk_size, flags);
fn (data);
GOMP_parallel_end ();
}
void
GOMP_parallel_loop_runtime (void (*fn) (void *), void *data,
unsigned num_threads, long start, long end,
long incr, unsigned flags)
{
struct gomp_task_icv *icv = gomp_icv (false);
gomp_parallel_loop_start (fn, data, num_threads, start, end, incr,
icv->run_sched_var, icv->run_sched_modifier,
flags);
fn (data);
GOMP_parallel_end ();
}
/* The GOMP_loop_end* routines are called after the thread is told that
all loop iterations are complete. The first two versions synchronize
all threads; the nowait version does not. */
void
GOMP_loop_end (void)
{
gomp_work_share_end ();
}
bool
GOMP_loop_end_cancel (void)
{
return gomp_work_share_end_cancel ();
}
void
GOMP_loop_end_nowait (void)
{
gomp_work_share_end_nowait ();
}
/* We use static functions above so that we're sure that the "runtime"
function can defer to the proper routine without interposition. We
export the static function with a strong alias when possible, or with
a wrapper function otherwise. */
#ifdef HAVE_ATTRIBUTE_ALIAS
extern __typeof(gomp_loop_static_start) GOMP_loop_static_start
__attribute__((alias ("gomp_loop_static_start")));
extern __typeof(gomp_loop_dynamic_start) GOMP_loop_dynamic_start
__attribute__((alias ("gomp_loop_dynamic_start")));
extern __typeof(gomp_loop_guided_start) GOMP_loop_guided_start
__attribute__((alias ("gomp_loop_guided_start")));
extern __typeof(gomp_loop_ordered_static_start) GOMP_loop_ordered_static_start
__attribute__((alias ("gomp_loop_ordered_static_start")));
extern __typeof(gomp_loop_ordered_dynamic_start) GOMP_loop_ordered_dynamic_start
__attribute__((alias ("gomp_loop_ordered_dynamic_start")));
extern __typeof(gomp_loop_ordered_guided_start) GOMP_loop_ordered_guided_start
__attribute__((alias ("gomp_loop_ordered_guided_start")));
extern __typeof(gomp_loop_static_next) GOMP_loop_static_next
__attribute__((alias ("gomp_loop_static_next")));
extern __typeof(gomp_loop_dynamic_next) GOMP_loop_dynamic_next
__attribute__((alias ("gomp_loop_dynamic_next")));
extern __typeof(gomp_loop_guided_next) GOMP_loop_guided_next
__attribute__((alias ("gomp_loop_guided_next")));
extern __typeof(gomp_loop_ordered_static_next) GOMP_loop_ordered_static_next
__attribute__((alias ("gomp_loop_ordered_static_next")));
extern __typeof(gomp_loop_ordered_dynamic_next) GOMP_loop_ordered_dynamic_next
__attribute__((alias ("gomp_loop_ordered_dynamic_next")));
extern __typeof(gomp_loop_ordered_guided_next) GOMP_loop_ordered_guided_next
__attribute__((alias ("gomp_loop_ordered_guided_next")));
#else
bool
GOMP_loop_static_start (long start, long end, long incr, long chunk_size,
long *istart, long *iend)
{
return gomp_loop_static_start (start, end, incr, chunk_size, istart, iend);
}
bool
GOMP_loop_dynamic_start (long start, long end, long incr, long chunk_size,
long *istart, long *iend)
{
return gomp_loop_dynamic_start (start, end, incr, chunk_size, istart, iend);
}
bool
GOMP_loop_guided_start (long start, long end, long incr, long chunk_size,
long *istart, long *iend)
{
return gomp_loop_guided_start (start, end, incr, chunk_size, istart, iend);
}
bool
GOMP_loop_ordered_static_start (long start, long end, long incr,
long chunk_size, long *istart, long *iend)
{
return gomp_loop_ordered_static_start (start, end, incr, chunk_size,
istart, iend);
}
bool
GOMP_loop_ordered_dynamic_start (long start, long end, long incr,
long chunk_size, long *istart, long *iend)
{
return gomp_loop_ordered_dynamic_start (start, end, incr, chunk_size,
istart, iend);
}
bool
GOMP_loop_ordered_guided_start (long start, long end, long incr,
long chunk_size, long *istart, long *iend)
{
return gomp_loop_ordered_guided_start (start, end, incr, chunk_size,
istart, iend);
}
bool
GOMP_loop_static_next (long *istart, long *iend)
{
return gomp_loop_static_next (istart, iend);
}
bool
GOMP_loop_dynamic_next (long *istart, long *iend)
{
return gomp_loop_dynamic_next (istart, iend);
}
bool
GOMP_loop_guided_next (long *istart, long *iend)
{
return gomp_loop_guided_next (istart, iend);
}
bool
GOMP_loop_ordered_static_next (long *istart, long *iend)
{
return gomp_loop_ordered_static_next (istart, iend);
}
bool
GOMP_loop_ordered_dynamic_next (long *istart, long *iend)
{
return gomp_loop_ordered_dynamic_next (istart, iend);
}
bool
GOMP_loop_ordered_guided_next (long *istart, long *iend)
{
return gomp_loop_ordered_guided_next (istart, iend);
}
#endif

View File

@ -1,572 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file handles the LOOP (FOR/DO) construct. */
#include <limits.h>
#include <stdlib.h>
#include "libgomp.h"
typedef unsigned long long gomp_ull;
/* Initialize the given work share construct from the given arguments. */
static inline void
gomp_loop_ull_init (struct gomp_work_share *ws, bool up, gomp_ull start,
gomp_ull end, gomp_ull incr, enum gomp_schedule_type sched,
gomp_ull chunk_size)
{
ws->sched = sched;
ws->chunk_size_ull = chunk_size;
/* Canonicalize loops that have zero iterations to ->next == ->end. */
ws->end_ull = ((up && start > end) || (!up && start < end))
? start : end;
ws->incr_ull = incr;
ws->next_ull = start;
ws->mode = 0;
if (sched == GFS_DYNAMIC)
{
ws->chunk_size_ull *= incr;
#if defined HAVE_SYNC_BUILTINS && defined __LP64__
{
/* For dynamic scheduling prepare things to make each iteration
faster. */
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
long nthreads = team ? team->nthreads : 1;
if (__builtin_expect (up, 1))
{
/* Cheap overflow protection. */
if (__builtin_expect ((nthreads | ws->chunk_size_ull)
< 1ULL << (sizeof (gomp_ull)
* __CHAR_BIT__ / 2 - 1), 1))
ws->mode = ws->end_ull < (__LONG_LONG_MAX__ * 2ULL + 1
- (nthreads + 1) * ws->chunk_size_ull);
}
/* Cheap overflow protection. */
else if (__builtin_expect ((nthreads | -ws->chunk_size_ull)
< 1ULL << (sizeof (gomp_ull)
* __CHAR_BIT__ / 2 - 1), 1))
ws->mode = ws->end_ull > ((nthreads + 1) * -ws->chunk_size_ull
- (__LONG_LONG_MAX__ * 2ULL + 1));
}
#endif
}
if (!up)
ws->mode |= 2;
}
/* The *_start routines are called when first encountering a loop construct
that is not bound directly to a parallel construct. The first thread
that arrives will create the work-share construct; subsequent threads
will see the construct exists and allocate work from it.
START, END, INCR are the bounds of the loop; due to the restrictions of
OpenMP, these values must be the same in every thread. This is not
verified (nor is it entirely verifiable, since START is not necessarily
retained intact in the work-share data structure). CHUNK_SIZE is the
scheduling parameter; again this must be identical in all threads.
Returns true if there's any work for this thread to perform. If so,
*ISTART and *IEND are filled with the bounds of the iteration block
allocated to this thread. Returns false if all work was assigned to
other threads prior to this thread's arrival. */
static bool
gomp_loop_ull_static_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull chunk_size,
gomp_ull *istart, gomp_ull *iend)
{
struct gomp_thread *thr = gomp_thread ();
thr->ts.static_trip = 0;
if (gomp_work_share_start (false))
{
gomp_loop_ull_init (thr->ts.work_share, up, start, end, incr,
GFS_STATIC, chunk_size);
gomp_work_share_init_done ();
}
return !gomp_iter_ull_static_next (istart, iend);
}
static bool
gomp_loop_ull_dynamic_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull chunk_size,
gomp_ull *istart, gomp_ull *iend)
{
struct gomp_thread *thr = gomp_thread ();
bool ret;
if (gomp_work_share_start (false))
{
gomp_loop_ull_init (thr->ts.work_share, up, start, end, incr,
GFS_DYNAMIC, chunk_size);
gomp_work_share_init_done ();
}
#if defined HAVE_SYNC_BUILTINS && defined __LP64__
ret = gomp_iter_ull_dynamic_next (istart, iend);
#else
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_ull_dynamic_next_locked (istart, iend);
gomp_mutex_unlock (&thr->ts.work_share->lock);
#endif
return ret;
}
static bool
gomp_loop_ull_guided_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull chunk_size,
gomp_ull *istart, gomp_ull *iend)
{
struct gomp_thread *thr = gomp_thread ();
bool ret;
if (gomp_work_share_start (false))
{
gomp_loop_ull_init (thr->ts.work_share, up, start, end, incr,
GFS_GUIDED, chunk_size);
gomp_work_share_init_done ();
}
#if defined HAVE_SYNC_BUILTINS && defined __LP64__
ret = gomp_iter_ull_guided_next (istart, iend);
#else
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_ull_guided_next_locked (istart, iend);
gomp_mutex_unlock (&thr->ts.work_share->lock);
#endif
return ret;
}
bool
GOMP_loop_ull_runtime_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull *istart, gomp_ull *iend)
{
struct gomp_task_icv *icv = gomp_icv (false);
switch (icv->run_sched_var)
{
case GFS_STATIC:
return gomp_loop_ull_static_start (up, start, end, incr,
icv->run_sched_modifier,
istart, iend);
case GFS_DYNAMIC:
return gomp_loop_ull_dynamic_start (up, start, end, incr,
icv->run_sched_modifier,
istart, iend);
case GFS_GUIDED:
return gomp_loop_ull_guided_start (up, start, end, incr,
icv->run_sched_modifier,
istart, iend);
case GFS_AUTO:
/* For now map to schedule(static), later on we could play with feedback
driven choice. */
return gomp_loop_ull_static_start (up, start, end, incr,
0, istart, iend);
default:
abort ();
}
}
/* The *_ordered_*_start routines are similar. The only difference is that
this work-share construct is initialized to expect an ORDERED section. */
static bool
gomp_loop_ull_ordered_static_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull chunk_size,
gomp_ull *istart, gomp_ull *iend)
{
struct gomp_thread *thr = gomp_thread ();
thr->ts.static_trip = 0;
if (gomp_work_share_start (true))
{
gomp_loop_ull_init (thr->ts.work_share, up, start, end, incr,
GFS_STATIC, chunk_size);
gomp_ordered_static_init ();
gomp_work_share_init_done ();
}
return !gomp_iter_ull_static_next (istart, iend);
}
static bool
gomp_loop_ull_ordered_dynamic_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull chunk_size,
gomp_ull *istart, gomp_ull *iend)
{
struct gomp_thread *thr = gomp_thread ();
bool ret;
if (gomp_work_share_start (true))
{
gomp_loop_ull_init (thr->ts.work_share, up, start, end, incr,
GFS_DYNAMIC, chunk_size);
gomp_mutex_lock (&thr->ts.work_share->lock);
gomp_work_share_init_done ();
}
else
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_ull_dynamic_next_locked (istart, iend);
if (ret)
gomp_ordered_first ();
gomp_mutex_unlock (&thr->ts.work_share->lock);
return ret;
}
static bool
gomp_loop_ull_ordered_guided_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull chunk_size,
gomp_ull *istart, gomp_ull *iend)
{
struct gomp_thread *thr = gomp_thread ();
bool ret;
if (gomp_work_share_start (true))
{
gomp_loop_ull_init (thr->ts.work_share, up, start, end, incr,
GFS_GUIDED, chunk_size);
gomp_mutex_lock (&thr->ts.work_share->lock);
gomp_work_share_init_done ();
}
else
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_ull_guided_next_locked (istart, iend);
if (ret)
gomp_ordered_first ();
gomp_mutex_unlock (&thr->ts.work_share->lock);
return ret;
}
bool
GOMP_loop_ull_ordered_runtime_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull *istart,
gomp_ull *iend)
{
struct gomp_task_icv *icv = gomp_icv (false);
switch (icv->run_sched_var)
{
case GFS_STATIC:
return gomp_loop_ull_ordered_static_start (up, start, end, incr,
icv->run_sched_modifier,
istart, iend);
case GFS_DYNAMIC:
return gomp_loop_ull_ordered_dynamic_start (up, start, end, incr,
icv->run_sched_modifier,
istart, iend);
case GFS_GUIDED:
return gomp_loop_ull_ordered_guided_start (up, start, end, incr,
icv->run_sched_modifier,
istart, iend);
case GFS_AUTO:
/* For now map to schedule(static), later on we could play with feedback
driven choice. */
return gomp_loop_ull_ordered_static_start (up, start, end, incr,
0, istart, iend);
default:
abort ();
}
}
/* The *_next routines are called when the thread completes processing of
the iteration block currently assigned to it. If the work-share
construct is bound directly to a parallel construct, then the iteration
bounds may have been set up before the parallel. In which case, this
may be the first iteration for the thread.
Returns true if there is work remaining to be performed; *ISTART and
*IEND are filled with a new iteration block. Returns false if all work
has been assigned. */
static bool
gomp_loop_ull_static_next (gomp_ull *istart, gomp_ull *iend)
{
return !gomp_iter_ull_static_next (istart, iend);
}
static bool
gomp_loop_ull_dynamic_next (gomp_ull *istart, gomp_ull *iend)
{
bool ret;
#if defined HAVE_SYNC_BUILTINS && defined __LP64__
ret = gomp_iter_ull_dynamic_next (istart, iend);
#else
struct gomp_thread *thr = gomp_thread ();
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_ull_dynamic_next_locked (istart, iend);
gomp_mutex_unlock (&thr->ts.work_share->lock);
#endif
return ret;
}
static bool
gomp_loop_ull_guided_next (gomp_ull *istart, gomp_ull *iend)
{
bool ret;
#if defined HAVE_SYNC_BUILTINS && defined __LP64__
ret = gomp_iter_ull_guided_next (istart, iend);
#else
struct gomp_thread *thr = gomp_thread ();
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_ull_guided_next_locked (istart, iend);
gomp_mutex_unlock (&thr->ts.work_share->lock);
#endif
return ret;
}
bool
GOMP_loop_ull_runtime_next (gomp_ull *istart, gomp_ull *iend)
{
struct gomp_thread *thr = gomp_thread ();
switch (thr->ts.work_share->sched)
{
case GFS_STATIC:
case GFS_AUTO:
return gomp_loop_ull_static_next (istart, iend);
case GFS_DYNAMIC:
return gomp_loop_ull_dynamic_next (istart, iend);
case GFS_GUIDED:
return gomp_loop_ull_guided_next (istart, iend);
default:
abort ();
}
}
/* The *_ordered_*_next routines are called when the thread completes
processing of the iteration block currently assigned to it.
Returns true if there is work remaining to be performed; *ISTART and
*IEND are filled with a new iteration block. Returns false if all work
has been assigned. */
static bool
gomp_loop_ull_ordered_static_next (gomp_ull *istart, gomp_ull *iend)
{
struct gomp_thread *thr = gomp_thread ();
int test;
gomp_ordered_sync ();
gomp_mutex_lock (&thr->ts.work_share->lock);
test = gomp_iter_ull_static_next (istart, iend);
if (test >= 0)
gomp_ordered_static_next ();
gomp_mutex_unlock (&thr->ts.work_share->lock);
return test == 0;
}
static bool
gomp_loop_ull_ordered_dynamic_next (gomp_ull *istart, gomp_ull *iend)
{
struct gomp_thread *thr = gomp_thread ();
bool ret;
gomp_ordered_sync ();
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_ull_dynamic_next_locked (istart, iend);
if (ret)
gomp_ordered_next ();
else
gomp_ordered_last ();
gomp_mutex_unlock (&thr->ts.work_share->lock);
return ret;
}
static bool
gomp_loop_ull_ordered_guided_next (gomp_ull *istart, gomp_ull *iend)
{
struct gomp_thread *thr = gomp_thread ();
bool ret;
gomp_ordered_sync ();
gomp_mutex_lock (&thr->ts.work_share->lock);
ret = gomp_iter_ull_guided_next_locked (istart, iend);
if (ret)
gomp_ordered_next ();
else
gomp_ordered_last ();
gomp_mutex_unlock (&thr->ts.work_share->lock);
return ret;
}
bool
GOMP_loop_ull_ordered_runtime_next (gomp_ull *istart, gomp_ull *iend)
{
struct gomp_thread *thr = gomp_thread ();
switch (thr->ts.work_share->sched)
{
case GFS_STATIC:
case GFS_AUTO:
return gomp_loop_ull_ordered_static_next (istart, iend);
case GFS_DYNAMIC:
return gomp_loop_ull_ordered_dynamic_next (istart, iend);
case GFS_GUIDED:
return gomp_loop_ull_ordered_guided_next (istart, iend);
default:
abort ();
}
}
/* We use static functions above so that we're sure that the "runtime"
function can defer to the proper routine without interposition. We
export the static function with a strong alias when possible, or with
a wrapper function otherwise. */
#ifdef HAVE_ATTRIBUTE_ALIAS
extern __typeof(gomp_loop_ull_static_start) GOMP_loop_ull_static_start
__attribute__((alias ("gomp_loop_ull_static_start")));
extern __typeof(gomp_loop_ull_dynamic_start) GOMP_loop_ull_dynamic_start
__attribute__((alias ("gomp_loop_ull_dynamic_start")));
extern __typeof(gomp_loop_ull_guided_start) GOMP_loop_ull_guided_start
__attribute__((alias ("gomp_loop_ull_guided_start")));
extern __typeof(gomp_loop_ull_ordered_static_start) GOMP_loop_ull_ordered_static_start
__attribute__((alias ("gomp_loop_ull_ordered_static_start")));
extern __typeof(gomp_loop_ull_ordered_dynamic_start) GOMP_loop_ull_ordered_dynamic_start
__attribute__((alias ("gomp_loop_ull_ordered_dynamic_start")));
extern __typeof(gomp_loop_ull_ordered_guided_start) GOMP_loop_ull_ordered_guided_start
__attribute__((alias ("gomp_loop_ull_ordered_guided_start")));
extern __typeof(gomp_loop_ull_static_next) GOMP_loop_ull_static_next
__attribute__((alias ("gomp_loop_ull_static_next")));
extern __typeof(gomp_loop_ull_dynamic_next) GOMP_loop_ull_dynamic_next
__attribute__((alias ("gomp_loop_ull_dynamic_next")));
extern __typeof(gomp_loop_ull_guided_next) GOMP_loop_ull_guided_next
__attribute__((alias ("gomp_loop_ull_guided_next")));
extern __typeof(gomp_loop_ull_ordered_static_next) GOMP_loop_ull_ordered_static_next
__attribute__((alias ("gomp_loop_ull_ordered_static_next")));
extern __typeof(gomp_loop_ull_ordered_dynamic_next) GOMP_loop_ull_ordered_dynamic_next
__attribute__((alias ("gomp_loop_ull_ordered_dynamic_next")));
extern __typeof(gomp_loop_ull_ordered_guided_next) GOMP_loop_ull_ordered_guided_next
__attribute__((alias ("gomp_loop_ull_ordered_guided_next")));
#else
bool
GOMP_loop_ull_static_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull chunk_size,
gomp_ull *istart, gomp_ull *iend)
{
return gomp_loop_ull_static_start (up, start, end, incr, chunk_size, istart,
iend);
}
bool
GOMP_loop_ull_dynamic_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull chunk_size,
gomp_ull *istart, gomp_ull *iend)
{
return gomp_loop_ull_dynamic_start (up, start, end, incr, chunk_size, istart,
iend);
}
bool
GOMP_loop_ull_guided_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull chunk_size,
gomp_ull *istart, gomp_ull *iend)
{
return gomp_loop_ull_guided_start (up, start, end, incr, chunk_size, istart,
iend);
}
bool
GOMP_loop_ull_ordered_static_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull chunk_size,
gomp_ull *istart, gomp_ull *iend)
{
return gomp_loop_ull_ordered_static_start (up, start, end, incr, chunk_size,
istart, iend);
}
bool
GOMP_loop_ull_ordered_dynamic_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull chunk_size,
gomp_ull *istart, gomp_ull *iend)
{
return gomp_loop_ull_ordered_dynamic_start (up, start, end, incr, chunk_size,
istart, iend);
}
bool
GOMP_loop_ull_ordered_guided_start (bool up, gomp_ull start, gomp_ull end,
gomp_ull incr, gomp_ull chunk_size,
gomp_ull *istart, gomp_ull *iend)
{
return gomp_loop_ull_ordered_guided_start (up, start, end, incr, chunk_size,
istart, iend);
}
bool
GOMP_loop_ull_static_next (gomp_ull *istart, gomp_ull *iend)
{
return gomp_loop_ull_static_next (istart, iend);
}
bool
GOMP_loop_ull_dynamic_next (gomp_ull *istart, gomp_ull *iend)
{
return gomp_loop_ull_dynamic_next (istart, iend);
}
bool
GOMP_loop_ull_guided_next (gomp_ull *istart, gomp_ull *iend)
{
return gomp_loop_ull_guided_next (istart, iend);
}
bool
GOMP_loop_ull_ordered_static_next (gomp_ull *istart, gomp_ull *iend)
{
return gomp_loop_ull_ordered_static_next (istart, iend);
}
bool
GOMP_loop_ull_ordered_dynamic_next (gomp_ull *istart, gomp_ull *iend)
{
return gomp_loop_ull_ordered_dynamic_next (istart, iend);
}
bool
GOMP_loop_ull_ordered_guided_next (gomp_ull *istart, gomp_ull *iend)
{
return gomp_loop_ull_ordered_guided_next (istart, iend);
}
#endif

View File

@ -1 +0,0 @@
/* Everything is in the header. */

View File

@ -1,58 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This is the default PTHREADS implementation of a mutex synchronization
mechanism for libgomp. This type is private to the library. */
#ifndef GOMP_MUTEX_H
#define GOMP_MUTEX_H 1
#include <pthread.h>
typedef pthread_mutex_t gomp_mutex_t;
#define GOMP_MUTEX_INIT_0 0
static inline void gomp_mutex_init (gomp_mutex_t *mutex)
{
pthread_mutex_init (mutex, NULL);
}
static inline void gomp_mutex_lock (gomp_mutex_t *mutex)
{
pthread_mutex_lock (mutex);
}
static inline void gomp_mutex_unlock (gomp_mutex_t *mutex)
{
pthread_mutex_unlock (mutex);
}
static inline void gomp_mutex_destroy (gomp_mutex_t *mutex)
{
pthread_mutex_destroy (mutex);
}
#endif /* GOMP_MUTEX_H */

View File

@ -1,23 +0,0 @@
/* This header is used during the build process to find the size and
alignment of the public OpenMP locks, so that we can export data
structures without polluting the namespace.
In this default POSIX implementation, we used to map the two locks to the
same PTHREADS primitive, but for OpenMP 3.0 sem_t needs to be used
instead, as pthread_mutex_unlock should not be called by different
thread than the one that called pthread_mutex_lock. */
#include <pthread.h>
#include <semaphore.h>
typedef pthread_mutex_t omp_lock_25_t;
typedef struct { pthread_mutex_t lock; int count; } omp_nest_lock_25_t;
#ifdef HAVE_BROKEN_POSIX_SEMAPHORES
/* If we don't have working semaphores, we'll make all explicit tasks
tied to the creating thread. */
typedef pthread_mutex_t omp_lock_t;
typedef struct { pthread_mutex_t lock; int count; void *owner; } omp_nest_lock_t;
#else
typedef sem_t omp_lock_t;
typedef struct { sem_t lock; int count; void *owner; } omp_nest_lock_t;
#endif

View File

@ -1,128 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
#ifndef _OMP_H
#define _OMP_H 1
#ifndef _LIBGOMP_OMP_LOCK_DEFINED
#define _LIBGOMP_OMP_LOCK_DEFINED 1
/* These two structures get edited by the libgomp build process to
reflect the shape of the two types. Their internals are private
to the library. */
typedef struct
{
unsigned char _x[4]
__attribute__((__aligned__(4)));
} omp_lock_t;
typedef struct
{
unsigned char _x[8 + sizeof (void *)]
__attribute__((__aligned__(sizeof(void*))));
} omp_nest_lock_t;
#endif
typedef enum omp_sched_t
{
omp_sched_static = 1,
omp_sched_dynamic = 2,
omp_sched_guided = 3,
omp_sched_auto = 4
} omp_sched_t;
typedef enum omp_proc_bind_t
{
omp_proc_bind_false = 0,
omp_proc_bind_true = 1,
omp_proc_bind_master = 2,
omp_proc_bind_close = 3,
omp_proc_bind_spread = 4
} omp_proc_bind_t;
#ifdef __cplusplus
extern "C" {
# define __GOMP_NOTHROW throw ()
#else
# define __GOMP_NOTHROW __attribute__((__nothrow__))
#endif
extern void omp_set_num_threads (int) __GOMP_NOTHROW;
extern int omp_get_num_threads (void) __GOMP_NOTHROW;
extern int omp_get_max_threads (void) __GOMP_NOTHROW;
extern int omp_get_thread_num (void) __GOMP_NOTHROW;
extern int omp_get_num_procs (void) __GOMP_NOTHROW;
extern int omp_in_parallel (void) __GOMP_NOTHROW;
extern void omp_set_dynamic (int) __GOMP_NOTHROW;
extern int omp_get_dynamic (void) __GOMP_NOTHROW;
extern void omp_set_nested (int) __GOMP_NOTHROW;
extern int omp_get_nested (void) __GOMP_NOTHROW;
extern void omp_init_lock (omp_lock_t *) __GOMP_NOTHROW;
extern void omp_destroy_lock (omp_lock_t *) __GOMP_NOTHROW;
extern void omp_set_lock (omp_lock_t *) __GOMP_NOTHROW;
extern void omp_unset_lock (omp_lock_t *) __GOMP_NOTHROW;
extern int omp_test_lock (omp_lock_t *) __GOMP_NOTHROW;
extern void omp_init_nest_lock (omp_nest_lock_t *) __GOMP_NOTHROW;
extern void omp_destroy_nest_lock (omp_nest_lock_t *) __GOMP_NOTHROW;
extern void omp_set_nest_lock (omp_nest_lock_t *) __GOMP_NOTHROW;
extern void omp_unset_nest_lock (omp_nest_lock_t *) __GOMP_NOTHROW;
extern int omp_test_nest_lock (omp_nest_lock_t *) __GOMP_NOTHROW;
extern double omp_get_wtime (void) __GOMP_NOTHROW;
extern double omp_get_wtick (void) __GOMP_NOTHROW;
extern void omp_set_schedule (omp_sched_t, int) __GOMP_NOTHROW;
extern void omp_get_schedule (omp_sched_t *, int *) __GOMP_NOTHROW;
extern int omp_get_thread_limit (void) __GOMP_NOTHROW;
extern void omp_set_max_active_levels (int) __GOMP_NOTHROW;
extern int omp_get_max_active_levels (void) __GOMP_NOTHROW;
extern int omp_get_level (void) __GOMP_NOTHROW;
extern int omp_get_ancestor_thread_num (int) __GOMP_NOTHROW;
extern int omp_get_team_size (int) __GOMP_NOTHROW;
extern int omp_get_active_level (void) __GOMP_NOTHROW;
extern int omp_in_final (void) __GOMP_NOTHROW;
extern int omp_get_cancellation (void) __GOMP_NOTHROW;
extern omp_proc_bind_t omp_get_proc_bind (void) __GOMP_NOTHROW;
extern void omp_set_default_device (int) __GOMP_NOTHROW;
extern int omp_get_default_device (void) __GOMP_NOTHROW;
extern int omp_get_num_devices (void) __GOMP_NOTHROW;
extern int omp_get_num_teams (void) __GOMP_NOTHROW;
extern int omp_get_team_num (void) __GOMP_NOTHROW;
extern int omp_is_initial_device (void) __GOMP_NOTHROW;
#ifdef __cplusplus
}
#endif
#endif /* _OMP_H */

View File

@ -1,98 +0,0 @@
! Copyright (C) 2005-2015 Free Software Foundation, Inc.
! Contributed by Jakub Jelinek <jakub@redhat.com>.
! This file is part of the GNU Offloading and Multi Processing Library
! (libgomp).
! Libgomp 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.
! Libgomp 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/>.
integer omp_lock_kind, omp_nest_lock_kind, openmp_version
parameter (omp_lock_kind = 8)
parameter (omp_nest_lock_kind = 8)
integer omp_sched_kind
parameter (omp_sched_kind = 4)
integer (omp_sched_kind) omp_sched_static, omp_sched_dynamic
integer (omp_sched_kind) omp_sched_guided, omp_sched_auto
parameter (omp_sched_static = 1)
parameter (omp_sched_dynamic = 2)
parameter (omp_sched_guided = 3)
parameter (omp_sched_auto = 4)
integer omp_proc_bind_kind
parameter (omp_proc_bind_kind = 4)
integer (omp_proc_bind_kind) omp_proc_bind_false
integer (omp_proc_bind_kind) omp_proc_bind_true
integer (omp_proc_bind_kind) omp_proc_bind_master
integer (omp_proc_bind_kind) omp_proc_bind_close
integer (omp_proc_bind_kind) omp_proc_bind_spread
parameter (omp_proc_bind_false = 0)
parameter (omp_proc_bind_true = 1)
parameter (omp_proc_bind_master = 2)
parameter (omp_proc_bind_close = 3)
parameter (omp_proc_bind_spread = 4)
parameter (openmp_version = 201307)
external omp_init_lock, omp_init_nest_lock
external omp_destroy_lock, omp_destroy_nest_lock
external omp_set_lock, omp_set_nest_lock
external omp_unset_lock, omp_unset_nest_lock
external omp_set_dynamic, omp_set_nested
external omp_set_num_threads
external omp_get_dynamic, omp_get_nested
logical(4) omp_get_dynamic, omp_get_nested
external omp_test_lock, omp_in_parallel
logical(4) omp_test_lock, omp_in_parallel
external omp_get_max_threads, omp_get_num_procs
integer(4) omp_get_max_threads, omp_get_num_procs
external omp_get_num_threads, omp_get_thread_num
integer(4) omp_get_num_threads, omp_get_thread_num
external omp_test_nest_lock
integer(4) omp_test_nest_lock
external omp_get_wtick, omp_get_wtime
double precision omp_get_wtick, omp_get_wtime
external omp_set_schedule, omp_get_schedule
external omp_get_thread_limit, omp_set_max_active_levels
external omp_get_max_active_levels, omp_get_level
external omp_get_ancestor_thread_num, omp_get_team_size
external omp_get_active_level
integer(4) omp_get_thread_limit, omp_get_max_active_levels
integer(4) omp_get_level, omp_get_ancestor_thread_num
integer(4) omp_get_team_size, omp_get_active_level
external omp_in_final
logical(4) omp_in_final
external omp_get_cancelllation
logical(4) omp_get_cancelllation
external omp_get_proc_bind
integer(omp_proc_bind_kind) omp_get_proc_bind
external omp_set_default_device, omp_get_default_device
external omp_get_num_devices, omp_get_num_teams
external omp_get_team_num
integer(4) omp_get_default_device, omp_get_num_devices
integer(4) omp_get_num_teams, omp_get_team_num
external omp_is_initial_device
logical(4) omp_is_initial_device

View File

@ -1,98 +0,0 @@
! Copyright (C) 2005-2015 Free Software Foundation, Inc.
! Contributed by Jakub Jelinek <jakub@redhat.com>.
! This file is part of the GNU Offloading and Multi Processing Library
! (libgomp).
! Libgomp 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.
! Libgomp 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/>.
integer omp_lock_kind, omp_nest_lock_kind, openmp_version
parameter (omp_lock_kind = @OMP_LOCK_KIND@)
parameter (omp_nest_lock_kind = @OMP_NEST_LOCK_KIND@)
integer omp_sched_kind
parameter (omp_sched_kind = 4)
integer (omp_sched_kind) omp_sched_static, omp_sched_dynamic
integer (omp_sched_kind) omp_sched_guided, omp_sched_auto
parameter (omp_sched_static = 1)
parameter (omp_sched_dynamic = 2)
parameter (omp_sched_guided = 3)
parameter (omp_sched_auto = 4)
integer omp_proc_bind_kind
parameter (omp_proc_bind_kind = 4)
integer (omp_proc_bind_kind) omp_proc_bind_false
integer (omp_proc_bind_kind) omp_proc_bind_true
integer (omp_proc_bind_kind) omp_proc_bind_master
integer (omp_proc_bind_kind) omp_proc_bind_close
integer (omp_proc_bind_kind) omp_proc_bind_spread
parameter (omp_proc_bind_false = 0)
parameter (omp_proc_bind_true = 1)
parameter (omp_proc_bind_master = 2)
parameter (omp_proc_bind_close = 3)
parameter (omp_proc_bind_spread = 4)
parameter (openmp_version = 201307)
external omp_init_lock, omp_init_nest_lock
external omp_destroy_lock, omp_destroy_nest_lock
external omp_set_lock, omp_set_nest_lock
external omp_unset_lock, omp_unset_nest_lock
external omp_set_dynamic, omp_set_nested
external omp_set_num_threads
external omp_get_dynamic, omp_get_nested
logical(4) omp_get_dynamic, omp_get_nested
external omp_test_lock, omp_in_parallel
logical(4) omp_test_lock, omp_in_parallel
external omp_get_max_threads, omp_get_num_procs
integer(4) omp_get_max_threads, omp_get_num_procs
external omp_get_num_threads, omp_get_thread_num
integer(4) omp_get_num_threads, omp_get_thread_num
external omp_test_nest_lock
integer(4) omp_test_nest_lock
external omp_get_wtick, omp_get_wtime
double precision omp_get_wtick, omp_get_wtime
external omp_set_schedule, omp_get_schedule
external omp_get_thread_limit, omp_set_max_active_levels
external omp_get_max_active_levels, omp_get_level
external omp_get_ancestor_thread_num, omp_get_team_size
external omp_get_active_level
integer(4) omp_get_thread_limit, omp_get_max_active_levels
integer(4) omp_get_level, omp_get_ancestor_thread_num
integer(4) omp_get_team_size, omp_get_active_level
external omp_in_final
logical(4) omp_in_final
external omp_get_cancelllation
logical(4) omp_get_cancelllation
external omp_get_proc_bind
integer(omp_proc_bind_kind) omp_get_proc_bind
external omp_set_default_device, omp_get_default_device
external omp_get_num_devices, omp_get_num_teams
external omp_get_team_num
integer(4) omp_get_default_device, omp_get_num_devices
integer(4) omp_get_num_teams, omp_get_team_num
external omp_is_initial_device
logical(4) omp_is_initial_device

View File

@ -1,252 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file handles the ORDERED construct. */
#include "libgomp.h"
/* This function is called when first allocating an iteration block. That
is, the thread is not currently on the queue. The work-share lock must
be held on entry. */
void
gomp_ordered_first (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
struct gomp_work_share *ws = thr->ts.work_share;
unsigned index;
/* Work share constructs can be orphaned. */
if (team == NULL || team->nthreads == 1)
return;
index = ws->ordered_cur + ws->ordered_num_used;
if (index >= team->nthreads)
index -= team->nthreads;
ws->ordered_team_ids[index] = thr->ts.team_id;
/* If this is the first and only thread in the queue, then there is
no one to release us when we get to our ordered section. Post to
our own release queue now so that we won't block later. */
if (ws->ordered_num_used++ == 0)
gomp_sem_post (team->ordered_release[thr->ts.team_id]);
}
/* This function is called when completing the last iteration block. That
is, there are no more iterations to perform and so the thread should be
removed from the queue entirely. Because of the way ORDERED blocks are
managed, it follows that we currently own access to the ORDERED block,
and should now pass it on to the next thread. The work-share lock must
be held on entry. */
void
gomp_ordered_last (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
struct gomp_work_share *ws = thr->ts.work_share;
unsigned next_id;
/* Work share constructs can be orphaned. */
if (team == NULL || team->nthreads == 1)
return;
/* We're no longer the owner. */
ws->ordered_owner = -1;
/* If we're not the last thread in the queue, then wake the next. */
if (--ws->ordered_num_used > 0)
{
unsigned next = ws->ordered_cur + 1;
if (next == team->nthreads)
next = 0;
ws->ordered_cur = next;
next_id = ws->ordered_team_ids[next];
gomp_sem_post (team->ordered_release[next_id]);
}
}
/* This function is called when allocating a subsequent allocation block.
That is, we're done with the current iteration block and we're allocating
another. This is the logical combination of a call to gomp_ordered_last
followed by a call to gomp_ordered_first. The work-share lock must be
held on entry. */
void
gomp_ordered_next (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
struct gomp_work_share *ws = thr->ts.work_share;
unsigned index, next_id;
/* Work share constructs can be orphaned. */
if (team == NULL || team->nthreads == 1)
return;
/* We're no longer the owner. */
ws->ordered_owner = -1;
/* If there's only one thread in the queue, that must be us. */
if (ws->ordered_num_used == 1)
{
/* We have a similar situation as in gomp_ordered_first
where we need to post to our own release semaphore. */
gomp_sem_post (team->ordered_release[thr->ts.team_id]);
return;
}
/* If the queue is entirely full, then we move ourself to the end of
the queue merely by incrementing ordered_cur. Only if it's not
full do we have to write our id. */
if (ws->ordered_num_used < team->nthreads)
{
index = ws->ordered_cur + ws->ordered_num_used;
if (index >= team->nthreads)
index -= team->nthreads;
ws->ordered_team_ids[index] = thr->ts.team_id;
}
index = ws->ordered_cur + 1;
if (index == team->nthreads)
index = 0;
ws->ordered_cur = index;
next_id = ws->ordered_team_ids[index];
gomp_sem_post (team->ordered_release[next_id]);
}
/* This function is called when a statically scheduled loop is first
being created. */
void
gomp_ordered_static_init (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
if (team == NULL || team->nthreads == 1)
return;
gomp_sem_post (team->ordered_release[0]);
}
/* This function is called when a statically scheduled loop is moving to
the next allocation block. Static schedules are not first come first
served like the others, so we're to move to the numerically next thread,
not the next thread on a list. The work-share lock should *not* be held
on entry. */
void
gomp_ordered_static_next (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
struct gomp_work_share *ws = thr->ts.work_share;
unsigned id = thr->ts.team_id;
if (team == NULL || team->nthreads == 1)
return;
ws->ordered_owner = -1;
/* This thread currently owns the lock. Increment the owner. */
if (++id == team->nthreads)
id = 0;
ws->ordered_team_ids[0] = id;
gomp_sem_post (team->ordered_release[id]);
}
/* This function is called when we need to assert that the thread owns the
ordered section. Due to the problem of posted-but-not-waited semaphores,
this needs to happen before completing a loop iteration. */
void
gomp_ordered_sync (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
struct gomp_work_share *ws = thr->ts.work_share;
/* Work share constructs can be orphaned. But this clearly means that
we are the only thread, and so we automatically own the section. */
if (team == NULL || team->nthreads == 1)
return;
/* ??? I believe it to be safe to access this data without taking the
ws->lock. The only presumed race condition is with the previous
thread on the queue incrementing ordered_cur such that it points
to us, concurrently with our check below. But our team_id is
already present in the queue, and the other thread will always
post to our release semaphore. So the two cases are that we will
either win the race an momentarily block on the semaphore, or lose
the race and find the semaphore already unlocked and so not block.
Either way we get correct results.
However, there is an implicit flush on entry to an ordered region,
so we do need to have a barrier here. If we were taking a lock
this could be MEMMODEL_RELEASE since the acquire would be coverd
by the lock. */
__atomic_thread_fence (MEMMODEL_ACQ_REL);
if (ws->ordered_owner != thr->ts.team_id)
{
gomp_sem_wait (team->ordered_release[thr->ts.team_id]);
ws->ordered_owner = thr->ts.team_id;
}
}
/* This function is called by user code when encountering the start of an
ORDERED block. We must check to see if the current thread is at the
head of the queue, and if not, block. */
#ifdef HAVE_ATTRIBUTE_ALIAS
extern void GOMP_ordered_start (void)
__attribute__((alias ("gomp_ordered_sync")));
#else
void
GOMP_ordered_start (void)
{
gomp_ordered_sync ();
}
#endif
/* This function is called by user code when encountering the end of an
ORDERED block. With the current ORDERED implementation there's nothing
for us to do.
However, the current implementation has a flaw in that it does not allow
the next thread into the ORDERED section immediately after the current
thread exits the ORDERED section in its last iteration. The existance
of this function allows the implementation to change. */
void
GOMP_ordered_end (void)
{
}

View File

@ -1,302 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file handles the (bare) PARALLEL construct. */
#include "libgomp.h"
#include <limits.h>
/* Determine the number of threads to be launched for a PARALLEL construct.
This algorithm is explicitly described in OpenMP 3.0 section 2.4.1.
SPECIFIED is a combination of the NUM_THREADS clause and the IF clause.
If the IF clause is false, SPECIFIED is forced to 1. When NUM_THREADS
is not present, SPECIFIED is 0. */
unsigned
gomp_resolve_num_threads (unsigned specified, unsigned count)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_task_icv *icv;
unsigned threads_requested, max_num_threads, num_threads;
unsigned long busy;
struct gomp_thread_pool *pool;
icv = gomp_icv (false);
if (specified == 1)
return 1;
else if (thr->ts.active_level >= 1 && !icv->nest_var)
return 1;
else if (thr->ts.active_level >= gomp_max_active_levels_var)
return 1;
/* If NUM_THREADS not specified, use nthreads_var. */
if (specified == 0)
threads_requested = icv->nthreads_var;
else
threads_requested = specified;
max_num_threads = threads_requested;
/* If dynamic threads are enabled, bound the number of threads
that we launch. */
if (icv->dyn_var)
{
unsigned dyn = gomp_dynamic_max_threads ();
if (dyn < max_num_threads)
max_num_threads = dyn;
/* Optimization for parallel sections. */
if (count && count < max_num_threads)
max_num_threads = count;
}
/* UINT_MAX stands for infinity. */
if (__builtin_expect (icv->thread_limit_var == UINT_MAX, 1)
|| max_num_threads == 1)
return max_num_threads;
/* The threads_busy counter lives in thread_pool, if there
isn't a thread_pool yet, there must be just one thread
in the contention group. If thr->team is NULL, this isn't
nested parallel, so there is just one thread in the
contention group as well, no need to handle it atomically. */
pool = thr->thread_pool;
if (thr->ts.team == NULL)
{
num_threads = max_num_threads;
if (num_threads > icv->thread_limit_var)
num_threads = icv->thread_limit_var;
if (pool)
pool->threads_busy = num_threads;
return num_threads;
}
#ifdef HAVE_SYNC_BUILTINS
do
{
busy = pool->threads_busy;
num_threads = max_num_threads;
if (icv->thread_limit_var - busy + 1 < num_threads)
num_threads = icv->thread_limit_var - busy + 1;
}
while (__sync_val_compare_and_swap (&pool->threads_busy,
busy, busy + num_threads - 1)
!= busy);
#else
gomp_mutex_lock (&gomp_managed_threads_lock);
num_threads = max_num_threads;
busy = pool->threads_busy;
if (icv->thread_limit_var - busy + 1 < num_threads)
num_threads = icv->thread_limit_var - busy + 1;
pool->threads_busy += num_threads - 1;
gomp_mutex_unlock (&gomp_managed_threads_lock);
#endif
return num_threads;
}
void
GOMP_parallel_start (void (*fn) (void *), void *data, unsigned num_threads)
{
num_threads = gomp_resolve_num_threads (num_threads, 0);
gomp_team_start (fn, data, num_threads, 0, gomp_new_team (num_threads));
}
void
GOMP_parallel_end (void)
{
struct gomp_task_icv *icv = gomp_icv (false);
if (__builtin_expect (icv->thread_limit_var != UINT_MAX, 0))
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
unsigned int nthreads = team ? team->nthreads : 1;
gomp_team_end ();
if (nthreads > 1)
{
/* If not nested, there is just one thread in the
contention group left, no need for atomicity. */
if (thr->ts.team == NULL)
thr->thread_pool->threads_busy = 1;
else
{
#ifdef HAVE_SYNC_BUILTINS
__sync_fetch_and_add (&thr->thread_pool->threads_busy,
1UL - nthreads);
#else
gomp_mutex_lock (&gomp_managed_threads_lock);
thr->thread_pool->threads_busy -= nthreads - 1;
gomp_mutex_unlock (&gomp_managed_threads_lock);
#endif
}
}
}
else
gomp_team_end ();
}
ialias (GOMP_parallel_end)
void
GOMP_parallel (void (*fn) (void *), void *data, unsigned num_threads, unsigned int flags)
{
num_threads = gomp_resolve_num_threads (num_threads, 0);
gomp_team_start (fn, data, num_threads, flags, gomp_new_team (num_threads));
fn (data);
ialias_call (GOMP_parallel_end) ();
}
bool
GOMP_cancellation_point (int which)
{
if (!gomp_cancel_var)
return false;
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
if (which & (GOMP_CANCEL_LOOP | GOMP_CANCEL_SECTIONS))
{
if (team == NULL)
return false;
return team->work_share_cancelled != 0;
}
else if (which & GOMP_CANCEL_TASKGROUP)
{
if (thr->task->taskgroup && thr->task->taskgroup->cancelled)
return true;
/* FALLTHRU into the GOMP_CANCEL_PARALLEL case,
as #pragma omp cancel parallel also cancels all explicit
tasks. */
}
if (team)
return gomp_team_barrier_cancelled (&team->barrier);
return false;
}
ialias (GOMP_cancellation_point)
bool
GOMP_cancel (int which, bool do_cancel)
{
if (!gomp_cancel_var)
return false;
if (!do_cancel)
return ialias_call (GOMP_cancellation_point) (which);
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
if (which & (GOMP_CANCEL_LOOP | GOMP_CANCEL_SECTIONS))
{
/* In orphaned worksharing region, all we want to cancel
is current thread. */
if (team != NULL)
team->work_share_cancelled = 1;
return true;
}
else if (which & GOMP_CANCEL_TASKGROUP)
{
if (thr->task->taskgroup && !thr->task->taskgroup->cancelled)
{
gomp_mutex_lock (&team->task_lock);
thr->task->taskgroup->cancelled = true;
gomp_mutex_unlock (&team->task_lock);
}
return true;
}
team->team_cancelled = 1;
gomp_team_barrier_cancel (team);
return true;
}
/* The public OpenMP API for thread and team related inquiries. */
int
omp_get_num_threads (void)
{
struct gomp_team *team = gomp_thread ()->ts.team;
return team ? team->nthreads : 1;
}
int
omp_get_thread_num (void)
{
return gomp_thread ()->ts.team_id;
}
/* This wasn't right for OpenMP 2.5. Active region used to be non-zero
when the IF clause doesn't evaluate to false, starting with OpenMP 3.0
it is non-zero with more than one thread in the team. */
int
omp_in_parallel (void)
{
return gomp_thread ()->ts.active_level > 0;
}
int
omp_get_level (void)
{
return gomp_thread ()->ts.level;
}
int
omp_get_ancestor_thread_num (int level)
{
struct gomp_team_state *ts = &gomp_thread ()->ts;
if (level < 0 || level > ts->level)
return -1;
for (level = ts->level - level; level > 0; --level)
ts = &ts->team->prev_ts;
return ts->team_id;
}
int
omp_get_team_size (int level)
{
struct gomp_team_state *ts = &gomp_thread ()->ts;
if (level < 0 || level > ts->level)
return -1;
for (level = ts->level - level; level > 0; --level)
ts = &ts->team->prev_ts;
if (ts->team == NULL)
return 1;
else
return ts->team->nthreads;
}
int
omp_get_active_level (void)
{
return gomp_thread ()->ts.active_level;
}
ialias (omp_get_num_threads)
ialias (omp_get_thread_num)
ialias (omp_in_parallel)
ialias (omp_get_level)
ialias (omp_get_ancestor_thread_num)
ialias (omp_get_team_size)
ialias (omp_get_active_level)

View File

@ -1,111 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file contains system specific routines related to counting
online processors and dynamic load balancing. It is expected that
a system may well want to write special versions of each of these.
The following implementation uses a mix of POSIX and BSD routines. */
#include "libgomp.h"
#include <unistd.h>
#include <stdlib.h>
#ifdef HAVE_GETLOADAVG
# ifdef HAVE_SYS_LOADAVG_H
# include <sys/loadavg.h>
# endif
#endif
unsigned int get_num_cpus(void);
/* At startup, determine the default number of threads. It would seem
this should be related to the number of cpus online. */
void
gomp_init_num_threads (void)
{
#ifdef __hermit__
gomp_global_icv.nthreads_var = get_num_cpus();
#elif defined(_SC_NPROCESSORS_ONLN)
gomp_global_icv.nthreads_var = sysconf (_SC_NPROCESSORS_ONLN);
#endif
}
/* When OMP_DYNAMIC is set, at thread launch determine the number of
threads we should spawn for this team. */
/* ??? I have no idea what best practice for this is. Surely some
function of the number of processors that are *still* online and
the load average. Here I use the number of processors online
minus the 15 minute load average. */
unsigned
gomp_dynamic_max_threads (void)
{
unsigned n_onln, loadavg;
#ifndef __hermit__
unsigned nthreads_var = gomp_icv (false)->nthreads_var;
#endif
#ifdef __hermit__
n_onln = get_num_cpus();
#elif defined(_SC_NPROCESSORS_ONLN)
n_onln = sysconf (_SC_NPROCESSORS_ONLN);
if (n_onln > nthreads_var)
n_onln = nthreads_var;
#else
n_onln = nthreads_var;
#endif
loadavg = 0;
#ifdef HAVE_GETLOADAVG
{
double dloadavg[3];
if (getloadavg (dloadavg, 3) == 3)
{
/* Add 0.1 to get a kind of biased rounding. */
loadavg = dloadavg[2] + 0.1;
}
}
#endif
if (loadavg >= n_onln)
return 1;
else
return n_onln - loadavg;
}
int
omp_get_num_procs (void)
{
#ifdef __hermit__
return get_num_cpus();
#elif defined(_SC_NPROCESSORS_ONLN);
return sysconf (_SC_NPROCESSORS_ONLN);
#else
return gomp_icv (false)->nthreads_var;
#endif
}
ialias (omp_get_num_procs)

View File

@ -1 +0,0 @@
/* Everything is in the header. */

View File

@ -1,66 +0,0 @@
/* Copyright (C) 2008-2015 Free Software Foundation, Inc.
Contributed by Jakub Jelinek <jakub@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This is a generic POSIX implementation of a mutex synchronization
mechanism for libgomp. This type is private to the library. */
#ifndef GOMP_PTRLOCK_H
#define GOMP_PTRLOCK_H 1
typedef struct { void *ptr; gomp_mutex_t lock; } gomp_ptrlock_t;
static inline void gomp_ptrlock_init (gomp_ptrlock_t *ptrlock, void *ptr)
{
ptrlock->ptr = ptr;
gomp_mutex_init (&ptrlock->lock);
}
static inline void *gomp_ptrlock_get (gomp_ptrlock_t *ptrlock)
{
if (ptrlock->ptr != NULL)
return ptrlock->ptr;
gomp_mutex_lock (&ptrlock->lock);
if (ptrlock->ptr != NULL)
{
gomp_mutex_unlock (&ptrlock->lock);
return ptrlock->ptr;
}
return NULL;
}
static inline void gomp_ptrlock_set (gomp_ptrlock_t *ptrlock, void *ptr)
{
ptrlock->ptr = ptr;
gomp_mutex_unlock (&ptrlock->lock);
}
static inline void gomp_ptrlock_destroy (gomp_ptrlock_t *ptrlock)
{
gomp_mutex_destroy (&ptrlock->lock);
}
#endif /* GOMP_PTRLOCK_H */

View File

@ -1,182 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file handles the SECTIONS construct. */
#include "libgomp.h"
/* Initialize the given work share construct from the given arguments. */
static inline void
gomp_sections_init (struct gomp_work_share *ws, unsigned count)
{
ws->sched = GFS_DYNAMIC;
ws->chunk_size = 1;
ws->end = count + 1L;
ws->incr = 1;
ws->next = 1;
#ifdef HAVE_SYNC_BUILTINS
/* Prepare things to make each iteration faster. */
if (sizeof (long) > sizeof (unsigned))
ws->mode = 1;
else
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
long nthreads = team ? team->nthreads : 1;
ws->mode = ((nthreads | ws->end)
< 1UL << (sizeof (long) * __CHAR_BIT__ / 2 - 1));
}
#else
ws->mode = 0;
#endif
}
/* This routine is called when first encountering a sections construct
that is not bound directly to a parallel construct. The first thread
that arrives will create the work-share construct; subsequent threads
will see the construct exists and allocate work from it.
COUNT is the number of sections in this construct.
Returns the 1-based section number for this thread to perform, or 0 if
all work was assigned to other threads prior to this thread's arrival. */
unsigned
GOMP_sections_start (unsigned count)
{
struct gomp_thread *thr = gomp_thread ();
long s, e, ret;
if (gomp_work_share_start (false))
{
gomp_sections_init (thr->ts.work_share, count);
gomp_work_share_init_done ();
}
#ifdef HAVE_SYNC_BUILTINS
if (gomp_iter_dynamic_next (&s, &e))
ret = s;
else
ret = 0;
#else
gomp_mutex_lock (&thr->ts.work_share->lock);
if (gomp_iter_dynamic_next_locked (&s, &e))
ret = s;
else
ret = 0;
gomp_mutex_unlock (&thr->ts.work_share->lock);
#endif
return ret;
}
/* This routine is called when the thread completes processing of the
section currently assigned to it. If the work-share construct is
bound directly to a parallel construct, then the construct may have
been set up before the parallel. In which case, this may be the
first iteration for the thread.
Returns the 1-based section number for this thread to perform, or 0 if
all work was assigned to other threads prior to this thread's arrival. */
unsigned
GOMP_sections_next (void)
{
long s, e, ret;
#ifdef HAVE_SYNC_BUILTINS
if (gomp_iter_dynamic_next (&s, &e))
ret = s;
else
ret = 0;
#else
struct gomp_thread *thr = gomp_thread ();
gomp_mutex_lock (&thr->ts.work_share->lock);
if (gomp_iter_dynamic_next_locked (&s, &e))
ret = s;
else
ret = 0;
gomp_mutex_unlock (&thr->ts.work_share->lock);
#endif
return ret;
}
/* This routine pre-initializes a work-share construct to avoid one
synchronization once we get into the loop. */
void
GOMP_parallel_sections_start (void (*fn) (void *), void *data,
unsigned num_threads, unsigned count)
{
struct gomp_team *team;
num_threads = gomp_resolve_num_threads (num_threads, count);
team = gomp_new_team (num_threads);
gomp_sections_init (&team->work_shares[0], count);
gomp_team_start (fn, data, num_threads, 0, team);
}
ialias_redirect (GOMP_parallel_end)
void
GOMP_parallel_sections (void (*fn) (void *), void *data,
unsigned num_threads, unsigned count, unsigned flags)
{
struct gomp_team *team;
num_threads = gomp_resolve_num_threads (num_threads, count);
team = gomp_new_team (num_threads);
gomp_sections_init (&team->work_shares[0], count);
gomp_team_start (fn, data, num_threads, flags, team);
fn (data);
GOMP_parallel_end ();
}
/* The GOMP_section_end* routines are called after the thread is told
that all sections are complete. The first two versions synchronize
all threads; the nowait version does not. */
void
GOMP_sections_end (void)
{
gomp_work_share_end ();
}
bool
GOMP_sections_end_cancel (void)
{
return gomp_work_share_end_cancel ();
}
void
GOMP_sections_end_nowait (void)
{
gomp_work_share_end_nowait ();
}

View File

@ -1,124 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This is the default POSIX 1003.1b implementation of a semaphore
synchronization mechanism for libgomp. This type is private to
the library.
This is a bit heavy weight for what we need, in that we're not
interested in sem_wait as a cancelation point, but it's not too
bad for a default. */
#include "libgomp.h"
#ifdef HAVE_BROKEN_POSIX_SEMAPHORES
#include <stdlib.h>
void gomp_sem_init (gomp_sem_t *sem, int value)
{
int ret;
ret = pthread_mutex_init (&sem->mutex, NULL);
if (ret)
return;
ret = pthread_cond_init (&sem->cond, NULL);
if (ret)
return;
sem->value = value;
}
void gomp_sem_wait (gomp_sem_t *sem)
{
int ret;
ret = pthread_mutex_lock (&sem->mutex);
if (ret)
return;
if (sem->value > 0)
{
sem->value--;
ret = pthread_mutex_unlock (&sem->mutex);
return;
}
while (sem->value <= 0)
{
ret = pthread_cond_wait (&sem->cond, &sem->mutex);
if (ret)
{
pthread_mutex_unlock (&sem->mutex);
return;
}
}
sem->value--;
ret = pthread_mutex_unlock (&sem->mutex);
return;
}
void gomp_sem_post (gomp_sem_t *sem)
{
int ret;
ret = pthread_mutex_lock (&sem->mutex);
if (ret)
return;
sem->value++;
ret = pthread_mutex_unlock (&sem->mutex);
if (ret)
return;
ret = pthread_cond_signal (&sem->cond);
return;
}
void gomp_sem_destroy (gomp_sem_t *sem)
{
int ret;
ret = pthread_mutex_destroy (&sem->mutex);
if (ret)
return;
ret = pthread_cond_destroy (&sem->cond);
return;
}
#else /* HAVE_BROKEN_POSIX_SEMAPHORES */
void
gomp_sem_wait (gomp_sem_t *sem)
{
/* With POSIX, the wait can be canceled by signals. We don't want that.
It is expected that the return value here is -1 and errno is EINTR. */
while (sem_wait (sem) != 0)
continue;
}
#endif

View File

@ -1,88 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This is the default POSIX 1003.1b implementation of a semaphore
synchronization mechanism for libgomp. This type is private to
the library.
This is a bit heavy weight for what we need, in that we're not
interested in sem_wait as a cancelation point, but it's not too
bad for a default. */
#ifndef GOMP_SEM_H
#define GOMP_SEM_H 1
#ifdef HAVE_ATTRIBUTE_VISIBILITY
# pragma GCC visibility push(default)
#endif
#include <semaphore.h>
#ifdef HAVE_ATTRIBUTE_VISIBILITY
# pragma GCC visibility pop
#endif
#ifdef HAVE_BROKEN_POSIX_SEMAPHORES
#include <pthread.h>
struct gomp_sem
{
pthread_mutex_t mutex;
pthread_cond_t cond;
int value;
};
typedef struct gomp_sem gomp_sem_t;
extern void gomp_sem_init (gomp_sem_t *sem, int value);
extern void gomp_sem_wait (gomp_sem_t *sem);
extern void gomp_sem_post (gomp_sem_t *sem);
extern void gomp_sem_destroy (gomp_sem_t *sem);
#else /* HAVE_BROKEN_POSIX_SEMAPHORES */
typedef sem_t gomp_sem_t;
static inline void gomp_sem_init (gomp_sem_t *sem, int value)
{
sem_init (sem, 0, value);
}
extern void gomp_sem_wait (gomp_sem_t *sem);
static inline void gomp_sem_post (gomp_sem_t *sem)
{
sem_post (sem);
}
static inline void gomp_sem_destroy (gomp_sem_t *sem)
{
sem_destroy (sem);
}
#endif /* doesn't HAVE_BROKEN_POSIX_SEMAPHORES */
#endif /* GOMP_SEM_H */

View File

@ -1,105 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file handles the SINGLE construct. */
#include "libgomp.h"
/* This routine is called when first encountering a SINGLE construct that
doesn't have a COPYPRIVATE clause. Returns true if this is the thread
that should execute the clause. */
bool
GOMP_single_start (void)
{
#ifdef HAVE_SYNC_BUILTINS
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
unsigned long single_count;
if (__builtin_expect (team == NULL, 0))
return true;
single_count = thr->ts.single_count++;
return __sync_bool_compare_and_swap (&team->single_count, single_count,
single_count + 1L);
#else
bool ret = gomp_work_share_start (false);
if (ret)
gomp_work_share_init_done ();
gomp_work_share_end_nowait ();
return ret;
#endif
}
/* This routine is called when first encountering a SINGLE construct that
does have a COPYPRIVATE clause. Returns NULL if this is the thread
that should execute the clause; otherwise the return value is pointer
given to GOMP_single_copy_end by the thread that did execute the clause. */
void *
GOMP_single_copy_start (void)
{
struct gomp_thread *thr = gomp_thread ();
bool first;
void *ret;
first = gomp_work_share_start (false);
if (first)
{
gomp_work_share_init_done ();
ret = NULL;
}
else
{
gomp_team_barrier_wait (&thr->ts.team->barrier);
ret = thr->ts.work_share->copyprivate;
gomp_work_share_end_nowait ();
}
return ret;
}
/* This routine is called when the thread that entered a SINGLE construct
with a COPYPRIVATE clause gets to the end of the construct. */
void
GOMP_single_copy_end (void *data)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
if (team != NULL)
{
thr->ts.work_share->copyprivate = data;
gomp_team_barrier_wait (&team->barrier);
}
gomp_work_share_end_nowait ();
}

File diff suppressed because it is too large Load Diff

View File

@ -1,958 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file handles the maintainence of threads in response to team
creation and termination. */
#include "libgomp.h"
#include <stdlib.h>
#include <string.h>
/* This attribute contains PTHREAD_CREATE_DETACHED. */
pthread_attr_t gomp_thread_attr;
/* This key is for the thread destructor. */
pthread_key_t gomp_thread_destructor;
/* This is the libgomp per-thread data structure. */
#if defined HAVE_TLS || defined USE_EMUTLS
__thread struct gomp_thread* gomp_tls_data = NULL;
#else
pthread_key_t gomp_tls_key;
#endif
/* This structure is used to communicate across pthread_create. */
struct gomp_thread_start_data
{
void (*fn) (void *);
void *fn_data;
struct gomp_team_state ts;
struct gomp_task *task;
struct gomp_thread_pool *thread_pool;
unsigned int place;
bool nested;
};
/* This function is a pthread_create entry point. This contains the idle
loop in which a thread waits to be called up to become part of a team. */
static void *
gomp_thread_start (void *xdata)
{
struct gomp_thread_start_data *data = xdata;
struct gomp_thread *thr;
struct gomp_thread_pool *pool;
void (*local_fn) (void *);
void *local_data;
#if defined HAVE_TLS || defined USE_EMUTLS
thr = gomp_tls_data = (struct gomp_thread*) gomp_malloc_cleared(sizeof(struct gomp_thread));
#else
struct gomp_thread local_thr;
thr = &local_thr;
pthread_setspecific (gomp_tls_key, thr);
#endif
gomp_sem_init (&thr->release, 0);
/* Extract what we need from data. */
local_fn = data->fn;
local_data = data->fn_data;
thr->thread_pool = data->thread_pool;
thr->ts = data->ts;
thr->task = data->task;
thr->place = data->place;
thr->ts.team->ordered_release[thr->ts.team_id] = &thr->release;
/* Make thread pool local. */
pool = thr->thread_pool;
if (data->nested)
{
struct gomp_team *team = thr->ts.team;
struct gomp_task *task = thr->task;
gomp_barrier_wait (&team->barrier);
local_fn (local_data);
gomp_team_barrier_wait_final (&team->barrier);
gomp_finish_task (task);
gomp_barrier_wait_last (&team->barrier);
}
else
{
pool->threads[thr->ts.team_id] = thr;
gomp_barrier_wait (&pool->threads_dock);
do
{
struct gomp_team *team = thr->ts.team;
struct gomp_task *task = thr->task;
local_fn (local_data);
gomp_team_barrier_wait_final (&team->barrier);
gomp_finish_task (task);
gomp_barrier_wait (&pool->threads_dock);
local_fn = thr->fn;
local_data = thr->data;
thr->fn = NULL;
}
while (local_fn);
}
gomp_sem_destroy (&thr->release);
thr->thread_pool = NULL;
thr->task = NULL;
#if defined HAVE_TLS || defined USE_EMUTLS
free(thr);
thr = gomp_tls_data = NULL;
#endif
return NULL;
}
/* Create a new team data structure. */
struct gomp_team *
gomp_new_team (unsigned nthreads)
{
struct gomp_team *team;
size_t size;
int i;
size = sizeof (*team) + nthreads * (sizeof (team->ordered_release[0])
+ sizeof (team->implicit_task[0]));
team = gomp_malloc (size);
team->work_share_chunk = 8;
#ifdef HAVE_SYNC_BUILTINS
team->single_count = 0;
#else
gomp_mutex_init (&team->work_share_list_free_lock);
#endif
team->work_shares_to_free = &team->work_shares[0];
gomp_init_work_share (&team->work_shares[0], false, nthreads);
team->work_shares[0].next_alloc = NULL;
team->work_share_list_free = NULL;
team->work_share_list_alloc = &team->work_shares[1];
for (i = 1; i < 7; i++)
team->work_shares[i].next_free = &team->work_shares[i + 1];
team->work_shares[i].next_free = NULL;
team->nthreads = nthreads;
gomp_barrier_init (&team->barrier, nthreads);
gomp_sem_init (&team->master_release, 0);
team->ordered_release = (void *) &team->implicit_task[nthreads];
team->ordered_release[0] = &team->master_release;
gomp_mutex_init (&team->task_lock);
team->task_queue = NULL;
team->task_count = 0;
team->task_queued_count = 0;
team->task_running_count = 0;
team->work_share_cancelled = 0;
team->team_cancelled = 0;
return team;
}
/* Free a team data structure. */
static void
free_team (struct gomp_team *team)
{
gomp_barrier_destroy (&team->barrier);
gomp_mutex_destroy (&team->task_lock);
free (team);
}
/* Allocate and initialize a thread pool. */
static struct gomp_thread_pool *gomp_new_thread_pool (void)
{
struct gomp_thread_pool *pool
= gomp_malloc (sizeof(struct gomp_thread_pool));
pool->threads = NULL;
pool->threads_size = 0;
pool->threads_used = 0;
pool->last_team = NULL;
return pool;
}
static void
gomp_free_pool_helper (void *thread_pool)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_thread_pool *pool
= (struct gomp_thread_pool *) thread_pool;
gomp_barrier_wait_last (&pool->threads_dock);
gomp_sem_destroy (&thr->release);
thr->thread_pool = NULL;
thr->task = NULL;
pthread_exit (NULL);
}
/* Free a thread pool and release its threads. */
void
gomp_free_thread (void *arg __attribute__((unused)))
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_thread_pool *pool = thr->thread_pool;
if (pool)
{
if (pool->threads_used > 0)
{
int i;
for (i = 1; i < pool->threads_used; i++)
{
struct gomp_thread *nthr = pool->threads[i];
nthr->fn = gomp_free_pool_helper;
nthr->data = pool;
}
/* This barrier undocks threads docked on pool->threads_dock. */
gomp_barrier_wait (&pool->threads_dock);
/* And this waits till all threads have called gomp_barrier_wait_last
in gomp_free_pool_helper. */
gomp_barrier_wait (&pool->threads_dock);
/* Now it is safe to destroy the barrier and free the pool. */
gomp_barrier_destroy (&pool->threads_dock);
#ifdef HAVE_SYNC_BUILTINS
__sync_fetch_and_add (&gomp_managed_threads,
1L - pool->threads_used);
#else
gomp_mutex_lock (&gomp_managed_threads_lock);
gomp_managed_threads -= pool->threads_used - 1L;
gomp_mutex_unlock (&gomp_managed_threads_lock);
#endif
}
free (pool->threads);
if (pool->last_team)
free_team (pool->last_team);
free (pool);
thr->thread_pool = NULL;
}
if (thr->task != NULL)
{
struct gomp_task *task = thr->task;
gomp_end_task ();
free (task);
}
}
/* Launch a team. */
void
gomp_team_start (void (*fn) (void *), void *data, unsigned nthreads,
unsigned flags, struct gomp_team *team)
{
struct gomp_thread_start_data *start_data;
struct gomp_thread *thr, *nthr;
struct gomp_task *task;
struct gomp_task_icv *icv;
bool nested;
struct gomp_thread_pool *pool;
unsigned i, n, old_threads_used = 0;
pthread_attr_t thread_attr, *attr;
unsigned long nthreads_var;
char bind, bind_var;
unsigned int s = 0, rest = 0, p = 0, k = 0;
unsigned int affinity_count = 0;
struct gomp_thread **affinity_thr = NULL;
thr = gomp_thread ();
nested = thr->ts.team != NULL;
if (__builtin_expect (thr->thread_pool == NULL, 0))
{
thr->thread_pool = gomp_new_thread_pool ();
thr->thread_pool->threads_busy = nthreads;
pthread_setspecific (gomp_thread_destructor, thr);
}
pool = thr->thread_pool;
task = thr->task;
icv = task ? &task->icv : &gomp_global_icv;
if (__builtin_expect (gomp_places_list != NULL, 0) && thr->place == 0)
gomp_init_affinity ();
/* Always save the previous state, even if this isn't a nested team.
In particular, we should save any work share state from an outer
orphaned work share construct. */
team->prev_ts = thr->ts;
thr->ts.team = team;
thr->ts.team_id = 0;
++thr->ts.level;
if (nthreads > 1)
++thr->ts.active_level;
thr->ts.work_share = &team->work_shares[0];
thr->ts.last_work_share = NULL;
#ifdef HAVE_SYNC_BUILTINS
thr->ts.single_count = 0;
#endif
thr->ts.static_trip = 0;
thr->task = &team->implicit_task[0];
nthreads_var = icv->nthreads_var;
if (__builtin_expect (gomp_nthreads_var_list != NULL, 0)
&& thr->ts.level < gomp_nthreads_var_list_len)
nthreads_var = gomp_nthreads_var_list[thr->ts.level];
bind_var = icv->bind_var;
if (bind_var != omp_proc_bind_false && (flags & 7) != omp_proc_bind_false)
bind_var = flags & 7;
bind = bind_var;
if (__builtin_expect (gomp_bind_var_list != NULL, 0)
&& thr->ts.level < gomp_bind_var_list_len)
bind_var = gomp_bind_var_list[thr->ts.level];
gomp_init_task (thr->task, task, icv);
team->implicit_task[0].icv.nthreads_var = nthreads_var;
team->implicit_task[0].icv.bind_var = bind_var;
if (nthreads == 1)
return;
i = 1;
if (__builtin_expect (gomp_places_list != NULL, 0))
{
/* Depending on chosen proc_bind model, set subpartition
for the master thread and initialize helper variables
P and optionally S, K and/or REST used by later place
computation for each additional thread. */
p = thr->place - 1;
switch (bind)
{
case omp_proc_bind_true:
case omp_proc_bind_close:
if (nthreads > thr->ts.place_partition_len)
{
/* T > P. S threads will be placed in each place,
and the final REM threads placed one by one
into the already occupied places. */
s = nthreads / thr->ts.place_partition_len;
rest = nthreads % thr->ts.place_partition_len;
}
else
s = 1;
k = 1;
break;
case omp_proc_bind_master:
/* Each thread will be bound to master's place. */
break;
case omp_proc_bind_spread:
if (nthreads <= thr->ts.place_partition_len)
{
/* T <= P. Each subpartition will have in between s
and s+1 places (subpartitions starting at or
after rest will have s places, earlier s+1 places),
each thread will be bound to the first place in
its subpartition (except for the master thread
that can be bound to another place in its
subpartition). */
s = thr->ts.place_partition_len / nthreads;
rest = thr->ts.place_partition_len % nthreads;
rest = (s + 1) * rest + thr->ts.place_partition_off;
if (p < rest)
{
p -= (p - thr->ts.place_partition_off) % (s + 1);
thr->ts.place_partition_len = s + 1;
}
else
{
p -= (p - rest) % s;
thr->ts.place_partition_len = s;
}
thr->ts.place_partition_off = p;
}
else
{
/* T > P. Each subpartition will have just a single
place and we'll place between s and s+1
threads into each subpartition. */
s = nthreads / thr->ts.place_partition_len;
rest = nthreads % thr->ts.place_partition_len;
thr->ts.place_partition_off = p;
thr->ts.place_partition_len = 1;
k = 1;
}
break;
}
}
else
bind = omp_proc_bind_false;
/* We only allow the reuse of idle threads for non-nested PARALLEL
regions. This appears to be implied by the semantics of
threadprivate variables, but perhaps that's reading too much into
things. Certainly it does prevent any locking problems, since
only the initial program thread will modify gomp_threads. */
if (!nested)
{
old_threads_used = pool->threads_used;
if (nthreads <= old_threads_used)
n = nthreads;
else if (old_threads_used == 0)
{
n = 0;
gomp_barrier_init (&pool->threads_dock, nthreads);
}
else
{
n = old_threads_used;
/* Increase the barrier threshold to make sure all new
threads arrive before the team is released. */
gomp_barrier_reinit (&pool->threads_dock, nthreads);
}
/* Not true yet, but soon will be. We're going to release all
threads from the dock, and those that aren't part of the
team will exit. */
pool->threads_used = nthreads;
/* If necessary, expand the size of the gomp_threads array. It is
expected that changes in the number of threads are rare, thus we
make no effort to expand gomp_threads_size geometrically. */
if (nthreads >= pool->threads_size)
{
pool->threads_size = nthreads + 1;
pool->threads
= gomp_realloc (pool->threads,
pool->threads_size
* sizeof (struct gomp_thread_data *));
}
/* Release existing idle threads. */
for (; i < n; ++i)
{
unsigned int place_partition_off = thr->ts.place_partition_off;
unsigned int place_partition_len = thr->ts.place_partition_len;
unsigned int place = 0;
if (__builtin_expect (gomp_places_list != NULL, 0))
{
switch (bind)
{
case omp_proc_bind_true:
case omp_proc_bind_close:
if (k == s)
{
++p;
if (p == (team->prev_ts.place_partition_off
+ team->prev_ts.place_partition_len))
p = team->prev_ts.place_partition_off;
k = 1;
if (i == nthreads - rest)
s = 1;
}
else
++k;
break;
case omp_proc_bind_master:
break;
case omp_proc_bind_spread:
if (k == 0)
{
/* T <= P. */
if (p < rest)
p += s + 1;
else
p += s;
if (p == (team->prev_ts.place_partition_off
+ team->prev_ts.place_partition_len))
p = team->prev_ts.place_partition_off;
place_partition_off = p;
if (p < rest)
place_partition_len = s + 1;
else
place_partition_len = s;
}
else
{
/* T > P. */
if (k == s)
{
++p;
if (p == (team->prev_ts.place_partition_off
+ team->prev_ts.place_partition_len))
p = team->prev_ts.place_partition_off;
k = 1;
if (i == nthreads - rest)
s = 1;
}
else
++k;
place_partition_off = p;
place_partition_len = 1;
}
break;
}
if (affinity_thr != NULL
|| (bind != omp_proc_bind_true
&& pool->threads[i]->place != p + 1)
|| pool->threads[i]->place <= place_partition_off
|| pool->threads[i]->place > (place_partition_off
+ place_partition_len))
{
unsigned int l;
if (affinity_thr == NULL)
{
unsigned int j;
if (team->prev_ts.place_partition_len > 64)
affinity_thr
= gomp_malloc (team->prev_ts.place_partition_len
* sizeof (struct gomp_thread *));
else
affinity_thr
= gomp_alloca (team->prev_ts.place_partition_len
* sizeof (struct gomp_thread *));
memset (affinity_thr, '\0',
team->prev_ts.place_partition_len
* sizeof (struct gomp_thread *));
for (j = i; j < old_threads_used; j++)
{
if (pool->threads[j]->place
> team->prev_ts.place_partition_off
&& (pool->threads[j]->place
<= (team->prev_ts.place_partition_off
+ team->prev_ts.place_partition_len)))
{
l = pool->threads[j]->place - 1
- team->prev_ts.place_partition_off;
pool->threads[j]->data = affinity_thr[l];
affinity_thr[l] = pool->threads[j];
}
pool->threads[j] = NULL;
}
if (nthreads > old_threads_used)
memset (&pool->threads[old_threads_used],
'\0', ((nthreads - old_threads_used)
* sizeof (struct gomp_thread *)));
n = nthreads;
affinity_count = old_threads_used - i;
}
if (affinity_count == 0)
break;
l = p;
if (affinity_thr[l - team->prev_ts.place_partition_off]
== NULL)
{
if (bind != omp_proc_bind_true)
continue;
for (l = place_partition_off;
l < place_partition_off + place_partition_len;
l++)
if (affinity_thr[l - team->prev_ts.place_partition_off]
!= NULL)
break;
if (l == place_partition_off + place_partition_len)
continue;
}
nthr = affinity_thr[l - team->prev_ts.place_partition_off];
affinity_thr[l - team->prev_ts.place_partition_off]
= (struct gomp_thread *) nthr->data;
affinity_count--;
pool->threads[i] = nthr;
}
else
nthr = pool->threads[i];
place = p + 1;
}
else
nthr = pool->threads[i];
nthr->ts.team = team;
nthr->ts.work_share = &team->work_shares[0];
nthr->ts.last_work_share = NULL;
nthr->ts.team_id = i;
nthr->ts.level = team->prev_ts.level + 1;
nthr->ts.active_level = thr->ts.active_level;
nthr->ts.place_partition_off = place_partition_off;
nthr->ts.place_partition_len = place_partition_len;
#ifdef HAVE_SYNC_BUILTINS
nthr->ts.single_count = 0;
#endif
nthr->ts.static_trip = 0;
nthr->task = &team->implicit_task[i];
nthr->place = place;
gomp_init_task (nthr->task, task, icv);
team->implicit_task[i].icv.nthreads_var = nthreads_var;
team->implicit_task[i].icv.bind_var = bind_var;
nthr->fn = fn;
nthr->data = data;
team->ordered_release[i] = &nthr->release;
}
if (__builtin_expect (affinity_thr != NULL, 0))
{
/* If AFFINITY_THR is non-NULL just because we had to
permute some threads in the pool, but we've managed
to find exactly as many old threads as we'd find
without affinity, we don't need to handle this
specially anymore. */
if (nthreads <= old_threads_used
? (affinity_count == old_threads_used - nthreads)
: (i == old_threads_used))
{
if (team->prev_ts.place_partition_len > 64)
free (affinity_thr);
affinity_thr = NULL;
affinity_count = 0;
}
else
{
i = 1;
/* We are going to compute the places/subpartitions
again from the beginning. So, we need to reinitialize
vars modified by the switch (bind) above inside
of the loop, to the state they had after the initial
switch (bind). */
switch (bind)
{
case omp_proc_bind_true:
case omp_proc_bind_close:
if (nthreads > thr->ts.place_partition_len)
/* T > P. S has been changed, so needs
to be recomputed. */
s = nthreads / thr->ts.place_partition_len;
k = 1;
p = thr->place - 1;
break;
case omp_proc_bind_master:
/* No vars have been changed. */
break;
case omp_proc_bind_spread:
p = thr->ts.place_partition_off;
if (k != 0)
{
/* T > P. */
s = nthreads / team->prev_ts.place_partition_len;
k = 1;
}
break;
}
/* Increase the barrier threshold to make sure all new
threads and all the threads we're going to let die
arrive before the team is released. */
if (affinity_count)
gomp_barrier_reinit (&pool->threads_dock,
nthreads + affinity_count);
}
}
if (i == nthreads)
goto do_release;
}
if (__builtin_expect (nthreads + affinity_count > old_threads_used, 0))
{
long diff = (long) (nthreads + affinity_count) - (long) old_threads_used;
if (old_threads_used == 0)
--diff;
#ifdef HAVE_SYNC_BUILTINS
__sync_fetch_and_add (&gomp_managed_threads, diff);
#else
gomp_mutex_lock (&gomp_managed_threads_lock);
gomp_managed_threads += diff;
gomp_mutex_unlock (&gomp_managed_threads_lock);
#endif
}
attr = &gomp_thread_attr;
if (__builtin_expect (gomp_places_list != NULL, 0))
{
size_t stacksize;
pthread_attr_init (&thread_attr);
pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED);
if (! pthread_attr_getstacksize (&gomp_thread_attr, &stacksize))
pthread_attr_setstacksize (&thread_attr, stacksize);
attr = &thread_attr;
}
start_data = gomp_alloca (sizeof (struct gomp_thread_start_data)
* (nthreads-i));
/* Launch new threads. */
for (; i < nthreads; ++i)
{
pthread_t pt;
int err;
start_data->ts.place_partition_off = thr->ts.place_partition_off;
start_data->ts.place_partition_len = thr->ts.place_partition_len;
start_data->place = 0;
if (__builtin_expect (gomp_places_list != NULL, 0))
{
switch (bind)
{
case omp_proc_bind_true:
case omp_proc_bind_close:
if (k == s)
{
++p;
if (p == (team->prev_ts.place_partition_off
+ team->prev_ts.place_partition_len))
p = team->prev_ts.place_partition_off;
k = 1;
if (i == nthreads - rest)
s = 1;
}
else
++k;
break;
case omp_proc_bind_master:
break;
case omp_proc_bind_spread:
if (k == 0)
{
/* T <= P. */
if (p < rest)
p += s + 1;
else
p += s;
if (p == (team->prev_ts.place_partition_off
+ team->prev_ts.place_partition_len))
p = team->prev_ts.place_partition_off;
start_data->ts.place_partition_off = p;
if (p < rest)
start_data->ts.place_partition_len = s + 1;
else
start_data->ts.place_partition_len = s;
}
else
{
/* T > P. */
if (k == s)
{
++p;
if (p == (team->prev_ts.place_partition_off
+ team->prev_ts.place_partition_len))
p = team->prev_ts.place_partition_off;
k = 1;
if (i == nthreads - rest)
s = 1;
}
else
++k;
start_data->ts.place_partition_off = p;
start_data->ts.place_partition_len = 1;
}
break;
}
start_data->place = p + 1;
if (affinity_thr != NULL && pool->threads[i] != NULL)
continue;
gomp_init_thread_affinity (attr, p);
}
start_data->fn = fn;
start_data->fn_data = data;
start_data->ts.team = team;
start_data->ts.work_share = &team->work_shares[0];
start_data->ts.last_work_share = NULL;
start_data->ts.team_id = i;
start_data->ts.level = team->prev_ts.level + 1;
start_data->ts.active_level = thr->ts.active_level;
#ifdef HAVE_SYNC_BUILTINS
start_data->ts.single_count = 0;
#endif
start_data->ts.static_trip = 0;
start_data->task = &team->implicit_task[i];
gomp_init_task (start_data->task, task, icv);
team->implicit_task[i].icv.nthreads_var = nthreads_var;
team->implicit_task[i].icv.bind_var = bind_var;
start_data->thread_pool = pool;
start_data->nested = nested;
err = pthread_create (&pt, attr, gomp_thread_start, start_data++);
if (err != 0)
gomp_fatal ("Thread creation failed: %s", strerror (err));
}
if (__builtin_expect (gomp_places_list != NULL, 0))
pthread_attr_destroy (&thread_attr);
do_release:
gomp_barrier_wait (nested ? &team->barrier : &pool->threads_dock);
/* Decrease the barrier threshold to match the number of threads
that should arrive back at the end of this team. The extra
threads should be exiting. Note that we arrange for this test
to never be true for nested teams. If AFFINITY_COUNT is non-zero,
the barrier as well as gomp_managed_threads was temporarily
set to NTHREADS + AFFINITY_COUNT. For NTHREADS < OLD_THREADS_COUNT,
AFFINITY_COUNT if non-zero will be always at least
OLD_THREADS_COUNT - NTHREADS. */
if (__builtin_expect (nthreads < old_threads_used, 0)
|| __builtin_expect (affinity_count, 0))
{
long diff = (long) nthreads - (long) old_threads_used;
if (affinity_count)
diff = -affinity_count;
gomp_barrier_reinit (&pool->threads_dock, nthreads);
#ifdef HAVE_SYNC_BUILTINS
__sync_fetch_and_add (&gomp_managed_threads, diff);
#else
gomp_mutex_lock (&gomp_managed_threads_lock);
gomp_managed_threads += diff;
gomp_mutex_unlock (&gomp_managed_threads_lock);
#endif
}
if (__builtin_expect (affinity_thr != NULL, 0)
&& team->prev_ts.place_partition_len > 64)
free (affinity_thr);
}
/* Terminate the current team. This is only to be called by the master
thread. We assume that we must wait for the other threads. */
void
gomp_team_end (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
/* This barrier handles all pending explicit threads.
As #pragma omp cancel parallel might get awaited count in
team->barrier in a inconsistent state, we need to use a different
counter here. */
gomp_team_barrier_wait_final (&team->barrier);
if (__builtin_expect (team->team_cancelled, 0))
{
struct gomp_work_share *ws = team->work_shares_to_free;
do
{
struct gomp_work_share *next_ws = gomp_ptrlock_get (&ws->next_ws);
if (next_ws == NULL)
gomp_ptrlock_set (&ws->next_ws, ws);
gomp_fini_work_share (ws);
ws = next_ws;
}
while (ws != NULL);
}
else
gomp_fini_work_share (thr->ts.work_share);
gomp_end_task ();
thr->ts = team->prev_ts;
if (__builtin_expect (thr->ts.team != NULL, 0))
{
#ifdef HAVE_SYNC_BUILTINS
__sync_fetch_and_add (&gomp_managed_threads, 1L - team->nthreads);
#else
gomp_mutex_lock (&gomp_managed_threads_lock);
gomp_managed_threads -= team->nthreads - 1L;
gomp_mutex_unlock (&gomp_managed_threads_lock);
#endif
/* This barrier has gomp_barrier_wait_last counterparts
and ensures the team can be safely destroyed. */
gomp_barrier_wait (&team->barrier);
}
if (__builtin_expect (team->work_shares[0].next_alloc != NULL, 0))
{
struct gomp_work_share *ws = team->work_shares[0].next_alloc;
do
{
struct gomp_work_share *next_ws = ws->next_alloc;
free (ws);
ws = next_ws;
}
while (ws != NULL);
}
gomp_sem_destroy (&team->master_release);
#ifndef HAVE_SYNC_BUILTINS
gomp_mutex_destroy (&team->work_share_list_free_lock);
#endif
if (__builtin_expect (thr->ts.team != NULL, 0)
|| __builtin_expect (team->nthreads == 1, 0))
free_team (team);
else
{
struct gomp_thread_pool *pool = thr->thread_pool;
if (pool->last_team)
free_team (pool->last_team);
pool->last_team = team;
}
}
/* Constructors for this file. */
static void __attribute__((constructor))
initialize_team (void)
{
#if !defined HAVE_TLS && !defined USE_EMUTLS
static struct gomp_thread initial_thread_tls_data;
pthread_key_create (&gomp_tls_key, NULL);
pthread_setspecific (gomp_tls_key, &initial_thread_tls_data);
#else
gomp_tls_data = (struct gomp_thread*) gomp_malloc_cleared(sizeof(struct gomp_thread));
#endif
if (pthread_key_create (&gomp_thread_destructor, gomp_free_thread) != 0)
gomp_fatal ("could not create thread pool destructor.");
}
static void __attribute__((destructor))
team_destructor (void)
{
#if defined HAVE_TLS || defined USE_EMUTLS
free(gomp_tls_data);
#endif
/* Without this dlclose on libgomp could lead to subsequent
crashes. */
pthread_key_delete (gomp_thread_destructor);
}
struct gomp_task_icv *
gomp_new_icv (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_task *task = gomp_malloc (sizeof (struct gomp_task));
gomp_init_task (task, NULL, &gomp_global_icv);
thr->task = task;
pthread_setspecific (gomp_thread_destructor, thr);
return &task->icv;
}

View File

@ -1,64 +0,0 @@
/* Copyright (C) 2015 RWTH Aachen University, Germany.
Contributed by Stefan Lankes <slankes@eonerc.rwth-aachen.de>.
Libgomp 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.
Libgomp 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/>. */
/*
* This file contains system specific timer routines. It is expected that
* a system may well want to write special versions of each of these.
*/
#include "libgomp.h"
#include <unistd.h>
extern unsigned int get_cpufreq(void);
static unsigned long long start_tsc;
inline static unsigned long long rdtsc(void)
{
unsigned long lo, hi;
asm volatile ("rdtsc" : "=a"(lo), "=d"(hi) :: "memory");
return ((unsigned long long) hi << 32ULL | (unsigned long long) lo);
}
__attribute__((constructor)) static void timer_init()
{
start_tsc = rdtsc();
}
double
omp_get_wtime (void)
{
double ret;
ret = (double) (rdtsc() - start_tsc) / ((double) get_cpufreq() * 1000000.0);
//printf("CPU frequency: %d MHz\n", get_cpufreq());
return ret;
}
double
omp_get_wtick (void)
{
return 1.0 / ((double) get_cpufreq() * 1000000.0);
}
ialias (omp_get_wtime)
ialias (omp_get_wtick)

View File

@ -1,297 +0,0 @@
/* Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp 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.
Libgomp 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/>. */
/* This file contains routines to manage the work-share queue for a team
of threads. */
#include "libgomp.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
/* Allocate a new work share structure, preferably from current team's
free gomp_work_share cache. */
static struct gomp_work_share *
alloc_work_share (struct gomp_team *team)
{
struct gomp_work_share *ws;
unsigned int i;
/* This is called in a critical section. */
if (team->work_share_list_alloc != NULL)
{
ws = team->work_share_list_alloc;
team->work_share_list_alloc = ws->next_free;
return ws;
}
#ifdef HAVE_SYNC_BUILTINS
ws = team->work_share_list_free;
/* We need atomic read from work_share_list_free,
as free_work_share can be called concurrently. */
__asm ("" : "+r" (ws));
if (ws && ws->next_free)
{
struct gomp_work_share *next = ws->next_free;
ws->next_free = NULL;
team->work_share_list_alloc = next->next_free;
return next;
}
#else
gomp_mutex_lock (&team->work_share_list_free_lock);
ws = team->work_share_list_free;
if (ws)
{
team->work_share_list_alloc = ws->next_free;
team->work_share_list_free = NULL;
gomp_mutex_unlock (&team->work_share_list_free_lock);
return ws;
}
gomp_mutex_unlock (&team->work_share_list_free_lock);
#endif
team->work_share_chunk *= 2;
ws = gomp_malloc (team->work_share_chunk * sizeof (struct gomp_work_share));
ws->next_alloc = team->work_shares[0].next_alloc;
team->work_shares[0].next_alloc = ws;
team->work_share_list_alloc = &ws[1];
for (i = 1; i < team->work_share_chunk - 1; i++)
ws[i].next_free = &ws[i + 1];
ws[i].next_free = NULL;
return ws;
}
/* Initialize an already allocated struct gomp_work_share.
This shouldn't touch the next_alloc field. */
void
gomp_init_work_share (struct gomp_work_share *ws, bool ordered,
unsigned nthreads)
{
gomp_mutex_init (&ws->lock);
if (__builtin_expect (ordered, 0))
{
#define INLINE_ORDERED_TEAM_IDS_CNT \
((sizeof (struct gomp_work_share) \
- offsetof (struct gomp_work_share, inline_ordered_team_ids)) \
/ sizeof (((struct gomp_work_share *) 0)->inline_ordered_team_ids[0]))
if (nthreads > INLINE_ORDERED_TEAM_IDS_CNT)
ws->ordered_team_ids
= gomp_malloc (nthreads * sizeof (*ws->ordered_team_ids));
else
ws->ordered_team_ids = ws->inline_ordered_team_ids;
memset (ws->ordered_team_ids, '\0',
nthreads * sizeof (*ws->ordered_team_ids));
ws->ordered_num_used = 0;
ws->ordered_owner = -1;
ws->ordered_cur = 0;
}
else
ws->ordered_team_ids = NULL;
gomp_ptrlock_init (&ws->next_ws, NULL);
ws->threads_completed = 0;
}
/* Do any needed destruction of gomp_work_share fields before it
is put back into free gomp_work_share cache or freed. */
void
gomp_fini_work_share (struct gomp_work_share *ws)
{
gomp_mutex_destroy (&ws->lock);
if (ws->ordered_team_ids != ws->inline_ordered_team_ids)
free (ws->ordered_team_ids);
gomp_ptrlock_destroy (&ws->next_ws);
}
/* Free a work share struct, if not orphaned, put it into current
team's free gomp_work_share cache. */
static inline void
free_work_share (struct gomp_team *team, struct gomp_work_share *ws)
{
gomp_fini_work_share (ws);
if (__builtin_expect (team == NULL, 0))
free (ws);
else
{
struct gomp_work_share *next_ws;
#ifdef HAVE_SYNC_BUILTINS
do
{
next_ws = team->work_share_list_free;
ws->next_free = next_ws;
}
while (!__sync_bool_compare_and_swap (&team->work_share_list_free,
next_ws, ws));
#else
gomp_mutex_lock (&team->work_share_list_free_lock);
next_ws = team->work_share_list_free;
ws->next_free = next_ws;
team->work_share_list_free = ws;
gomp_mutex_unlock (&team->work_share_list_free_lock);
#endif
}
}
/* The current thread is ready to begin the next work sharing construct.
In all cases, thr->ts.work_share is updated to point to the new
structure. In all cases the work_share lock is locked. Return true
if this was the first thread to reach this point. */
bool
gomp_work_share_start (bool ordered)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
struct gomp_work_share *ws;
/* Work sharing constructs can be orphaned. */
if (team == NULL)
{
ws = gomp_malloc (sizeof (*ws));
gomp_init_work_share (ws, ordered, 1);
thr->ts.work_share = ws;
return ws;
}
ws = thr->ts.work_share;
thr->ts.last_work_share = ws;
ws = gomp_ptrlock_get (&ws->next_ws);
if (ws == NULL)
{
/* This thread encountered a new ws first. */
struct gomp_work_share *ws = alloc_work_share (team);
gomp_init_work_share (ws, ordered, team->nthreads);
thr->ts.work_share = ws;
return true;
}
else
{
thr->ts.work_share = ws;
return false;
}
}
/* The current thread is done with its current work sharing construct.
This version does imply a barrier at the end of the work-share. */
void
gomp_work_share_end (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
gomp_barrier_state_t bstate;
/* Work sharing constructs can be orphaned. */
if (team == NULL)
{
free_work_share (NULL, thr->ts.work_share);
thr->ts.work_share = NULL;
return;
}
bstate = gomp_barrier_wait_start (&team->barrier);
if (gomp_barrier_last_thread (bstate))
{
if (__builtin_expect (thr->ts.last_work_share != NULL, 1))
{
team->work_shares_to_free = thr->ts.work_share;
free_work_share (team, thr->ts.last_work_share);
}
}
gomp_team_barrier_wait_end (&team->barrier, bstate);
thr->ts.last_work_share = NULL;
}
/* The current thread is done with its current work sharing construct.
This version implies a cancellable barrier at the end of the work-share. */
bool
gomp_work_share_end_cancel (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
gomp_barrier_state_t bstate;
/* Cancellable work sharing constructs cannot be orphaned. */
bstate = gomp_barrier_wait_cancel_start (&team->barrier);
if (gomp_barrier_last_thread (bstate))
{
if (__builtin_expect (thr->ts.last_work_share != NULL, 1))
{
team->work_shares_to_free = thr->ts.work_share;
free_work_share (team, thr->ts.last_work_share);
}
}
thr->ts.last_work_share = NULL;
return gomp_team_barrier_wait_cancel_end (&team->barrier, bstate);
}
/* The current thread is done with its current work sharing construct.
This version does NOT imply a barrier at the end of the work-share. */
void
gomp_work_share_end_nowait (void)
{
struct gomp_thread *thr = gomp_thread ();
struct gomp_team *team = thr->ts.team;
struct gomp_work_share *ws = thr->ts.work_share;
unsigned completed;
/* Work sharing constructs can be orphaned. */
if (team == NULL)
{
free_work_share (NULL, ws);
thr->ts.work_share = NULL;
return;
}
if (__builtin_expect (thr->ts.last_work_share == NULL, 0))
return;
#ifdef HAVE_SYNC_BUILTINS
completed = __sync_add_and_fetch (&ws->threads_completed, 1);
#else
gomp_mutex_lock (&ws->lock);
completed = ++ws->threads_completed;
gomp_mutex_unlock (&ws->lock);
#endif
if (completed == team->nthreads)
{
team->work_shares_to_free = thr->ts.work_share;
free_work_share (team, thr->ts.last_work_share);
}
thr->ts.last_work_share = NULL;
}

@ -1 +0,0 @@
Subproject commit 4a4ecc1a2ebe29ca58213e1902831b1e547e8f16