Skip to content

Commit ea174c0

Browse files
authored
[Libomptarget] Remove global ctor and use reference counting (#80499)
Summary: Currently we rely on global constructors to initialize and shut down the OpenMP runtime library and plugin manager. This causes some issues because we do not have a defined lifetime that we can rely on to release and allocate resources. This patch instead adds some simple reference counted initialization and deinitialization function. A future patch will use the `deinit` interface to more intelligently handle plugin deinitilization. Right now we do nothing and rely on `atexit` inside of the plugins to tear them down. This isn't great because it limits our ability to control these things. Note that I made the `__tgt_register_lib` functions do the initialization instead of adding calls to the new runtime functions in the linker wrapper. The reason for this is because in the past it's been easier to not introduce a new function call, since sometimes the user's compiler will link against an older `libomptarget`. Maybe if we change the name with offloading in the future we can simplify this. Depends on #80460
1 parent e630a45 commit ea174c0

File tree

7 files changed

+89
-15
lines changed

7 files changed

+89
-15
lines changed

openmp/libomptarget/include/PluginManager.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ struct PluginManager {
206206
ProtectedObj<DeviceContainerTy> Devices;
207207
};
208208

209+
/// Initialize the plugin manager and OpenMP runtime.
210+
void initRuntime();
211+
212+
/// Deinitialize the plugin and delete it.
213+
void deinitRuntime();
214+
209215
extern PluginManager *PM;
210216

211217
#endif // OMPTARGET_PLUGIN_MANAGER_H

openmp/libomptarget/include/omptarget.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,12 @@ void *llvm_omp_target_dynamic_shared_alloc();
312312
/// add the clauses of the requires directives in a given file
313313
void __tgt_register_requires(int64_t Flags);
314314

315+
/// Initializes the runtime library.
316+
void __tgt_rtl_init();
317+
318+
/// Deinitializes the runtime library.
319+
void __tgt_rtl_deinit();
320+
315321
/// adds a target shared library to the target execution image
316322
void __tgt_register_lib(__tgt_bin_desc *Desc);
317323

openmp/libomptarget/src/OffloadRTL.cpp

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,39 @@
2020
extern void llvm::omp::target::ompt::connectLibrary();
2121
#endif
2222

23-
__attribute__((constructor(101))) void init() {
23+
static std::mutex PluginMtx;
24+
static uint32_t RefCount = 0;
25+
26+
void initRuntime() {
27+
std::scoped_lock<decltype(PluginMtx)> Lock(PluginMtx);
2428
Profiler::get();
2529
TIMESCOPE();
2630

27-
DP("Init offload library!\n");
28-
29-
PM = new PluginManager();
31+
if (PM == nullptr)
32+
PM = new PluginManager();
3033

34+
RefCount++;
35+
if (RefCount == 1) {
36+
DP("Init offload library!\n");
3137
#ifdef OMPT_SUPPORT
32-
// Initialize OMPT first
33-
llvm::omp::target::ompt::connectLibrary();
38+
// Initialize OMPT first
39+
llvm::omp::target::ompt::connectLibrary();
3440
#endif
3541

36-
PM->init();
37-
38-
PM->registerDelayedLibraries();
42+
PM->init();
43+
PM->registerDelayedLibraries();
44+
}
3945
}
4046

41-
__attribute__((destructor(101))) void deinit() {
42-
DP("Deinit offload library!\n");
43-
delete PM;
47+
void deinitRuntime() {
48+
std::scoped_lock<decltype(PluginMtx)> Lock(PluginMtx);
49+
assert(PM && "Runtime not initialized");
50+
51+
if (RefCount == 1) {
52+
DP("Deinit offload library!\n");
53+
delete PM;
54+
PM = nullptr;
55+
}
56+
57+
RefCount--;
4458
}

openmp/libomptarget/src/PluginManager.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
using namespace llvm;
2222
using namespace llvm::sys;
2323

24-
PluginManager *PM;
24+
PluginManager *PM = nullptr;
2525

2626
// List of all plugins that can support offloading.
2727
static const char *RTLNames[] = {ENABLED_OFFLOAD_PLUGINS};

openmp/libomptarget/src/exports

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
VERS1.0 {
22
global:
3+
__tgt_rtl_init;
4+
__tgt_rtl_deinit;
35
__tgt_register_requires;
46
__tgt_register_lib;
57
__tgt_unregister_lib;

openmp/libomptarget/src/interface.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,13 @@ EXTERN void __tgt_register_requires(int64_t Flags) {
3838
__PRETTY_FUNCTION__);
3939
}
4040

41+
EXTERN void __tgt_rtl_init() { initRuntime(); }
42+
EXTERN void __tgt_rtl_deinit() { deinitRuntime(); }
43+
4144
////////////////////////////////////////////////////////////////////////////////
4245
/// adds a target shared library to the target execution image
4346
EXTERN void __tgt_register_lib(__tgt_bin_desc *Desc) {
47+
initRuntime();
4448
if (PM->delayRegisterLib(Desc))
4549
return;
4650

@@ -49,12 +53,17 @@ EXTERN void __tgt_register_lib(__tgt_bin_desc *Desc) {
4953

5054
////////////////////////////////////////////////////////////////////////////////
5155
/// Initialize all available devices without registering any image
52-
EXTERN void __tgt_init_all_rtls() { PM->initAllPlugins(); }
56+
EXTERN void __tgt_init_all_rtls() {
57+
assert(PM && "Runtime not initialized");
58+
PM->initAllPlugins();
59+
}
5360

5461
////////////////////////////////////////////////////////////////////////////////
5562
/// unloads a target shared library
5663
EXTERN void __tgt_unregister_lib(__tgt_bin_desc *Desc) {
5764
PM->unregisterLib(Desc);
65+
66+
deinitRuntime();
5867
}
5968

6069
template <typename TargetAsyncInfoTy>
@@ -64,6 +73,7 @@ targetData(ident_t *Loc, int64_t DeviceId, int32_t ArgNum, void **ArgsBase,
6473
map_var_info_t *ArgNames, void **ArgMappers,
6574
TargetDataFuncPtrTy TargetDataFunction, const char *RegionTypeMsg,
6675
const char *RegionName) {
76+
assert(PM && "Runtime not initialized");
6777
static_assert(std::is_convertible_v<TargetAsyncInfoTy, AsyncInfoTy>,
6878
"TargetAsyncInfoTy must be convertible to AsyncInfoTy.");
6979

@@ -239,6 +249,7 @@ template <typename TargetAsyncInfoTy>
239249
static inline int targetKernel(ident_t *Loc, int64_t DeviceId, int32_t NumTeams,
240250
int32_t ThreadLimit, void *HostPtr,
241251
KernelArgsTy *KernelArgs) {
252+
assert(PM && "Runtime not initialized");
242253
static_assert(std::is_convertible_v<TargetAsyncInfoTy, AsyncInfoTy>,
243254
"Target AsyncInfoTy must be convertible to AsyncInfoTy.");
244255
DP("Entering target region for device %" PRId64 " with entry point " DPxMOD
@@ -345,6 +356,7 @@ EXTERN int __tgt_activate_record_replay(int64_t DeviceId, uint64_t MemorySize,
345356
void *VAddr, bool IsRecord,
346357
bool SaveOutput,
347358
uint64_t &ReqPtrArgOffset) {
359+
assert(PM && "Runtime not initialized");
348360
OMPT_IF_BUILT(ReturnAddressSetterRAII RA(__builtin_return_address(0)));
349361
auto DeviceOrErr = PM->getDevice(DeviceId);
350362
if (!DeviceOrErr)
@@ -380,7 +392,7 @@ EXTERN int __tgt_target_kernel_replay(ident_t *Loc, int64_t DeviceId,
380392
ptrdiff_t *TgtOffsets, int32_t NumArgs,
381393
int32_t NumTeams, int32_t ThreadLimit,
382394
uint64_t LoopTripCount) {
383-
395+
assert(PM && "Runtime not initialized");
384396
OMPT_IF_BUILT(ReturnAddressSetterRAII RA(__builtin_return_address(0)));
385397
if (checkDeviceAndCtors(DeviceId, Loc)) {
386398
DP("Not offloading to device %" PRId64 "\n", DeviceId);
@@ -431,6 +443,7 @@ EXTERN void __tgt_push_mapper_component(void *RtMapperHandle, void *Base,
431443
}
432444

433445
EXTERN void __tgt_set_info_flag(uint32_t NewInfoLevel) {
446+
assert(PM && "Runtime not initialized");
434447
std::atomic<uint32_t> &InfoLevel = getInfoLevelInternal();
435448
InfoLevel.store(NewInfoLevel);
436449
for (auto &R : PM->pluginAdaptors()) {
@@ -440,6 +453,7 @@ EXTERN void __tgt_set_info_flag(uint32_t NewInfoLevel) {
440453
}
441454

442455
EXTERN int __tgt_print_device_info(int64_t DeviceId) {
456+
assert(PM && "Runtime not initialized");
443457
auto DeviceOrErr = PM->getDevice(DeviceId);
444458
if (!DeviceOrErr)
445459
FATAL_MESSAGE(DeviceId, "%s", toString(DeviceOrErr.takeError()).c_str());
@@ -448,7 +462,9 @@ EXTERN int __tgt_print_device_info(int64_t DeviceId) {
448462
}
449463

450464
EXTERN void __tgt_target_nowait_query(void **AsyncHandle) {
465+
assert(PM && "Runtime not initialized");
451466
OMPT_IF_BUILT(ReturnAddressSetterRAII RA(__builtin_return_address(0)));
467+
452468
if (!AsyncHandle || !*AsyncHandle) {
453469
FATAL_MESSAGE0(
454470
1, "Receive an invalid async handle from the current OpenMP task. Is "
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %libomptarget-compile-generic
2+
// RUN: env LIBOMPTARGET_DEBUG=1 %libomptarget-run-generic 2>&1 \
3+
// RUN: %fcheck-generic
4+
5+
// REQUIRES: libomptarget-debug
6+
7+
#include <omp.h>
8+
#include <stdio.h>
9+
10+
extern void __tgt_rtl_init(void);
11+
extern void __tgt_rtl_deinit(void);
12+
13+
// Sanity checks to make sure that this works and is thread safe.
14+
int main() {
15+
// CHECK: Init offload library!
16+
// CHECK: Deinit offload library!
17+
__tgt_rtl_init();
18+
#pragma omp parallel num_threads(8)
19+
{
20+
__tgt_rtl_init();
21+
__tgt_rtl_deinit();
22+
}
23+
__tgt_rtl_deinit();
24+
25+
__tgt_rtl_init();
26+
__tgt_rtl_deinit();
27+
28+
// CHECK: PASS
29+
printf("PASS\n");
30+
}

0 commit comments

Comments
 (0)