From faef78bdbad5be8d232abf7f9c21b55431cec6d9 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 12 Aug 2015 21:00:11 +0200 Subject: [PATCH] add libgomp - derived from the orginal libgomp - remove ACC support - remove the support of target devices --- hermit/usr/libgomp/Makefile | 43 + hermit/usr/libgomp/affinity.c | 116 +++ hermit/usr/libgomp/alloc.c | 59 ++ hermit/usr/libgomp/bar.c | 299 +++++++ hermit/usr/libgomp/bar.h | 158 ++++ hermit/usr/libgomp/barrier.c | 54 ++ hermit/usr/libgomp/config.h | 154 ++++ hermit/usr/libgomp/critical.c | 149 ++++ hermit/usr/libgomp/env.c | 1479 +++++++++++++++++++++++++++++++ hermit/usr/libgomp/error.c | 91 ++ hermit/usr/libgomp/fortran.c | 495 +++++++++++ hermit/usr/libgomp/hashtab.h | 442 +++++++++ hermit/usr/libgomp/iter.c | 338 +++++++ hermit/usr/libgomp/iter_ull.c | 345 +++++++ hermit/usr/libgomp/libgomp.a | Bin 0 -> 169844 bytes hermit/usr/libgomp/libgomp.h | 887 ++++++++++++++++++ hermit/usr/libgomp/libgomp.spec | 3 + hermit/usr/libgomp/libgomp_f.h | 96 ++ hermit/usr/libgomp/libgomp_g.h | 234 +++++ hermit/usr/libgomp/lock.c | 305 +++++++ hermit/usr/libgomp/loop.c | 675 ++++++++++++++ hermit/usr/libgomp/loop_ull.c | 572 ++++++++++++ hermit/usr/libgomp/mutex.c | 1 + hermit/usr/libgomp/mutex.h | 58 ++ hermit/usr/libgomp/omp-lock.h | 23 + hermit/usr/libgomp/omp.h | 128 +++ hermit/usr/libgomp/omp_lib.h | 98 ++ hermit/usr/libgomp/omp_lib.h.in | 98 ++ hermit/usr/libgomp/ordered.c | 252 ++++++ hermit/usr/libgomp/parallel.c | 302 +++++++ hermit/usr/libgomp/proc.c | 109 +++ hermit/usr/libgomp/ptrlock.c | 1 + hermit/usr/libgomp/ptrlock.h | 66 ++ hermit/usr/libgomp/sections.c | 182 ++++ hermit/usr/libgomp/sem.c | 124 +++ hermit/usr/libgomp/sem.h | 88 ++ hermit/usr/libgomp/single.c | 105 +++ hermit/usr/libgomp/task.c | 1216 +++++++++++++++++++++++++ hermit/usr/libgomp/team.c | 948 ++++++++++++++++++++ hermit/usr/libgomp/work.c | 297 +++++++ 40 files changed, 11090 insertions(+) create mode 100644 hermit/usr/libgomp/Makefile create mode 100644 hermit/usr/libgomp/affinity.c create mode 100644 hermit/usr/libgomp/alloc.c create mode 100644 hermit/usr/libgomp/bar.c create mode 100644 hermit/usr/libgomp/bar.h create mode 100644 hermit/usr/libgomp/barrier.c create mode 100644 hermit/usr/libgomp/config.h create mode 100644 hermit/usr/libgomp/critical.c create mode 100644 hermit/usr/libgomp/env.c create mode 100644 hermit/usr/libgomp/error.c create mode 100644 hermit/usr/libgomp/fortran.c create mode 100644 hermit/usr/libgomp/hashtab.h create mode 100644 hermit/usr/libgomp/iter.c create mode 100644 hermit/usr/libgomp/iter_ull.c create mode 100644 hermit/usr/libgomp/libgomp.a create mode 100644 hermit/usr/libgomp/libgomp.h create mode 100644 hermit/usr/libgomp/libgomp.spec create mode 100644 hermit/usr/libgomp/libgomp_f.h create mode 100644 hermit/usr/libgomp/libgomp_g.h create mode 100644 hermit/usr/libgomp/lock.c create mode 100644 hermit/usr/libgomp/loop.c create mode 100644 hermit/usr/libgomp/loop_ull.c create mode 100644 hermit/usr/libgomp/mutex.c create mode 100644 hermit/usr/libgomp/mutex.h create mode 100644 hermit/usr/libgomp/omp-lock.h create mode 100644 hermit/usr/libgomp/omp.h create mode 100644 hermit/usr/libgomp/omp_lib.h create mode 100644 hermit/usr/libgomp/omp_lib.h.in create mode 100644 hermit/usr/libgomp/ordered.c create mode 100644 hermit/usr/libgomp/parallel.c create mode 100644 hermit/usr/libgomp/proc.c create mode 100644 hermit/usr/libgomp/ptrlock.c create mode 100644 hermit/usr/libgomp/ptrlock.h create mode 100644 hermit/usr/libgomp/sections.c create mode 100644 hermit/usr/libgomp/sem.c create mode 100644 hermit/usr/libgomp/sem.h create mode 100644 hermit/usr/libgomp/single.c create mode 100644 hermit/usr/libgomp/task.c create mode 100644 hermit/usr/libgomp/team.c create mode 100644 hermit/usr/libgomp/work.c diff --git a/hermit/usr/libgomp/Makefile b/hermit/usr/libgomp/Makefile new file mode 100644 index 000000000..914a4c879 --- /dev/null +++ b/hermit/usr/libgomp/Makefile @@ -0,0 +1,43 @@ +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 + +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 diff --git a/hermit/usr/libgomp/affinity.c b/hermit/usr/libgomp/affinity.c new file mode 100644 index 000000000..6840d3a72 --- /dev/null +++ b/hermit/usr/libgomp/affinity.c @@ -0,0 +1,116 @@ +/* Copyright (C) 2006-2015 Free Software Foundation, Inc. + Contributed by Jakub Jelinek . + + 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 + . */ + +/* 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; +} diff --git a/hermit/usr/libgomp/alloc.c b/hermit/usr/libgomp/alloc.c new file mode 100644 index 000000000..f738a663d --- /dev/null +++ b/hermit/usr/libgomp/alloc.c @@ -0,0 +1,59 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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 + + +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; +} diff --git a/hermit/usr/libgomp/bar.c b/hermit/usr/libgomp/bar.c new file mode 100644 index 000000000..de66d6c14 --- /dev/null +++ b/hermit/usr/libgomp/bar.c @@ -0,0 +1,299 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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); +} diff --git a/hermit/usr/libgomp/bar.h b/hermit/usr/libgomp/bar.h new file mode 100644 index 000000000..3b29c3199 --- /dev/null +++ b/hermit/usr/libgomp/bar.h @@ -0,0 +1,158 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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 + +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 */ diff --git a/hermit/usr/libgomp/barrier.c b/hermit/usr/libgomp/barrier.c new file mode 100644 index 000000000..c17660ca3 --- /dev/null +++ b/hermit/usr/libgomp/barrier.c @@ -0,0 +1,54 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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); +} diff --git a/hermit/usr/libgomp/config.h b/hermit/usr/libgomp/config.h new file mode 100644 index 000000000..340145417 --- /dev/null +++ b/hermit/usr/libgomp/config.h @@ -0,0 +1,154 @@ +/* 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 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 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 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 header file. */ +#define HAVE_PTHREAD_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SEMAPHORE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the 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 header file. */ +/* #undef HAVE_SYS_LOADAVG_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if the target supports thread-local storage. */ +#undef HAVE_TLS + +/* Define to 1 if you have the 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 and . */ +#define STRING_WITH_STRINGS 1 + +/* Define to 1 if you can safely include both and . */ +#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" diff --git a/hermit/usr/libgomp/critical.c b/hermit/usr/libgomp/critical.c new file mode 100644 index 000000000..12b23d566 --- /dev/null +++ b/hermit/usr/libgomp/critical.c @@ -0,0 +1,149 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* This file handles the CRITICAL construct. */ + +#include "libgomp.h" +#include + + +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 diff --git a/hermit/usr/libgomp/env.c b/hermit/usr/libgomp/env.c new file mode 100644 index 000000000..4d6a6bc5e --- /dev/null +++ b/hermit/usr/libgomp/env.c @@ -0,0 +1,1479 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* This file defines the OpenMP internal control variables, and arranges + for them to be initialized from environment variables at startup. */ + +#include "libgomp.h" +#include "libgomp_f.h" +//#include "oacc-int.h" +#include +#include +#include +#ifdef HAVE_INTTYPES_H +# include /* For PRIu64. */ +#endif +#ifdef STRING_WITH_STRINGS +# include +# include +#else +# ifdef HAVE_STRING_H +# include +# else +# ifdef HAVE_STRINGS_H +# include +# endif +# endif +#endif +#include +#include + +#ifndef HAVE_STRTOULL +# define strtoull(ptr, eptr, base) strtoul (ptr, eptr, base) +#endif + +struct gomp_task_icv gomp_global_icv = { + .nthreads_var = 1, + .thread_limit_var = UINT_MAX, + .run_sched_var = GFS_DYNAMIC, + .run_sched_modifier = 1, + .default_device_var = 0, + .dyn_var = false, + .nest_var = false, + .bind_var = omp_proc_bind_false, + .target_data = NULL +}; + +unsigned long gomp_max_active_levels_var = INT_MAX; +bool gomp_cancel_var = false; +#ifndef HAVE_SYNC_BUILTINS +gomp_mutex_t gomp_managed_threads_lock; +#endif +unsigned long gomp_available_cpus = 1, gomp_managed_threads = 1; +unsigned long long gomp_spin_count_var, gomp_throttled_spin_count_var; +unsigned long *gomp_nthreads_var_list, gomp_nthreads_var_list_len; +char *gomp_bind_var_list; +unsigned long gomp_bind_var_list_len; +void **gomp_places_list; +unsigned long gomp_places_list_len; +int gomp_debug_var; +char *goacc_device_type; +int goacc_device_num; + +/* Parse the OMP_SCHEDULE environment variable. */ + +static void +parse_schedule (void) +{ + char *env, *end; + unsigned long value; + + env = getenv ("OMP_SCHEDULE"); + if (env == NULL) + return; + + while (isspace ((unsigned char) *env)) + ++env; + if (strncasecmp (env, "static", 6) == 0) + { + gomp_global_icv.run_sched_var = GFS_STATIC; + env += 6; + } + else if (strncasecmp (env, "dynamic", 7) == 0) + { + gomp_global_icv.run_sched_var = GFS_DYNAMIC; + env += 7; + } + else if (strncasecmp (env, "guided", 6) == 0) + { + gomp_global_icv.run_sched_var = GFS_GUIDED; + env += 6; + } + else if (strncasecmp (env, "auto", 4) == 0) + { + gomp_global_icv.run_sched_var = GFS_AUTO; + env += 4; + } + else + goto unknown; + + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '\0') + { + gomp_global_icv.run_sched_modifier + = gomp_global_icv.run_sched_var != GFS_STATIC; + return; + } + if (*env++ != ',') + goto unknown; + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '\0') + goto invalid; + + errno = 0; + value = strtoul (env, &end, 10); + if (errno) + goto invalid; + + while (isspace ((unsigned char) *end)) + ++end; + if (*end != '\0') + goto invalid; + + if ((int)value != value) + goto invalid; + + if (value == 0 && gomp_global_icv.run_sched_var != GFS_STATIC) + value = 1; + gomp_global_icv.run_sched_modifier = value; + return; + + unknown: + gomp_error ("Unknown value for environment variable OMP_SCHEDULE"); + return; + + invalid: + gomp_error ("Invalid value for chunk size in " + "environment variable OMP_SCHEDULE"); + return; +} + +/* Parse an unsigned long environment variable. Return true if one was + present and it was successfully parsed. */ + +static bool +parse_unsigned_long (const char *name, unsigned long *pvalue, bool allow_zero) +{ + char *env, *end; + unsigned long value; + + env = getenv (name); + if (env == NULL) + return false; + + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '\0') + goto invalid; + + errno = 0; + value = strtoul (env, &end, 10); + if (errno || (long) value <= 0 - allow_zero) + goto invalid; + + while (isspace ((unsigned char) *end)) + ++end; + if (*end != '\0') + goto invalid; + + *pvalue = value; + return true; + + invalid: + gomp_error ("Invalid value for environment variable %s", name); + return false; +} + +/* Parse a positive int environment variable. Return true if one was + present and it was successfully parsed. */ + +static bool +parse_int (const char *name, int *pvalue, bool allow_zero) +{ + unsigned long value; + if (!parse_unsigned_long (name, &value, allow_zero)) + return false; + if (value > INT_MAX) + { + gomp_error ("Invalid value for environment variable %s", name); + return false; + } + *pvalue = (int) value; + return true; +} + +/* Parse an unsigned long list environment variable. Return true if one was + present and it was successfully parsed. */ + +static bool +parse_unsigned_long_list (const char *name, unsigned long *p1stvalue, + unsigned long **pvalues, + unsigned long *pnvalues) +{ + char *env, *end; + unsigned long value, *values = NULL; + + env = getenv (name); + if (env == NULL) + return false; + + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '\0') + goto invalid; + + errno = 0; + value = strtoul (env, &end, 10); + if (errno || (long) value <= 0) + goto invalid; + + while (isspace ((unsigned char) *end)) + ++end; + if (*end != '\0') + { + if (*end == ',') + { + unsigned long nvalues = 0, nalloced = 0; + + do + { + env = end + 1; + if (nvalues == nalloced) + { + unsigned long *n; + nalloced = nalloced ? nalloced * 2 : 16; + n = realloc (values, nalloced * sizeof (unsigned long)); + if (n == NULL) + { + free (values); + gomp_error ("Out of memory while trying to parse" + " environment variable %s", name); + return false; + } + values = n; + if (nvalues == 0) + values[nvalues++] = value; + } + + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '\0') + goto invalid; + + errno = 0; + value = strtoul (env, &end, 10); + if (errno || (long) value <= 0) + goto invalid; + + values[nvalues++] = value; + while (isspace ((unsigned char) *end)) + ++end; + if (*end == '\0') + break; + if (*end != ',') + goto invalid; + } + while (1); + *p1stvalue = values[0]; + *pvalues = values; + *pnvalues = nvalues; + return true; + } + goto invalid; + } + + *p1stvalue = value; + return true; + + invalid: + free (values); + gomp_error ("Invalid value for environment variable %s", name); + return false; +} + +/* Parse environment variable set to a boolean or list of omp_proc_bind_t + enum values. Return true if one was present and it was successfully + parsed. */ + +static bool +parse_bind_var (const char *name, char *p1stvalue, + char **pvalues, unsigned long *pnvalues) +{ + char *env; + char value = omp_proc_bind_false, *values = NULL; + int i; + static struct proc_bind_kinds + { + const char name[7]; + const char len; + omp_proc_bind_t kind; + } kinds[] = + { + { "false", 5, omp_proc_bind_false }, + { "true", 4, omp_proc_bind_true }, + { "master", 6, omp_proc_bind_master }, + { "close", 5, omp_proc_bind_close }, + { "spread", 6, omp_proc_bind_spread } + }; + + env = getenv (name); + if (env == NULL) + return false; + + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '\0') + goto invalid; + + for (i = 0; i < 5; i++) + if (strncasecmp (env, kinds[i].name, kinds[i].len) == 0) + { + value = kinds[i].kind; + env += kinds[i].len; + break; + } + if (i == 5) + goto invalid; + + while (isspace ((unsigned char) *env)) + ++env; + if (*env != '\0') + { + if (*env == ',') + { + unsigned long nvalues = 0, nalloced = 0; + + if (value == omp_proc_bind_false + || value == omp_proc_bind_true) + goto invalid; + + do + { + env++; + if (nvalues == nalloced) + { + char *n; + nalloced = nalloced ? nalloced * 2 : 16; + n = realloc (values, nalloced); + if (n == NULL) + { + free (values); + gomp_error ("Out of memory while trying to parse" + " environment variable %s", name); + return false; + } + values = n; + if (nvalues == 0) + values[nvalues++] = value; + } + + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '\0') + goto invalid; + + for (i = 2; i < 5; i++) + if (strncasecmp (env, kinds[i].name, kinds[i].len) == 0) + { + value = kinds[i].kind; + env += kinds[i].len; + break; + } + if (i == 5) + goto invalid; + + values[nvalues++] = value; + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '\0') + break; + if (*env != ',') + goto invalid; + } + while (1); + *p1stvalue = values[0]; + *pvalues = values; + *pnvalues = nvalues; + return true; + } + goto invalid; + } + + *p1stvalue = value; + return true; + + invalid: + free (values); + gomp_error ("Invalid value for environment variable %s", name); + return false; +} + +static bool +parse_one_place (char **envp, bool *negatep, unsigned long *lenp, + long *stridep) +{ + char *env = *envp, *start; + void *p = gomp_places_list ? gomp_places_list[gomp_places_list_len] : NULL; + unsigned long len = 1; + long stride = 1; + int pass; + bool any_negate = false; + *negatep = false; + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '!') + { + *negatep = true; + ++env; + while (isspace ((unsigned char) *env)) + ++env; + } + if (*env != '{') + return false; + ++env; + while (isspace ((unsigned char) *env)) + ++env; + start = env; + for (pass = 0; pass < (any_negate ? 2 : 1); pass++) + { + env = start; + do + { + unsigned long this_num, this_len = 1; + long this_stride = 1; + bool this_negate = (*env == '!'); + if (this_negate) + { + if (gomp_places_list) + any_negate = true; + ++env; + while (isspace ((unsigned char) *env)) + ++env; + } + + errno = 0; + this_num = strtoul (env, &env, 10); + if (errno) + return false; + while (isspace ((unsigned char) *env)) + ++env; + if (*env == ':') + { + ++env; + while (isspace ((unsigned char) *env)) + ++env; + errno = 0; + this_len = strtoul (env, &env, 10); + if (errno || this_len == 0) + return false; + while (isspace ((unsigned char) *env)) + ++env; + if (*env == ':') + { + ++env; + while (isspace ((unsigned char) *env)) + ++env; + errno = 0; + this_stride = strtol (env, &env, 10); + if (errno) + return false; + while (isspace ((unsigned char) *env)) + ++env; + } + } + if (this_negate && this_len != 1) + return false; + if (gomp_places_list && pass == this_negate) + { + if (this_negate) + { + if (!gomp_affinity_remove_cpu (p, this_num)) + return false; + } + else if (!gomp_affinity_add_cpus (p, this_num, this_len, + this_stride, false)) + return false; + } + if (*env == '}') + break; + if (*env != ',') + return false; + ++env; + } + while (1); + } + + ++env; + while (isspace ((unsigned char) *env)) + ++env; + if (*env == ':') + { + ++env; + while (isspace ((unsigned char) *env)) + ++env; + errno = 0; + len = strtoul (env, &env, 10); + if (errno || len == 0 || len >= 65536) + return false; + while (isspace ((unsigned char) *env)) + ++env; + if (*env == ':') + { + ++env; + while (isspace ((unsigned char) *env)) + ++env; + errno = 0; + stride = strtol (env, &env, 10); + if (errno) + return false; + while (isspace ((unsigned char) *env)) + ++env; + } + } + if (*negatep && len != 1) + return false; + *envp = env; + *lenp = len; + *stridep = stride; + return true; +} + +static bool +parse_places_var (const char *name, bool ignore) +{ + char *env = getenv (name), *end; + bool any_negate = false; + int level = 0; + unsigned long count = 0; + if (env == NULL) + return false; + + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '\0') + goto invalid; + + if (strncasecmp (env, "threads", 7) == 0) + { + env += 7; + level = 1; + } + else if (strncasecmp (env, "cores", 5) == 0) + { + env += 5; + level = 2; + } + else if (strncasecmp (env, "sockets", 7) == 0) + { + env += 7; + level = 3; + } + if (level) + { + count = ULONG_MAX; + while (isspace ((unsigned char) *env)) + ++env; + if (*env != '\0') + { + if (*env++ != '(') + goto invalid; + while (isspace ((unsigned char) *env)) + ++env; + + errno = 0; + count = strtoul (env, &end, 10); + if (errno) + goto invalid; + env = end; + while (isspace ((unsigned char) *env)) + ++env; + if (*env != ')') + goto invalid; + ++env; + while (isspace ((unsigned char) *env)) + ++env; + if (*env != '\0') + goto invalid; + } + + if (ignore) + return false; + + return gomp_affinity_init_level (level, count, false); + } + + count = 0; + end = env; + do + { + bool negate; + unsigned long len; + long stride; + if (!parse_one_place (&end, &negate, &len, &stride)) + goto invalid; + if (negate) + { + if (!any_negate) + count++; + any_negate = true; + } + else + count += len; + if (count > 65536) + goto invalid; + if (*end == '\0') + break; + if (*end != ',') + goto invalid; + end++; + } + while (1); + + if (ignore) + return false; + + gomp_places_list_len = 0; + gomp_places_list = gomp_affinity_alloc (count, false); + if (gomp_places_list == NULL) + return false; + + do + { + bool negate; + unsigned long len; + long stride; + gomp_affinity_init_place (gomp_places_list[gomp_places_list_len]); + if (!parse_one_place (&env, &negate, &len, &stride)) + goto invalid; + if (negate) + { + void *p; + for (count = 0; count < gomp_places_list_len; count++) + if (gomp_affinity_same_place + (gomp_places_list[count], + gomp_places_list[gomp_places_list_len])) + break; + if (count == gomp_places_list_len) + { + gomp_error ("Trying to remove a non-existing place from list " + "of places"); + goto invalid; + } + p = gomp_places_list[count]; + memmove (&gomp_places_list[count], + &gomp_places_list[count + 1], + (gomp_places_list_len - count - 1) * sizeof (void *)); + --gomp_places_list_len; + gomp_places_list[gomp_places_list_len] = p; + } + else if (len == 1) + ++gomp_places_list_len; + else + { + for (count = 0; count < len - 1; count++) + if (!gomp_affinity_copy_place + (gomp_places_list[gomp_places_list_len + count + 1], + gomp_places_list[gomp_places_list_len + count], + stride)) + goto invalid; + gomp_places_list_len += len; + } + if (*env == '\0') + break; + env++; + } + while (1); + + if (gomp_places_list_len == 0) + { + gomp_error ("All places have been removed"); + goto invalid; + } + if (!gomp_affinity_finalize_place_list (false)) + goto invalid; + return true; + + invalid: + free (gomp_places_list); + gomp_places_list = NULL; + gomp_places_list_len = 0; + gomp_error ("Invalid value for environment variable %s", name); + return false; +} + +/* Parse the OMP_STACKSIZE environment varible. Return true if one was + present and it was successfully parsed. */ + +static bool +parse_stacksize (const char *name, unsigned long *pvalue) +{ + char *env, *end; + unsigned long value, shift = 10; + + env = getenv (name); + if (env == NULL) + return false; + + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '\0') + goto invalid; + + errno = 0; + value = strtoul (env, &end, 10); + if (errno) + goto invalid; + + while (isspace ((unsigned char) *end)) + ++end; + if (*end != '\0') + { + switch (tolower ((unsigned char) *end)) + { + case 'b': + shift = 0; + break; + case 'k': + break; + case 'm': + shift = 20; + break; + case 'g': + shift = 30; + break; + default: + goto invalid; + } + ++end; + while (isspace ((unsigned char) *end)) + ++end; + if (*end != '\0') + goto invalid; + } + + if (((value << shift) >> shift) != value) + goto invalid; + + *pvalue = value << shift; + return true; + + invalid: + gomp_error ("Invalid value for environment variable %s", name); + return false; +} + +/* Parse the GOMP_SPINCOUNT environment varible. Return true if one was + present and it was successfully parsed. */ + +static bool +parse_spincount (const char *name, unsigned long long *pvalue) +{ + char *env, *end; + unsigned long long value, mult = 1; + + env = getenv (name); + if (env == NULL) + return false; + + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '\0') + goto invalid; + + if (strncasecmp (env, "infinite", 8) == 0 + || strncasecmp (env, "infinity", 8) == 0) + { + value = ~0ULL; + end = env + 8; + goto check_tail; + } + + errno = 0; + value = strtoull (env, &end, 10); + if (errno) + goto invalid; + + while (isspace ((unsigned char) *end)) + ++end; + if (*end != '\0') + { + switch (tolower ((unsigned char) *end)) + { + case 'k': + mult = 1000LL; + break; + case 'm': + mult = 1000LL * 1000LL; + break; + case 'g': + mult = 1000LL * 1000LL * 1000LL; + break; + case 't': + mult = 1000LL * 1000LL * 1000LL * 1000LL; + break; + default: + goto invalid; + } + ++end; + check_tail: + while (isspace ((unsigned char) *end)) + ++end; + if (*end != '\0') + goto invalid; + } + + if (value > ~0ULL / mult) + value = ~0ULL; + else + value *= mult; + + *pvalue = value; + return true; + + invalid: + gomp_error ("Invalid value for environment variable %s", name); + return false; +} + +/* Parse a boolean value for environment variable NAME and store the + result in VALUE. */ + +static void +parse_boolean (const char *name, bool *value) +{ + const char *env; + + env = getenv (name); + if (env == NULL) + return; + + while (isspace ((unsigned char) *env)) + ++env; + if (strncasecmp (env, "true", 4) == 0) + { + *value = true; + env += 4; + } + else if (strncasecmp (env, "false", 5) == 0) + { + *value = false; + env += 5; + } + else + env = "X"; + while (isspace ((unsigned char) *env)) + ++env; + if (*env != '\0') + gomp_error ("Invalid value for environment variable %s", name); +} + +/* Parse the OMP_WAIT_POLICY environment variable and store the + result in gomp_active_wait_policy. */ + +static int +parse_wait_policy (void) +{ + const char *env; + int ret = -1; + + env = getenv ("OMP_WAIT_POLICY"); + if (env == NULL) + return -1; + + while (isspace ((unsigned char) *env)) + ++env; + if (strncasecmp (env, "active", 6) == 0) + { + ret = 1; + env += 6; + } + else if (strncasecmp (env, "passive", 7) == 0) + { + ret = 0; + env += 7; + } + else + env = "X"; + while (isspace ((unsigned char) *env)) + ++env; + if (*env == '\0') + return ret; + gomp_error ("Invalid value for environment variable OMP_WAIT_POLICY"); + return -1; +} + +/* Parse the GOMP_CPU_AFFINITY environment varible. Return true if one was + present and it was successfully parsed. */ + +static bool +parse_affinity (bool ignore) +{ + char *env, *end, *start; + int pass; + unsigned long cpu_beg, cpu_end, cpu_stride; + size_t count = 0, needed; + + env = getenv ("GOMP_CPU_AFFINITY"); + if (env == NULL) + return false; + + start = env; + for (pass = 0; pass < 2; pass++) + { + env = start; + if (pass == 1) + { + if (ignore) + return false; + + gomp_places_list_len = 0; + gomp_places_list = gomp_affinity_alloc (count, true); + if (gomp_places_list == NULL) + return false; + } + do + { + while (isspace ((unsigned char) *env)) + ++env; + + errno = 0; + cpu_beg = strtoul (env, &end, 0); + if (errno || cpu_beg >= 65536) + goto invalid; + cpu_end = cpu_beg; + cpu_stride = 1; + + env = end; + if (*env == '-') + { + errno = 0; + cpu_end = strtoul (++env, &end, 0); + if (errno || cpu_end >= 65536 || cpu_end < cpu_beg) + goto invalid; + + env = end; + if (*env == ':') + { + errno = 0; + cpu_stride = strtoul (++env, &end, 0); + if (errno || cpu_stride == 0 || cpu_stride >= 65536) + goto invalid; + + env = end; + } + } + + needed = (cpu_end - cpu_beg) / cpu_stride + 1; + if (pass == 0) + count += needed; + else + { + while (needed--) + { + void *p = gomp_places_list[gomp_places_list_len]; + gomp_affinity_init_place (p); + if (gomp_affinity_add_cpus (p, cpu_beg, 1, 0, true)) + ++gomp_places_list_len; + cpu_beg += cpu_stride; + } + } + + while (isspace ((unsigned char) *env)) + ++env; + + if (*env == ',') + env++; + else if (*env == '\0') + break; + } + while (1); + } + + if (gomp_places_list_len == 0) + { + free (gomp_places_list); + gomp_places_list = NULL; + return false; + } + return true; + + invalid: + gomp_error ("Invalid value for enviroment variable GOMP_CPU_AFFINITY"); + return false; +} + +static void +parse_acc_device_type (void) +{ + const char *env = getenv ("ACC_DEVICE_TYPE"); + + if (env && *env != '\0') + goacc_device_type = strdup (env); + else + goacc_device_type = NULL; +} + +static void +handle_omp_display_env (unsigned long stacksize, int wait_policy) +{ + const char *env; + bool display = false; + bool verbose = false; + int i; + + env = getenv ("OMP_DISPLAY_ENV"); + if (env == NULL) + return; + + while (isspace ((unsigned char) *env)) + ++env; + if (strncasecmp (env, "true", 4) == 0) + { + display = true; + env += 4; + } + else if (strncasecmp (env, "false", 5) == 0) + { + display = false; + env += 5; + } + else if (strncasecmp (env, "verbose", 7) == 0) + { + display = true; + verbose = true; + env += 7; + } + else + env = "X"; + while (isspace ((unsigned char) *env)) + ++env; + if (*env != '\0') + gomp_error ("Invalid value for environment variable OMP_DISPLAY_ENV"); + + if (!display) + return; + + fputs ("\nOPENMP DISPLAY ENVIRONMENT BEGIN\n", stderr); + + fputs (" _OPENMP = '201307'\n", stderr); + fprintf (stderr, " OMP_DYNAMIC = '%s'\n", + gomp_global_icv.dyn_var ? "TRUE" : "FALSE"); + fprintf (stderr, " OMP_NESTED = '%s'\n", + gomp_global_icv.nest_var ? "TRUE" : "FALSE"); + + fprintf (stderr, " OMP_NUM_THREADS = '%lu", gomp_global_icv.nthreads_var); + for (i = 1; i < gomp_nthreads_var_list_len; i++) + fprintf (stderr, ",%lu", gomp_nthreads_var_list[i]); + fputs ("'\n", stderr); + + fprintf (stderr, " OMP_SCHEDULE = '"); + switch (gomp_global_icv.run_sched_var) + { + case GFS_RUNTIME: + fputs ("RUNTIME", stderr); + break; + case GFS_STATIC: + fputs ("STATIC", stderr); + break; + case GFS_DYNAMIC: + fputs ("DYNAMIC", stderr); + break; + case GFS_GUIDED: + fputs ("GUIDED", stderr); + break; + case GFS_AUTO: + fputs ("AUTO", stderr); + break; + } + fputs ("'\n", stderr); + + fputs (" OMP_PROC_BIND = '", stderr); + switch (gomp_global_icv.bind_var) + { + case omp_proc_bind_false: + fputs ("FALSE", stderr); + break; + case omp_proc_bind_true: + fputs ("TRUE", stderr); + break; + case omp_proc_bind_master: + fputs ("MASTER", stderr); + break; + case omp_proc_bind_close: + fputs ("CLOSE", stderr); + break; + case omp_proc_bind_spread: + fputs ("SPREAD", stderr); + break; + } + for (i = 1; i < gomp_bind_var_list_len; i++) + switch (gomp_bind_var_list[i]) + { + case omp_proc_bind_master: + fputs (",MASTER", stderr); + break; + case omp_proc_bind_close: + fputs (",CLOSE", stderr); + break; + case omp_proc_bind_spread: + fputs (",SPREAD", stderr); + break; + } + fputs ("'\n", stderr); + fputs (" OMP_PLACES = '", stderr); + for (i = 0; i < gomp_places_list_len; i++) + { + fputs ("{", stderr); + gomp_affinity_print_place (gomp_places_list[i]); + fputs (i + 1 == gomp_places_list_len ? "}" : "},", stderr); + } + fputs ("'\n", stderr); + + fprintf (stderr, " OMP_STACKSIZE = '%lu'\n", stacksize); + + /* GOMP's default value is actually neither active nor passive. */ + fprintf (stderr, " OMP_WAIT_POLICY = '%s'\n", + wait_policy > 0 ? "ACTIVE" : "PASSIVE"); + fprintf (stderr, " OMP_THREAD_LIMIT = '%u'\n", + gomp_global_icv.thread_limit_var); + fprintf (stderr, " OMP_MAX_ACTIVE_LEVELS = '%lu'\n", + gomp_max_active_levels_var); + + fprintf (stderr, " OMP_CANCELLATION = '%s'\n", + gomp_cancel_var ? "TRUE" : "FALSE"); + fprintf (stderr, " OMP_DEFAULT_DEVICE = '%d'\n", + gomp_global_icv.default_device_var); + + if (verbose) + { + fputs (" GOMP_CPU_AFFINITY = ''\n", stderr); + fprintf (stderr, " GOMP_STACKSIZE = '%lu'\n", stacksize); +#ifdef HAVE_INTTYPES_H + fprintf (stderr, " GOMP_SPINCOUNT = '%"PRIu64"'\n", + (uint64_t) gomp_spin_count_var); +#else + fprintf (stderr, " GOMP_SPINCOUNT = '%lu'\n", + (unsigned long) gomp_spin_count_var); +#endif + } + + fputs ("OPENMP DISPLAY ENVIRONMENT END\n", stderr); +} + + +static void __attribute__((constructor)) +initialize_env (void) +{ + unsigned long thread_limit_var, stacksize; + int wait_policy; + +#ifndef __hermit__ + /* Do a compile time check that mkomp_h.pl did good job. */ + omp_check_defines (); +#endif + + parse_schedule (); + parse_boolean ("OMP_DYNAMIC", &gomp_global_icv.dyn_var); + parse_boolean ("OMP_NESTED", &gomp_global_icv.nest_var); + parse_boolean ("OMP_CANCELLATION", &gomp_cancel_var); + parse_int ("OMP_DEFAULT_DEVICE", &gomp_global_icv.default_device_var, true); + parse_unsigned_long ("OMP_MAX_ACTIVE_LEVELS", &gomp_max_active_levels_var, + true); + if (parse_unsigned_long ("OMP_THREAD_LIMIT", &thread_limit_var, false)) + { + gomp_global_icv.thread_limit_var + = thread_limit_var > INT_MAX ? UINT_MAX : thread_limit_var; + } + parse_int ("GOMP_DEBUG", &gomp_debug_var, true); +#ifndef HAVE_SYNC_BUILTINS + gomp_mutex_init (&gomp_managed_threads_lock); +#endif + gomp_init_num_threads (); + gomp_available_cpus = gomp_global_icv.nthreads_var; + if (!parse_unsigned_long_list ("OMP_NUM_THREADS", + &gomp_global_icv.nthreads_var, + &gomp_nthreads_var_list, + &gomp_nthreads_var_list_len)) + gomp_global_icv.nthreads_var = gomp_available_cpus; + bool ignore = false; + if (parse_bind_var ("OMP_PROC_BIND", + &gomp_global_icv.bind_var, + &gomp_bind_var_list, + &gomp_bind_var_list_len) + && gomp_global_icv.bind_var == omp_proc_bind_false) + ignore = true; + /* Make sure OMP_PLACES and GOMP_CPU_AFFINITY env vars are always + parsed if present in the environment. If OMP_PROC_BIND was set + explictly to false, don't populate places list though. If places + list was successfully set from OMP_PLACES, only parse but don't process + GOMP_CPU_AFFINITY. If OMP_PROC_BIND was not set in the environment, + default to OMP_PROC_BIND=true if OMP_PLACES or GOMP_CPU_AFFINITY + was successfully parsed into a places list, otherwise to + OMP_PROC_BIND=false. */ + if (parse_places_var ("OMP_PLACES", ignore)) + { + if (gomp_global_icv.bind_var == omp_proc_bind_false) + gomp_global_icv.bind_var = true; + ignore = true; + } + if (parse_affinity (ignore)) + { + if (gomp_global_icv.bind_var == omp_proc_bind_false) + gomp_global_icv.bind_var = true; + ignore = true; + } + if (gomp_global_icv.bind_var != omp_proc_bind_false) + gomp_init_affinity (); + wait_policy = parse_wait_policy (); + if (!parse_spincount ("GOMP_SPINCOUNT", &gomp_spin_count_var)) + { + /* Using a rough estimation of 100000 spins per msec, + use 5 min blocking for OMP_WAIT_POLICY=active, + 3 msec blocking when OMP_WAIT_POLICY is not specificed + and 0 when OMP_WAIT_POLICY=passive. + Depending on the CPU speed, this can be e.g. 5 times longer + or 5 times shorter. */ + if (wait_policy > 0) + gomp_spin_count_var = 30000000000LL; + else if (wait_policy < 0) + gomp_spin_count_var = 300000LL; + } + /* gomp_throttled_spin_count_var is used when there are more libgomp + managed threads than available CPUs. Use very short spinning. */ + if (wait_policy > 0) + gomp_throttled_spin_count_var = 1000LL; + else if (wait_policy < 0) + gomp_throttled_spin_count_var = 100LL; + if (gomp_throttled_spin_count_var > gomp_spin_count_var) + gomp_throttled_spin_count_var = gomp_spin_count_var; + + /* Not strictly environment related, but ordering constructors is tricky. */ + pthread_attr_init (&gomp_thread_attr); + pthread_attr_setdetachstate (&gomp_thread_attr, PTHREAD_CREATE_DETACHED); + + if (parse_stacksize ("OMP_STACKSIZE", &stacksize) + || parse_stacksize ("GOMP_STACKSIZE", &stacksize)) + { + int err; + + err = pthread_attr_setstacksize (&gomp_thread_attr, stacksize); + +#ifdef PTHREAD_STACK_MIN + if (err == EINVAL) + { + if (stacksize < PTHREAD_STACK_MIN) + gomp_error ("Stack size less than minimum of %luk", + PTHREAD_STACK_MIN / 1024ul + + (PTHREAD_STACK_MIN % 1024 != 0)); + else + gomp_error ("Stack size larger than system limit"); + } + else +#endif + if (err != 0) + gomp_error ("Stack size change failed: %s", strerror (err)); + } + + handle_omp_display_env (stacksize, wait_policy); + + /* OpenACC. */ + + if (!parse_int ("ACC_DEVICE_NUM", &goacc_device_num, true)) + goacc_device_num = 0; + + parse_acc_device_type (); + +#if 0 + goacc_runtime_initialize (); +#endif +} + + +/* The public OpenMP API routines that access these variables. */ + +void +omp_set_num_threads (int n) +{ + struct gomp_task_icv *icv = gomp_icv (true); + icv->nthreads_var = (n > 0 ? n : 1); +} + +void +omp_set_dynamic (int val) +{ + struct gomp_task_icv *icv = gomp_icv (true); + icv->dyn_var = val; +} + +int +omp_get_dynamic (void) +{ + struct gomp_task_icv *icv = gomp_icv (false); + return icv->dyn_var; +} + +void +omp_set_nested (int val) +{ + struct gomp_task_icv *icv = gomp_icv (true); + icv->nest_var = val; +} + +int +omp_get_nested (void) +{ + struct gomp_task_icv *icv = gomp_icv (false); + return icv->nest_var; +} + +void +omp_set_schedule (omp_sched_t kind, int modifier) +{ + struct gomp_task_icv *icv = gomp_icv (true); + switch (kind) + { + case omp_sched_static: + if (modifier < 1) + modifier = 0; + icv->run_sched_modifier = modifier; + break; + case omp_sched_dynamic: + case omp_sched_guided: + if (modifier < 1) + modifier = 1; + icv->run_sched_modifier = modifier; + break; + case omp_sched_auto: + break; + default: + return; + } + icv->run_sched_var = kind; +} + +void +omp_get_schedule (omp_sched_t *kind, int *modifier) +{ + struct gomp_task_icv *icv = gomp_icv (false); + *kind = icv->run_sched_var; + *modifier = icv->run_sched_modifier; +} + +int +omp_get_max_threads (void) +{ + struct gomp_task_icv *icv = gomp_icv (false); + return icv->nthreads_var; +} + +int +omp_get_thread_limit (void) +{ + struct gomp_task_icv *icv = gomp_icv (false); + return icv->thread_limit_var > INT_MAX ? INT_MAX : icv->thread_limit_var; +} + +void +omp_set_max_active_levels (int max_levels) +{ + if (max_levels >= 0) + gomp_max_active_levels_var = max_levels; +} + +int +omp_get_max_active_levels (void) +{ + return gomp_max_active_levels_var; +} + +int +omp_get_cancellation (void) +{ + return gomp_cancel_var; +} + +omp_proc_bind_t +omp_get_proc_bind (void) +{ + struct gomp_task_icv *icv = gomp_icv (false); + return icv->bind_var; +} + +void +omp_set_default_device (int device_num) +{ + struct gomp_task_icv *icv = gomp_icv (true); + icv->default_device_var = device_num >= 0 ? device_num : 0; +} + +int +omp_get_default_device (void) +{ + struct gomp_task_icv *icv = gomp_icv (false); + return icv->default_device_var; +} + +int +omp_get_num_devices (void) +{ +#ifdef __hermit__ + return 0; +#else + return gomp_get_num_devices (); +#endif +} + +int +omp_get_num_teams (void) +{ + /* Hardcoded to 1 on host, MIC, HSAIL? Maybe variable on PTX. */ + return 1; +} + +int +omp_get_team_num (void) +{ + /* Hardcoded to 0 on host, MIC, HSAIL? Maybe variable on PTX. */ + return 0; +} + +int +omp_is_initial_device (void) +{ + /* Hardcoded to 1 on host, should be 0 on MIC, HSAIL, PTX. */ + return 1; +} + +ialias (omp_set_dynamic) +ialias (omp_set_nested) +ialias (omp_set_num_threads) +ialias (omp_get_dynamic) +ialias (omp_get_nested) +ialias (omp_set_schedule) +ialias (omp_get_schedule) +ialias (omp_get_max_threads) +ialias (omp_get_thread_limit) +ialias (omp_set_max_active_levels) +ialias (omp_get_max_active_levels) +ialias (omp_get_cancellation) +ialias (omp_get_proc_bind) +ialias (omp_set_default_device) +ialias (omp_get_default_device) +ialias (omp_get_num_devices) +ialias (omp_get_num_teams) +ialias (omp_get_team_num) +ialias (omp_is_initial_device) diff --git a/hermit/usr/libgomp/error.c b/hermit/usr/libgomp/error.c new file mode 100644 index 000000000..094c24a38 --- /dev/null +++ b/hermit/usr/libgomp/error.c @@ -0,0 +1,91 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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 +#include +#include + + +#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); +} diff --git a/hermit/usr/libgomp/fortran.c b/hermit/usr/libgomp/fortran.c new file mode 100644 index 000000000..993145f88 --- /dev/null +++ b/hermit/usr/libgomp/fortran.c @@ -0,0 +1,495 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Jakub Jelinek . + + 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 + . */ + +/* This file contains Fortran wrapper routines. */ + +#include "libgomp.h" +#include "libgomp_f.h" +#include +#include + +#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 (); +} diff --git a/hermit/usr/libgomp/hashtab.h b/hermit/usr/libgomp/hashtab.h new file mode 100644 index 000000000..0cc224ddb --- /dev/null +++ b/hermit/usr/libgomp/hashtab.h @@ -0,0 +1,442 @@ +/* An expandable hash tables datatype. + Copyright (C) 1999-2015 Free Software Foundation, Inc. + Contributed by Vladimir Makarov . + +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; +} diff --git a/hermit/usr/libgomp/iter.c b/hermit/usr/libgomp/iter.c new file mode 100644 index 000000000..0ceb41d90 --- /dev/null +++ b/hermit/usr/libgomp/iter.c @@ -0,0 +1,338 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* This file contains routines for managing work-share iteration, both + for loops and sections. */ + +#include "libgomp.h" +#include + + +/* 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 */ diff --git a/hermit/usr/libgomp/iter_ull.c b/hermit/usr/libgomp/iter_ull.c new file mode 100644 index 000000000..b1cad84d4 --- /dev/null +++ b/hermit/usr/libgomp/iter_ull.c @@ -0,0 +1,345 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* This file contains routines for managing work-share iteration, both + for loops and sections. */ + +#include "libgomp.h" +#include + +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 */ diff --git a/hermit/usr/libgomp/libgomp.a b/hermit/usr/libgomp/libgomp.a new file mode 100644 index 0000000000000000000000000000000000000000..5dcf511497f15b5ce5b212605136ba524b35cc82 GIT binary patch literal 169844 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 aOEV+wpr5`B@8i^r(4J=i literal 0 HcmV?d00001 diff --git a/hermit/usr/libgomp/libgomp.h b/hermit/usr/libgomp/libgomp.h new file mode 100644 index 000000000..9a83a4a7b --- /dev/null +++ b/hermit/usr/libgomp/libgomp.h @@ -0,0 +1,887 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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 +#include +#include +#include +#include + +#ifdef HAVE_ATTRIBUTE_VISIBILITY +# pragma GCC visibility push(hidden) +#endif + +/* If we were a C++ library, we'd get this from . */ +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; +}; + + +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 */ diff --git a/hermit/usr/libgomp/libgomp.spec b/hermit/usr/libgomp/libgomp.spec new file mode 100644 index 000000000..5b3a383ae --- /dev/null +++ b/hermit/usr/libgomp/libgomp.spec @@ -0,0 +1,3 @@ +# 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 diff --git a/hermit/usr/libgomp/libgomp_f.h b/hermit/usr/libgomp/libgomp_f.h new file mode 100644 index 000000000..2c495965f --- /dev/null +++ b/hermit/usr/libgomp/libgomp_f.h @@ -0,0 +1,96 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Jakub Jelinek . + + 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 + . */ + +/* 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 */ diff --git a/hermit/usr/libgomp/libgomp_g.h b/hermit/usr/libgomp/libgomp_g.h new file mode 100644 index 000000000..5e88d451b --- /dev/null +++ b/hermit/usr/libgomp/libgomp_g.h @@ -0,0 +1,234 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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 +#include + +/* 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 */ diff --git a/hermit/usr/libgomp/lock.c b/hermit/usr/libgomp/lock.c new file mode 100644 index 000000000..6cbc1c3cc --- /dev/null +++ b/hermit/usr/libgomp/lock.c @@ -0,0 +1,305 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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 diff --git a/hermit/usr/libgomp/loop.c b/hermit/usr/libgomp/loop.c new file mode 100644 index 000000000..27d78db7a --- /dev/null +++ b/hermit/usr/libgomp/loop.c @@ -0,0 +1,675 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* This file handles the LOOP (FOR/DO) construct. */ + +#include +#include +#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 diff --git a/hermit/usr/libgomp/loop_ull.c b/hermit/usr/libgomp/loop_ull.c new file mode 100644 index 000000000..de56ae0b7 --- /dev/null +++ b/hermit/usr/libgomp/loop_ull.c @@ -0,0 +1,572 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* This file handles the LOOP (FOR/DO) construct. */ + +#include +#include +#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 diff --git a/hermit/usr/libgomp/mutex.c b/hermit/usr/libgomp/mutex.c new file mode 100644 index 000000000..39bb64da0 --- /dev/null +++ b/hermit/usr/libgomp/mutex.c @@ -0,0 +1 @@ +/* Everything is in the header. */ diff --git a/hermit/usr/libgomp/mutex.h b/hermit/usr/libgomp/mutex.h new file mode 100644 index 000000000..5b46026e4 --- /dev/null +++ b/hermit/usr/libgomp/mutex.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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 + +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 */ diff --git a/hermit/usr/libgomp/omp-lock.h b/hermit/usr/libgomp/omp-lock.h new file mode 100644 index 000000000..e51dc271f --- /dev/null +++ b/hermit/usr/libgomp/omp-lock.h @@ -0,0 +1,23 @@ +/* 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 +#include + +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 diff --git a/hermit/usr/libgomp/omp.h b/hermit/usr/libgomp/omp.h new file mode 100644 index 000000000..c15a22b26 --- /dev/null +++ b/hermit/usr/libgomp/omp.h @@ -0,0 +1,128 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +#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 */ diff --git a/hermit/usr/libgomp/omp_lib.h b/hermit/usr/libgomp/omp_lib.h new file mode 100644 index 000000000..1de6eb70e --- /dev/null +++ b/hermit/usr/libgomp/omp_lib.h @@ -0,0 +1,98 @@ +! Copyright (C) 2005-2015 Free Software Foundation, Inc. +! Contributed by Jakub Jelinek . + +! 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 +! . + + 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 diff --git a/hermit/usr/libgomp/omp_lib.h.in b/hermit/usr/libgomp/omp_lib.h.in new file mode 100644 index 000000000..d590bc151 --- /dev/null +++ b/hermit/usr/libgomp/omp_lib.h.in @@ -0,0 +1,98 @@ +! Copyright (C) 2005-2015 Free Software Foundation, Inc. +! Contributed by Jakub Jelinek . + +! 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 +! . + + 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 diff --git a/hermit/usr/libgomp/ordered.c b/hermit/usr/libgomp/ordered.c new file mode 100644 index 000000000..69ca217b4 --- /dev/null +++ b/hermit/usr/libgomp/ordered.c @@ -0,0 +1,252 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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) +{ +} diff --git a/hermit/usr/libgomp/parallel.c b/hermit/usr/libgomp/parallel.c new file mode 100644 index 000000000..6d5ef050f --- /dev/null +++ b/hermit/usr/libgomp/parallel.c @@ -0,0 +1,302 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* This file handles the (bare) PARALLEL construct. */ + +#include "libgomp.h" +#include + + +/* 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) diff --git a/hermit/usr/libgomp/proc.c b/hermit/usr/libgomp/proc.c new file mode 100644 index 000000000..643f23d13 --- /dev/null +++ b/hermit/usr/libgomp/proc.c @@ -0,0 +1,109 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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 +#include +#ifdef HAVE_GETLOADAVG +# ifdef HAVE_SYS_LOADAVG_H +# include +# 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; + unsigned nthreads_var = gomp_icv (false)->nthreads_var; + +#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) diff --git a/hermit/usr/libgomp/ptrlock.c b/hermit/usr/libgomp/ptrlock.c new file mode 100644 index 000000000..39bb64da0 --- /dev/null +++ b/hermit/usr/libgomp/ptrlock.c @@ -0,0 +1 @@ +/* Everything is in the header. */ diff --git a/hermit/usr/libgomp/ptrlock.h b/hermit/usr/libgomp/ptrlock.h new file mode 100644 index 000000000..86faad780 --- /dev/null +++ b/hermit/usr/libgomp/ptrlock.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2008-2015 Free Software Foundation, Inc. + Contributed by Jakub Jelinek . + + 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 + . */ + +/* 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 */ diff --git a/hermit/usr/libgomp/sections.c b/hermit/usr/libgomp/sections.c new file mode 100644 index 000000000..f3a17259f --- /dev/null +++ b/hermit/usr/libgomp/sections.c @@ -0,0 +1,182 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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 (); +} diff --git a/hermit/usr/libgomp/sem.c b/hermit/usr/libgomp/sem.c new file mode 100644 index 000000000..4b8fb0831 --- /dev/null +++ b/hermit/usr/libgomp/sem.c @@ -0,0 +1,124 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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 + +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 diff --git a/hermit/usr/libgomp/sem.h b/hermit/usr/libgomp/sem.h new file mode 100644 index 000000000..51ba379da --- /dev/null +++ b/hermit/usr/libgomp/sem.h @@ -0,0 +1,88 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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 + +#ifdef HAVE_ATTRIBUTE_VISIBILITY +# pragma GCC visibility pop +#endif + +#ifdef HAVE_BROKEN_POSIX_SEMAPHORES +#include + +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 */ diff --git a/hermit/usr/libgomp/single.c b/hermit/usr/libgomp/single.c new file mode 100644 index 000000000..7cb6eed38 --- /dev/null +++ b/hermit/usr/libgomp/single.c @@ -0,0 +1,105 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* 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 (); +} diff --git a/hermit/usr/libgomp/task.c b/hermit/usr/libgomp/task.c new file mode 100644 index 000000000..74920d5dd --- /dev/null +++ b/hermit/usr/libgomp/task.c @@ -0,0 +1,1216 @@ +/* Copyright (C) 2007-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* This file handles the maintainence of tasks in response to task + creation and termination. */ + +#include "libgomp.h" +#include +#include + +typedef struct gomp_task_depend_entry *hash_entry_type; + +static inline void * +htab_alloc (size_t size) +{ + return gomp_malloc (size); +} + +static inline void +htab_free (void *ptr) +{ + free (ptr); +} + +#include "hashtab.h" + +static inline hashval_t +htab_hash (hash_entry_type element) +{ + return hash_pointer (element->addr); +} + +static inline bool +htab_eq (hash_entry_type x, hash_entry_type y) +{ + return x->addr == y->addr; +} + +/* Create a new task data structure. */ + +void +gomp_init_task (struct gomp_task *task, struct gomp_task *parent_task, + struct gomp_task_icv *prev_icv) +{ + task->parent = parent_task; + task->icv = *prev_icv; + task->kind = GOMP_TASK_IMPLICIT; + task->taskwait = NULL; + task->in_tied_task = false; + task->final_task = false; + task->copy_ctors_done = false; + task->parent_depends_on = false; + task->children = NULL; + task->taskgroup = NULL; + task->dependers = NULL; + task->depend_hash = NULL; + task->depend_count = 0; +} + +/* Clean up a task, after completing it. */ + +void +gomp_end_task (void) +{ + struct gomp_thread *thr = gomp_thread (); + struct gomp_task *task = thr->task; + + gomp_finish_task (task); + thr->task = task->parent; +} + +static inline void +gomp_clear_parent (struct gomp_task *children) +{ + struct gomp_task *task = children; + + if (task) + do + { + task->parent = NULL; + task = task->next_child; + } + while (task != children); +} + +static void gomp_task_maybe_wait_for_dependencies (void **depend); + +/* Called when encountering an explicit task directive. If IF_CLAUSE is + false, then we must not delay in executing the task. If UNTIED is true, + then the task may be executed by any member of the team. */ + +void +GOMP_task (void (*fn) (void *), void *data, void (*cpyfn) (void *, void *), + long arg_size, long arg_align, bool if_clause, unsigned flags, + void **depend) +{ + struct gomp_thread *thr = gomp_thread (); + struct gomp_team *team = thr->ts.team; + +#ifdef HAVE_BROKEN_POSIX_SEMAPHORES + /* If pthread_mutex_* is used for omp_*lock*, then each task must be + tied to one thread all the time. This means UNTIED tasks must be + tied and if CPYFN is non-NULL IF(0) must be forced, as CPYFN + might be running on different thread than FN. */ + if (cpyfn) + if_clause = false; + if (flags & 1) + flags &= ~1; +#endif + + /* If parallel or taskgroup has been cancelled, don't start new tasks. */ + if (team + && (gomp_team_barrier_cancelled (&team->barrier) + || (thr->task->taskgroup && thr->task->taskgroup->cancelled))) + return; + + if (!if_clause || team == NULL + || (thr->task && thr->task->final_task) + || team->task_count > 64 * team->nthreads) + { + struct gomp_task task; + + /* If there are depend clauses and earlier deferred sibling tasks + with depend clauses, check if there isn't a dependency. If there + is, we need to wait for them. There is no need to handle + depend clauses for non-deferred tasks other than this, because + the parent task is suspended until the child task finishes and thus + it can't start further child tasks. */ + if ((flags & 8) && thr->task && thr->task->depend_hash) + gomp_task_maybe_wait_for_dependencies (depend); + + gomp_init_task (&task, thr->task, gomp_icv (false)); + task.kind = GOMP_TASK_IFFALSE; + task.final_task = (thr->task && thr->task->final_task) || (flags & 2); + if (thr->task) + { + task.in_tied_task = thr->task->in_tied_task; + task.taskgroup = thr->task->taskgroup; + } + thr->task = &task; + if (__builtin_expect (cpyfn != NULL, 0)) + { + char buf[arg_size + arg_align - 1]; + char *arg = (char *) (((uintptr_t) buf + arg_align - 1) + & ~(uintptr_t) (arg_align - 1)); + cpyfn (arg, data); + fn (arg); + } + else + fn (data); + /* Access to "children" is normally done inside a task_lock + mutex region, but the only way this particular task.children + can be set is if this thread's task work function (fn) + creates children. So since the setter is *this* thread, we + need no barriers here when testing for non-NULL. We can have + task.children set by the current thread then changed by a + child thread, but seeing a stale non-NULL value is not a + problem. Once past the task_lock acquisition, this thread + will see the real value of task.children. */ + if (task.children != NULL) + { + gomp_mutex_lock (&team->task_lock); + gomp_clear_parent (task.children); + gomp_mutex_unlock (&team->task_lock); + } + gomp_end_task (); + } + else + { + struct gomp_task *task; + struct gomp_task *parent = thr->task; + struct gomp_taskgroup *taskgroup = parent->taskgroup; + char *arg; + bool do_wake; + size_t depend_size = 0; + + if (flags & 8) + depend_size = ((uintptr_t) depend[0] + * sizeof (struct gomp_task_depend_entry)); + task = gomp_malloc (sizeof (*task) + depend_size + + arg_size + arg_align - 1); + arg = (char *) (((uintptr_t) (task + 1) + depend_size + arg_align - 1) + & ~(uintptr_t) (arg_align - 1)); + gomp_init_task (task, parent, gomp_icv (false)); + task->kind = GOMP_TASK_IFFALSE; + task->in_tied_task = parent->in_tied_task; + task->taskgroup = taskgroup; + thr->task = task; + if (cpyfn) + { + cpyfn (arg, data); + task->copy_ctors_done = true; + } + else + memcpy (arg, data, arg_size); + thr->task = parent; + task->kind = GOMP_TASK_WAITING; + task->fn = fn; + task->fn_data = arg; + task->final_task = (flags & 2) >> 1; + gomp_mutex_lock (&team->task_lock); + /* If parallel or taskgroup has been cancelled, don't start new + tasks. */ + if (__builtin_expect ((gomp_team_barrier_cancelled (&team->barrier) + || (taskgroup && taskgroup->cancelled)) + && !task->copy_ctors_done, 0)) + { + gomp_mutex_unlock (&team->task_lock); + gomp_finish_task (task); + free (task); + return; + } + if (taskgroup) + taskgroup->num_children++; + if (depend_size) + { + size_t ndepend = (uintptr_t) depend[0]; + size_t nout = (uintptr_t) depend[1]; + size_t i; + hash_entry_type ent; + + task->depend_count = ndepend; + task->num_dependees = 0; + if (parent->depend_hash == NULL) + parent->depend_hash + = htab_create (2 * ndepend > 12 ? 2 * ndepend : 12); + for (i = 0; i < ndepend; i++) + { + task->depend[i].addr = depend[2 + i]; + task->depend[i].next = NULL; + task->depend[i].prev = NULL; + task->depend[i].task = task; + task->depend[i].is_in = i >= nout; + task->depend[i].redundant = false; + task->depend[i].redundant_out = false; + + hash_entry_type *slot + = htab_find_slot (&parent->depend_hash, &task->depend[i], + INSERT); + hash_entry_type out = NULL, last = NULL; + if (*slot) + { + /* If multiple depends on the same task are the + same, all but the first one are redundant. + As inout/out come first, if any of them is + inout/out, it will win, which is the right + semantics. */ + if ((*slot)->task == task) + { + task->depend[i].redundant = true; + continue; + } + for (ent = *slot; ent; ent = ent->next) + { + if (ent->redundant_out) + break; + + last = ent; + + /* depend(in:...) doesn't depend on earlier + depend(in:...). */ + if (i >= nout && ent->is_in) + continue; + + if (!ent->is_in) + out = ent; + + struct gomp_task *tsk = ent->task; + if (tsk->dependers == NULL) + { + tsk->dependers + = gomp_malloc (sizeof (struct gomp_dependers_vec) + + 6 * sizeof (struct gomp_task *)); + tsk->dependers->n_elem = 1; + tsk->dependers->allocated = 6; + tsk->dependers->elem[0] = task; + task->num_dependees++; + continue; + } + /* We already have some other dependency on tsk + from earlier depend clause. */ + else if (tsk->dependers->n_elem + && (tsk->dependers->elem[tsk->dependers->n_elem + - 1] + == task)) + continue; + else if (tsk->dependers->n_elem + == tsk->dependers->allocated) + { + tsk->dependers->allocated + = tsk->dependers->allocated * 2 + 2; + tsk->dependers + = gomp_realloc (tsk->dependers, + sizeof (struct gomp_dependers_vec) + + (tsk->dependers->allocated + * sizeof (struct gomp_task *))); + } + tsk->dependers->elem[tsk->dependers->n_elem++] = task; + task->num_dependees++; + } + task->depend[i].next = *slot; + (*slot)->prev = &task->depend[i]; + } + *slot = &task->depend[i]; + + /* There is no need to store more than one depend({,in}out:) + task per address in the hash table chain for the purpose + of creation of deferred tasks, because each out + depends on all earlier outs, thus it is enough to record + just the last depend({,in}out:). For depend(in:), we need + to keep all of the previous ones not terminated yet, because + a later depend({,in}out:) might need to depend on all of + them. So, if the new task's clause is depend({,in}out:), + we know there is at most one other depend({,in}out:) clause + in the list (out). For non-deferred tasks we want to see + all outs, so they are moved to the end of the chain, + after first redundant_out entry all following entries + should be redundant_out. */ + if (!task->depend[i].is_in && out) + { + if (out != last) + { + out->next->prev = out->prev; + out->prev->next = out->next; + out->next = last->next; + out->prev = last; + last->next = out; + if (out->next) + out->next->prev = out; + } + out->redundant_out = true; + } + } + if (task->num_dependees) + { + gomp_mutex_unlock (&team->task_lock); + return; + } + } + if (parent->children) + { + task->next_child = parent->children; + task->prev_child = parent->children->prev_child; + task->next_child->prev_child = task; + task->prev_child->next_child = task; + } + else + { + task->next_child = task; + task->prev_child = task; + } + parent->children = task; + if (taskgroup) + { + if (taskgroup->children) + { + task->next_taskgroup = taskgroup->children; + task->prev_taskgroup = taskgroup->children->prev_taskgroup; + task->next_taskgroup->prev_taskgroup = task; + task->prev_taskgroup->next_taskgroup = task; + } + else + { + task->next_taskgroup = task; + task->prev_taskgroup = task; + } + taskgroup->children = task; + } + if (team->task_queue) + { + task->next_queue = team->task_queue; + task->prev_queue = team->task_queue->prev_queue; + task->next_queue->prev_queue = task; + task->prev_queue->next_queue = task; + } + else + { + task->next_queue = task; + task->prev_queue = task; + team->task_queue = task; + } + ++team->task_count; + ++team->task_queued_count; + gomp_team_barrier_set_task_pending (&team->barrier); + do_wake = team->task_running_count + !parent->in_tied_task + < team->nthreads; + gomp_mutex_unlock (&team->task_lock); + if (do_wake) + gomp_team_barrier_wake (&team->barrier, 1); + } +} + +static inline bool +gomp_task_run_pre (struct gomp_task *child_task, struct gomp_task *parent, + struct gomp_taskgroup *taskgroup, struct gomp_team *team) +{ + if (parent) + { + if (parent->children == child_task) + parent->children = child_task->next_child; + if (__builtin_expect (child_task->parent_depends_on, 0) + && parent->taskwait->last_parent_depends_on == child_task) + { + if (child_task->prev_child->kind == GOMP_TASK_WAITING + && child_task->prev_child->parent_depends_on) + parent->taskwait->last_parent_depends_on = child_task->prev_child; + else + parent->taskwait->last_parent_depends_on = NULL; + } + } + if (taskgroup && taskgroup->children == child_task) + taskgroup->children = child_task->next_taskgroup; + child_task->prev_queue->next_queue = child_task->next_queue; + child_task->next_queue->prev_queue = child_task->prev_queue; + if (team->task_queue == child_task) + { + if (child_task->next_queue != child_task) + team->task_queue = child_task->next_queue; + else + team->task_queue = NULL; + } + child_task->kind = GOMP_TASK_TIED; + if (--team->task_queued_count == 0) + gomp_team_barrier_clear_task_pending (&team->barrier); + if ((gomp_team_barrier_cancelled (&team->barrier) + || (taskgroup && taskgroup->cancelled)) + && !child_task->copy_ctors_done) + return true; + return false; +} + +static void +gomp_task_run_post_handle_depend_hash (struct gomp_task *child_task) +{ + struct gomp_task *parent = child_task->parent; + size_t i; + + for (i = 0; i < child_task->depend_count; i++) + if (!child_task->depend[i].redundant) + { + if (child_task->depend[i].next) + child_task->depend[i].next->prev = child_task->depend[i].prev; + if (child_task->depend[i].prev) + child_task->depend[i].prev->next = child_task->depend[i].next; + else + { + hash_entry_type *slot + = htab_find_slot (&parent->depend_hash, &child_task->depend[i], + NO_INSERT); + if (*slot != &child_task->depend[i]) + abort (); + if (child_task->depend[i].next) + *slot = child_task->depend[i].next; + else + htab_clear_slot (parent->depend_hash, slot); + } + } +} + +static size_t +gomp_task_run_post_handle_dependers (struct gomp_task *child_task, + struct gomp_team *team) +{ + struct gomp_task *parent = child_task->parent; + size_t i, count = child_task->dependers->n_elem, ret = 0; + for (i = 0; i < count; i++) + { + struct gomp_task *task = child_task->dependers->elem[i]; + if (--task->num_dependees != 0) + continue; + + struct gomp_taskgroup *taskgroup = task->taskgroup; + if (parent) + { + if (parent->children) + { + /* If parent is in gomp_task_maybe_wait_for_dependencies + and it doesn't need to wait for this task, put it after + all ready to run tasks it needs to wait for. */ + if (parent->taskwait && parent->taskwait->last_parent_depends_on + && !task->parent_depends_on) + { + struct gomp_task *last_parent_depends_on + = parent->taskwait->last_parent_depends_on; + task->next_child = last_parent_depends_on->next_child; + task->prev_child = last_parent_depends_on; + } + else + { + task->next_child = parent->children; + task->prev_child = parent->children->prev_child; + parent->children = task; + } + task->next_child->prev_child = task; + task->prev_child->next_child = task; + } + else + { + task->next_child = task; + task->prev_child = task; + parent->children = task; + } + if (parent->taskwait) + { + if (parent->taskwait->in_taskwait) + { + parent->taskwait->in_taskwait = false; + gomp_sem_post (&parent->taskwait->taskwait_sem); + } + else if (parent->taskwait->in_depend_wait) + { + parent->taskwait->in_depend_wait = false; + gomp_sem_post (&parent->taskwait->taskwait_sem); + } + if (parent->taskwait->last_parent_depends_on == NULL + && task->parent_depends_on) + parent->taskwait->last_parent_depends_on = task; + } + } + if (taskgroup) + { + if (taskgroup->children) + { + task->next_taskgroup = taskgroup->children; + task->prev_taskgroup = taskgroup->children->prev_taskgroup; + task->next_taskgroup->prev_taskgroup = task; + task->prev_taskgroup->next_taskgroup = task; + } + else + { + task->next_taskgroup = task; + task->prev_taskgroup = task; + } + taskgroup->children = task; + if (taskgroup->in_taskgroup_wait) + { + taskgroup->in_taskgroup_wait = false; + gomp_sem_post (&taskgroup->taskgroup_sem); + } + } + if (team->task_queue) + { + task->next_queue = team->task_queue; + task->prev_queue = team->task_queue->prev_queue; + task->next_queue->prev_queue = task; + task->prev_queue->next_queue = task; + } + else + { + task->next_queue = task; + task->prev_queue = task; + team->task_queue = task; + } + ++team->task_count; + ++team->task_queued_count; + ++ret; + } + free (child_task->dependers); + child_task->dependers = NULL; + if (ret > 1) + gomp_team_barrier_set_task_pending (&team->barrier); + return ret; +} + +static inline size_t +gomp_task_run_post_handle_depend (struct gomp_task *child_task, + struct gomp_team *team) +{ + if (child_task->depend_count == 0) + return 0; + + /* If parent is gone already, the hash table is freed and nothing + will use the hash table anymore, no need to remove anything from it. */ + if (child_task->parent != NULL) + gomp_task_run_post_handle_depend_hash (child_task); + + if (child_task->dependers == NULL) + return 0; + + return gomp_task_run_post_handle_dependers (child_task, team); +} + +static inline void +gomp_task_run_post_remove_parent (struct gomp_task *child_task) +{ + struct gomp_task *parent = child_task->parent; + if (parent == NULL) + return; + if (__builtin_expect (child_task->parent_depends_on, 0) + && --parent->taskwait->n_depend == 0 + && parent->taskwait->in_depend_wait) + { + parent->taskwait->in_depend_wait = false; + gomp_sem_post (&parent->taskwait->taskwait_sem); + } + child_task->prev_child->next_child = child_task->next_child; + child_task->next_child->prev_child = child_task->prev_child; + if (parent->children != child_task) + return; + if (child_task->next_child != child_task) + parent->children = child_task->next_child; + else + { + /* We access task->children in GOMP_taskwait + outside of the task lock mutex region, so + need a release barrier here to ensure memory + written by child_task->fn above is flushed + before the NULL is written. */ + __atomic_store_n (&parent->children, NULL, MEMMODEL_RELEASE); + if (parent->taskwait && parent->taskwait->in_taskwait) + { + parent->taskwait->in_taskwait = false; + gomp_sem_post (&parent->taskwait->taskwait_sem); + } + } +} + +static inline void +gomp_task_run_post_remove_taskgroup (struct gomp_task *child_task) +{ + struct gomp_taskgroup *taskgroup = child_task->taskgroup; + if (taskgroup == NULL) + return; + child_task->prev_taskgroup->next_taskgroup = child_task->next_taskgroup; + child_task->next_taskgroup->prev_taskgroup = child_task->prev_taskgroup; + if (taskgroup->num_children > 1) + --taskgroup->num_children; + else + { + /* We access taskgroup->num_children in GOMP_taskgroup_end + outside of the task lock mutex region, so + need a release barrier here to ensure memory + written by child_task->fn above is flushed + before the NULL is written. */ + __atomic_store_n (&taskgroup->num_children, 0, MEMMODEL_RELEASE); + } + if (taskgroup->children != child_task) + return; + if (child_task->next_taskgroup != child_task) + taskgroup->children = child_task->next_taskgroup; + else + { + taskgroup->children = NULL; + if (taskgroup->in_taskgroup_wait) + { + taskgroup->in_taskgroup_wait = false; + gomp_sem_post (&taskgroup->taskgroup_sem); + } + } +} + +void +gomp_barrier_handle_tasks (gomp_barrier_state_t state) +{ + struct gomp_thread *thr = gomp_thread (); + struct gomp_team *team = thr->ts.team; + struct gomp_task *task = thr->task; + struct gomp_task *child_task = NULL; + struct gomp_task *to_free = NULL; + int do_wake = 0; + + gomp_mutex_lock (&team->task_lock); + if (gomp_barrier_last_thread (state)) + { + if (team->task_count == 0) + { + gomp_team_barrier_done (&team->barrier, state); + gomp_mutex_unlock (&team->task_lock); + gomp_team_barrier_wake (&team->barrier, 0); + return; + } + gomp_team_barrier_set_waiting_for_tasks (&team->barrier); + } + + while (1) + { + bool cancelled = false; + if (team->task_queue != NULL) + { + child_task = team->task_queue; + cancelled = gomp_task_run_pre (child_task, child_task->parent, + child_task->taskgroup, team); + if (__builtin_expect (cancelled, 0)) + { + if (to_free) + { + gomp_finish_task (to_free); + free (to_free); + to_free = NULL; + } + goto finish_cancelled; + } + team->task_running_count++; + child_task->in_tied_task = true; + } + gomp_mutex_unlock (&team->task_lock); + if (do_wake) + { + gomp_team_barrier_wake (&team->barrier, do_wake); + do_wake = 0; + } + if (to_free) + { + gomp_finish_task (to_free); + free (to_free); + to_free = NULL; + } + if (child_task) + { + thr->task = child_task; + child_task->fn (child_task->fn_data); + thr->task = task; + } + else + return; + gomp_mutex_lock (&team->task_lock); + if (child_task) + { + finish_cancelled:; + size_t new_tasks + = gomp_task_run_post_handle_depend (child_task, team); + gomp_task_run_post_remove_parent (child_task); + gomp_clear_parent (child_task->children); + gomp_task_run_post_remove_taskgroup (child_task); + to_free = child_task; + child_task = NULL; + if (!cancelled) + team->task_running_count--; + if (new_tasks > 1) + { + do_wake = team->nthreads - team->task_running_count; + if (do_wake > new_tasks) + do_wake = new_tasks; + } + if (--team->task_count == 0 + && gomp_team_barrier_waiting_for_tasks (&team->barrier)) + { + gomp_team_barrier_done (&team->barrier, state); + gomp_mutex_unlock (&team->task_lock); + gomp_team_barrier_wake (&team->barrier, 0); + gomp_mutex_lock (&team->task_lock); + } + } + } +} + +/* Called when encountering a taskwait directive. */ + +void +GOMP_taskwait (void) +{ + struct gomp_thread *thr = gomp_thread (); + struct gomp_team *team = thr->ts.team; + struct gomp_task *task = thr->task; + struct gomp_task *child_task = NULL; + struct gomp_task *to_free = NULL; + struct gomp_taskwait taskwait; + int do_wake = 0; + + /* The acquire barrier on load of task->children here synchronizes + with the write of a NULL in gomp_task_run_post_remove_parent. It is + not necessary that we synchronize with other non-NULL writes at + this point, but we must ensure that all writes to memory by a + child thread task work function are seen before we exit from + GOMP_taskwait. */ + if (task == NULL + || __atomic_load_n (&task->children, MEMMODEL_ACQUIRE) == NULL) + return; + + memset (&taskwait, 0, sizeof (taskwait)); + gomp_mutex_lock (&team->task_lock); + while (1) + { + bool cancelled = false; + if (task->children == NULL) + { + bool destroy_taskwait = task->taskwait != NULL; + task->taskwait = NULL; + gomp_mutex_unlock (&team->task_lock); + if (to_free) + { + gomp_finish_task (to_free); + free (to_free); + } + if (destroy_taskwait) + gomp_sem_destroy (&taskwait.taskwait_sem); + return; + } + if (task->children->kind == GOMP_TASK_WAITING) + { + child_task = task->children; + cancelled + = gomp_task_run_pre (child_task, task, child_task->taskgroup, + team); + if (__builtin_expect (cancelled, 0)) + { + if (to_free) + { + gomp_finish_task (to_free); + free (to_free); + to_free = NULL; + } + goto finish_cancelled; + } + } + else + { + /* All tasks we are waiting for are already running + in other threads. Wait for them. */ + if (task->taskwait == NULL) + { + taskwait.in_depend_wait = false; + gomp_sem_init (&taskwait.taskwait_sem, 0); + task->taskwait = &taskwait; + } + taskwait.in_taskwait = true; + } + gomp_mutex_unlock (&team->task_lock); + if (do_wake) + { + gomp_team_barrier_wake (&team->barrier, do_wake); + do_wake = 0; + } + if (to_free) + { + gomp_finish_task (to_free); + free (to_free); + to_free = NULL; + } + if (child_task) + { + thr->task = child_task; + child_task->fn (child_task->fn_data); + thr->task = task; + } + else + gomp_sem_wait (&taskwait.taskwait_sem); + gomp_mutex_lock (&team->task_lock); + if (child_task) + { + finish_cancelled:; + size_t new_tasks + = gomp_task_run_post_handle_depend (child_task, team); + child_task->prev_child->next_child = child_task->next_child; + child_task->next_child->prev_child = child_task->prev_child; + if (task->children == child_task) + { + if (child_task->next_child != child_task) + task->children = child_task->next_child; + else + task->children = NULL; + } + gomp_clear_parent (child_task->children); + gomp_task_run_post_remove_taskgroup (child_task); + to_free = child_task; + child_task = NULL; + team->task_count--; + if (new_tasks > 1) + { + do_wake = team->nthreads - team->task_running_count + - !task->in_tied_task; + if (do_wake > new_tasks) + do_wake = new_tasks; + } + } + } +} + +/* This is like GOMP_taskwait, but we only wait for tasks that the + upcoming task depends on. */ + +static void +gomp_task_maybe_wait_for_dependencies (void **depend) +{ + struct gomp_thread *thr = gomp_thread (); + struct gomp_task *task = thr->task; + struct gomp_team *team = thr->ts.team; + struct gomp_task_depend_entry elem, *ent = NULL; + struct gomp_taskwait taskwait; + struct gomp_task *last_parent_depends_on = NULL; + size_t ndepend = (uintptr_t) depend[0]; + size_t nout = (uintptr_t) depend[1]; + size_t i; + size_t num_awaited = 0; + struct gomp_task *child_task = NULL; + struct gomp_task *to_free = NULL; + int do_wake = 0; + + gomp_mutex_lock (&team->task_lock); + for (i = 0; i < ndepend; i++) + { + elem.addr = depend[i + 2]; + ent = htab_find (task->depend_hash, &elem); + for (; ent; ent = ent->next) + if (i >= nout && ent->is_in) + continue; + else + { + struct gomp_task *tsk = ent->task; + if (!tsk->parent_depends_on) + { + tsk->parent_depends_on = true; + ++num_awaited; + if (tsk->num_dependees == 0 && tsk->kind == GOMP_TASK_WAITING) + { + /* If a task we need to wait for is not already + running and is ready to be scheduled, move it + to front, so that we run it as soon as possible. */ + if (last_parent_depends_on) + { + tsk->prev_child->next_child = tsk->next_child; + tsk->next_child->prev_child = tsk->prev_child; + tsk->prev_child = last_parent_depends_on; + tsk->next_child = last_parent_depends_on->next_child; + tsk->prev_child->next_child = tsk; + tsk->next_child->prev_child = tsk; + } + else if (tsk != task->children) + { + tsk->prev_child->next_child = tsk->next_child; + tsk->next_child->prev_child = tsk->prev_child; + tsk->prev_child = task->children; + tsk->next_child = task->children->next_child; + task->children = tsk; + tsk->prev_child->next_child = tsk; + tsk->next_child->prev_child = tsk; + } + last_parent_depends_on = tsk; + } + } + } + } + if (num_awaited == 0) + { + gomp_mutex_unlock (&team->task_lock); + return; + } + + memset (&taskwait, 0, sizeof (taskwait)); + taskwait.n_depend = num_awaited; + taskwait.last_parent_depends_on = last_parent_depends_on; + gomp_sem_init (&taskwait.taskwait_sem, 0); + task->taskwait = &taskwait; + + while (1) + { + bool cancelled = false; + if (taskwait.n_depend == 0) + { + task->taskwait = NULL; + gomp_mutex_unlock (&team->task_lock); + if (to_free) + { + gomp_finish_task (to_free); + free (to_free); + } + gomp_sem_destroy (&taskwait.taskwait_sem); + return; + } + if (task->children->kind == GOMP_TASK_WAITING) + { + child_task = task->children; + cancelled + = gomp_task_run_pre (child_task, task, child_task->taskgroup, + team); + if (__builtin_expect (cancelled, 0)) + { + if (to_free) + { + gomp_finish_task (to_free); + free (to_free); + to_free = NULL; + } + goto finish_cancelled; + } + } + else + /* All tasks we are waiting for are already running + in other threads. Wait for them. */ + taskwait.in_depend_wait = true; + gomp_mutex_unlock (&team->task_lock); + if (do_wake) + { + gomp_team_barrier_wake (&team->barrier, do_wake); + do_wake = 0; + } + if (to_free) + { + gomp_finish_task (to_free); + free (to_free); + to_free = NULL; + } + if (child_task) + { + thr->task = child_task; + child_task->fn (child_task->fn_data); + thr->task = task; + } + else + gomp_sem_wait (&taskwait.taskwait_sem); + gomp_mutex_lock (&team->task_lock); + if (child_task) + { + finish_cancelled:; + size_t new_tasks + = gomp_task_run_post_handle_depend (child_task, team); + if (child_task->parent_depends_on) + --taskwait.n_depend; + child_task->prev_child->next_child = child_task->next_child; + child_task->next_child->prev_child = child_task->prev_child; + if (task->children == child_task) + { + if (child_task->next_child != child_task) + task->children = child_task->next_child; + else + task->children = NULL; + } + gomp_clear_parent (child_task->children); + gomp_task_run_post_remove_taskgroup (child_task); + to_free = child_task; + child_task = NULL; + team->task_count--; + if (new_tasks > 1) + { + do_wake = team->nthreads - team->task_running_count + - !task->in_tied_task; + if (do_wake > new_tasks) + do_wake = new_tasks; + } + } + } +} + +/* Called when encountering a taskyield directive. */ + +void +GOMP_taskyield (void) +{ + /* Nothing at the moment. */ +} + +void +GOMP_taskgroup_start (void) +{ + struct gomp_thread *thr = gomp_thread (); + struct gomp_team *team = thr->ts.team; + struct gomp_task *task = thr->task; + struct gomp_taskgroup *taskgroup; + + /* If team is NULL, all tasks are executed as + GOMP_TASK_IFFALSE tasks and thus all children tasks of + taskgroup and their descendant tasks will be finished + by the time GOMP_taskgroup_end is called. */ + if (team == NULL) + return; + taskgroup = gomp_malloc (sizeof (struct gomp_taskgroup)); + taskgroup->prev = task->taskgroup; + taskgroup->children = NULL; + taskgroup->in_taskgroup_wait = false; + taskgroup->cancelled = false; + taskgroup->num_children = 0; + gomp_sem_init (&taskgroup->taskgroup_sem, 0); + task->taskgroup = taskgroup; +} + +void +GOMP_taskgroup_end (void) +{ + struct gomp_thread *thr = gomp_thread (); + struct gomp_team *team = thr->ts.team; + struct gomp_task *task = thr->task; + struct gomp_taskgroup *taskgroup; + struct gomp_task *child_task = NULL; + struct gomp_task *to_free = NULL; + int do_wake = 0; + + if (team == NULL) + return; + taskgroup = task->taskgroup; + + /* The acquire barrier on load of taskgroup->num_children here + synchronizes with the write of 0 in gomp_task_run_post_remove_taskgroup. + It is not necessary that we synchronize with other non-0 writes at + this point, but we must ensure that all writes to memory by a + child thread task work function are seen before we exit from + GOMP_taskgroup_end. */ + if (__atomic_load_n (&taskgroup->num_children, MEMMODEL_ACQUIRE) == 0) + goto finish; + + gomp_mutex_lock (&team->task_lock); + while (1) + { + bool cancelled = false; + if (taskgroup->children == NULL) + { + if (taskgroup->num_children) + { + if (task->children == NULL) + goto do_wait; + child_task = task->children; + } + else + { + gomp_mutex_unlock (&team->task_lock); + if (to_free) + { + gomp_finish_task (to_free); + free (to_free); + } + goto finish; + } + } + else + child_task = taskgroup->children; + if (child_task->kind == GOMP_TASK_WAITING) + { + cancelled + = gomp_task_run_pre (child_task, child_task->parent, taskgroup, + team); + if (__builtin_expect (cancelled, 0)) + { + if (to_free) + { + gomp_finish_task (to_free); + free (to_free); + to_free = NULL; + } + goto finish_cancelled; + } + } + else + { + child_task = NULL; + do_wait: + /* All tasks we are waiting for are already running + in other threads. Wait for them. */ + taskgroup->in_taskgroup_wait = true; + } + gomp_mutex_unlock (&team->task_lock); + if (do_wake) + { + gomp_team_barrier_wake (&team->barrier, do_wake); + do_wake = 0; + } + if (to_free) + { + gomp_finish_task (to_free); + free (to_free); + to_free = NULL; + } + if (child_task) + { + thr->task = child_task; + child_task->fn (child_task->fn_data); + thr->task = task; + } + else + gomp_sem_wait (&taskgroup->taskgroup_sem); + gomp_mutex_lock (&team->task_lock); + if (child_task) + { + finish_cancelled:; + size_t new_tasks + = gomp_task_run_post_handle_depend (child_task, team); + gomp_task_run_post_remove_parent (child_task); + gomp_clear_parent (child_task->children); + gomp_task_run_post_remove_taskgroup (child_task); + to_free = child_task; + child_task = NULL; + team->task_count--; + if (new_tasks > 1) + { + do_wake = team->nthreads - team->task_running_count + - !task->in_tied_task; + if (do_wake > new_tasks) + do_wake = new_tasks; + } + } + } + + finish: + task->taskgroup = taskgroup->prev; + gomp_sem_destroy (&taskgroup->taskgroup_sem); + free (taskgroup); +} + +int +omp_in_final (void) +{ + struct gomp_thread *thr = gomp_thread (); + return thr->task && thr->task->final_task; +} + +ialias (omp_in_final) diff --git a/hermit/usr/libgomp/team.c b/hermit/usr/libgomp/team.c new file mode 100644 index 000000000..b98b23374 --- /dev/null +++ b/hermit/usr/libgomp/team.c @@ -0,0 +1,948 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* This file handles the maintainence of threads in response to team + creation and termination. */ + +#include "libgomp.h" +#include +#include + +/* 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; +#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; +#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; + 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); +#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) +{ + /* 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; +} diff --git a/hermit/usr/libgomp/work.c b/hermit/usr/libgomp/work.c new file mode 100644 index 000000000..0570b90c7 --- /dev/null +++ b/hermit/usr/libgomp/work.c @@ -0,0 +1,297 @@ +/* Copyright (C) 2005-2015 Free Software Foundation, Inc. + Contributed by Richard Henderson . + + 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 + . */ + +/* This file contains routines to manage the work-share queue for a team + of threads. */ + +#include "libgomp.h" +#include +#include +#include + + +/* 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; +}