1 //===--- Kernel.cpp - OpenMP device kernel interface -------------- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file contains the kernel entry points for the device.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "Debug.h"
14 #include "Interface.h"
15 #include "Mapping.h"
16 #include "State.h"
17 #include "Synchronization.h"
18 #include "Types.h"
19 
20 using namespace _OMP;
21 
22 #pragma omp begin declare target device_type(nohost)
23 
24 static void inititializeRuntime(bool IsSPMD) {
25   // Order is important here.
26   synchronize::init(IsSPMD);
27   mapping::init(IsSPMD);
28   state::init(IsSPMD);
29 }
30 
31 /// Simple generic state machine for worker threads.
32 static void genericStateMachine(IdentTy *Ident) {
33   FunctionTracingRAII();
34 
35   uint32_t TId = mapping::getThreadIdInBlock();
36 
37   do {
38     ParallelRegionFnTy WorkFn = 0;
39 
40     // Wait for the signal that we have a new work function.
41     synchronize::threads();
42 
43     // Retrieve the work function from the runtime.
44     bool IsActive = __kmpc_kernel_parallel(&WorkFn);
45 
46     // If there is nothing more to do, break out of the state machine by
47     // returning to the caller.
48     if (!WorkFn)
49       return;
50 
51     if (IsActive) {
52       ASSERT(!mapping::isSPMDMode());
53       ((void (*)(uint32_t, uint32_t))WorkFn)(0, TId);
54       __kmpc_kernel_end_parallel();
55     }
56 
57     synchronize::threads();
58 
59   } while (true);
60 }
61 
62 extern "C" {
63 
64 /// Initialization
65 ///
66 /// \param Ident               Source location identification, can be NULL.
67 ///
68 int32_t __kmpc_target_init(IdentTy *Ident, int8_t Mode,
69                            bool UseGenericStateMachine, bool) {
70   FunctionTracingRAII();
71   const bool IsSPMD = Mode & OMP_TGT_EXEC_MODE_SPMD;
72   if (IsSPMD) {
73     inititializeRuntime(/* IsSPMD */ true);
74     synchronize::threadsAligned();
75   } else {
76     inititializeRuntime(/* IsSPMD */ false);
77     // No need to wait since only the main threads will execute user
78     // code and workers will run into a barrier right away.
79   }
80 
81   if (IsSPMD) {
82     state::assumeInitialState(IsSPMD);
83     return -1;
84   }
85 
86   if (mapping::isInitialThreadInLevel0(IsSPMD))
87     return -1;
88 
89   // Enter the generic state machine if enabled and if this thread can possibly
90   // be an active worker thread.
91   //
92   // The latter check is important for NVIDIA Pascal (but not Volta) and AMD
93   // GPU.  In those cases, a single thread can apparently satisfy a barrier on
94   // behalf of all threads in the same warp.  Thus, it would not be safe for
95   // other threads in the main thread's warp to reach the first
96   // synchronize::threads call in genericStateMachine before the main thread
97   // reaches its corresponding synchronize::threads call: that would permit all
98   // active worker threads to proceed before the main thread has actually set
99   // state::ParallelRegionFn, and then they would immediately quit without
100   // doing any work.  mapping::getBlockSize() does not include any of the main
101   // thread's warp, so none of its threads can ever be active worker threads.
102   if (UseGenericStateMachine &&
103       mapping::getThreadIdInBlock() < mapping::getBlockSize(IsSPMD))
104     genericStateMachine(Ident);
105 
106   return mapping::getThreadIdInBlock();
107 }
108 
109 /// De-Initialization
110 ///
111 /// In non-SPMD, this function releases the workers trapped in a state machine
112 /// and also any memory dynamically allocated by the runtime.
113 ///
114 /// \param Ident Source location identification, can be NULL.
115 ///
116 void __kmpc_target_deinit(IdentTy *Ident, int8_t Mode, bool) {
117   FunctionTracingRAII();
118   const bool IsSPMD = Mode & OMP_TGT_EXEC_MODE_SPMD;
119   state::assumeInitialState(IsSPMD);
120   if (IsSPMD)
121     return;
122 
123   // Signal the workers to exit the state machine and exit the kernel.
124   state::ParallelRegionFn = nullptr;
125 }
126 
127 int8_t __kmpc_is_spmd_exec_mode() {
128   FunctionTracingRAII();
129   return mapping::isSPMDMode();
130 }
131 }
132 
133 #pragma omp end declare target
134