1 //===------- dlwrap.h - Convenience wrapper around dlopen/dlsym  -- 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 // The openmp plugins depend on extern libraries. These can be used via:
10 //  - bitcode file statically linked
11 //  - (relocatable) object file statically linked
12 //  - static library
13 //  - dynamic library, linked at build time
14 //  - dynamic library, loaded at application run time by dlopen
15 //
16 // This file factors out most boilerplate for using a dlopened library.
17 // - Function symbols are generated that are statically linked against
18 // - The dlopen can be done implicitly when initializing the library
19 // - dlsym lookups are done once and cached
20 // - The abstraction is very thin to permit varied uses of the library
21 //
22 // Given int foo(char, double, void*);, writing DLWRAP(foo, 3) will expand to:
23 // int foo(char x0, double x1, void* x2) {
24 //   constexpr size_t index = id();
25 //   void * dlsymResult = pointer(index);
26 //   return ((int (*)(char, double, void*))dlsymResult)(x0, x1, x2);
27 // }
28 //
29 // Multiple calls to DLWRAP(symbol_name, arity) with bespoke
30 // initialization code that can use the thin abstraction:
31 // namespace dlwrap {
32 //   static size_t size();
33 //   static const char *symbol(size_t);
34 //   static void **pointer(size_t);
35 // }
36 // will compile to an object file that only exposes the symbols that the
37 // dynamic library would do, with the right function types.
38 //
39 //===----------------------------------------------------------------------===//
40 
41 #ifndef DLWRAP_H_INCLUDED
42 #define DLWRAP_H_INCLUDED
43 
44 #include <array>
45 #include <cstddef>
46 #include <tuple>
47 #include <type_traits>
48 
49 // Where symbol is a function, these expand to some book keeping and an
50 // implementation of that function
51 #define DLWRAP(SYMBOL, ARITY) DLWRAP_IMPL(SYMBOL, ARITY)
52 #define DLWRAP_INTERNAL(SYMBOL, ARITY) DLWRAP_INTERNAL_IMPL(SYMBOL, ARITY)
53 
54 // For example, given a prototype:
55 // int foo(char, double);
56 //
57 // DLWRAP(foo, 2) expands to:
58 //
59 // namespace dlwrap {
60 // struct foo_Trait : public dlwrap::trait<decltype(&foo)> {
61 //   using T = dlwrap::trait<decltype(&foo)>;
62 //   static T::FunctionType get() {
63 //     constexpr size_t Index = getIndex();
64 //     void *P = *dlwrap::pointer(Index);
65 //     return reinterpret_cast<T::FunctionType>(P);
66 //   }
67 // };
68 // }
69 // int foo(char x0, double x1) { return dlwrap::foo_Trait::get()(x0, x1); }
70 //
71 // DLWRAP_INTERNAL is similar, except the function it expands to is:
72 // static int dlwrap_foo(char x0, double x1) { ... }
73 // so that the function pointer call can be wrapped in library-specific code
74 
75 // DLWRAP_FINALIZE() expands to definitions of:
76 #define DLWRAP_FINALIZE() DLWRAP_FINALIZE_IMPL()
77 namespace dlwrap {
78 static size_t size();
79 static const char *symbol(size_t); // get symbol name in [0, size())
80 static void **pointer(size_t); // get pointer to function pointer in [0, size())
81 } // namespace dlwrap
82 
83 // Implementation details follow.
84 
85 namespace dlwrap {
86 
87 // Extract return / argument types from address of function symbol
88 template <typename F> struct trait;
89 template <typename R, typename... Ts> struct trait<R (*)(Ts...)> {
90   constexpr static const size_t nargs = sizeof...(Ts);
91   typedef R ReturnType;
92   template <size_t i> struct arg {
93     typedef typename std::tuple_element<i, std::tuple<Ts...>>::type type;
94   };
95 
96   typedef R (*FunctionType)(Ts...);
97 };
98 
99 namespace type {
100 // Book keeping is by type specialization
101 
102 template <size_t S> struct count {
103   static constexpr size_t N = count<S - 1>::N;
104 };
105 
106 template <> struct count<0> { static constexpr size_t N = 0; };
107 
108 // Get a constexpr size_t ID, starts at zero
109 #define DLWRAP_ID() (dlwrap::type::count<__LINE__>::N)
110 
111 // Increment value returned by DLWRAP_ID
112 #define DLWRAP_INC()                                                           \
113   template <> struct dlwrap::type::count<__LINE__> {                           \
114     static constexpr size_t N = 1 + dlwrap::type::count<__LINE__ - 1>::N;      \
115   }
116 
117 template <size_t N> struct symbol;
118 #define DLWRAP_SYMBOL(SYMBOL, ID)                                              \
119   template <> struct dlwrap::type::symbol<ID> {                                \
120     static constexpr const char *call() { return #SYMBOL; }                    \
121   }
122 } // namespace type
123 
124 template <size_t N, size_t... Is>
125 constexpr std::array<const char *, N> static getSymbolArray(
126     std::index_sequence<Is...>) {
127   return {{dlwrap::type::symbol<Is>::call()...}};
128 }
129 
130 } // namespace dlwrap
131 
132 #define DLWRAP_INSTANTIATE(SYM_USE, SYM_DEF, ARITY)                            \
133   DLWRAP_INSTANTIATE_##ARITY(SYM_USE, SYM_DEF,                                 \
134                              dlwrap::trait<decltype(&SYM_USE)>)
135 
136 #define DLWRAP_FINALIZE_IMPL()                                                 \
137   static size_t dlwrap::size() { return DLWRAP_ID(); }                         \
138   static const char *dlwrap::symbol(size_t i) {                                \
139     static constexpr const std::array<const char *, DLWRAP_ID()>               \
140         dlwrap_symbols = getSymbolArray<DLWRAP_ID()>(                          \
141             std::make_index_sequence<DLWRAP_ID()>());                          \
142     return dlwrap_symbols[i];                                                  \
143   }                                                                            \
144   static void **dlwrap::pointer(size_t i) {                                    \
145     static std::array<void *, DLWRAP_ID()> dlwrap_pointers;                    \
146     return &dlwrap_pointers.data()[i];                                         \
147   }
148 
149 #define DLWRAP_COMMON(SYMBOL, ARITY)                                           \
150   DLWRAP_INC();                                                                \
151   DLWRAP_SYMBOL(SYMBOL, DLWRAP_ID() - 1);                                      \
152   namespace dlwrap {                                                           \
153   struct SYMBOL##_Trait : public dlwrap::trait<decltype(&SYMBOL)> {            \
154     using T = dlwrap::trait<decltype(&SYMBOL)>;                                \
155     static T::FunctionType get() {                                             \
156       constexpr size_t Index = DLWRAP_ID() - 1;                                \
157       void *P = *dlwrap::pointer(Index);                                       \
158       return reinterpret_cast<T::FunctionType>(P);                             \
159     }                                                                          \
160   };                                                                           \
161   }
162 
163 #define DLWRAP_IMPL(SYMBOL, ARITY)                                             \
164   DLWRAP_COMMON(SYMBOL, ARITY);                                                \
165   DLWRAP_INSTANTIATE(SYMBOL, SYMBOL, ARITY)
166 
167 #define DLWRAP_INTERNAL_IMPL(SYMBOL, ARITY)                                    \
168   DLWRAP_COMMON(SYMBOL, ARITY);                                                \
169   static DLWRAP_INSTANTIATE(SYMBOL, dlwrap_##SYMBOL, ARITY)
170 
171 #define DLWRAP_INSTANTIATE_0(SYM_USE, SYM_DEF, T)                              \
172   T::ReturnType SYM_DEF() { return dlwrap::SYM_USE##_Trait::get()(); }
173 #define DLWRAP_INSTANTIATE_1(SYM_USE, SYM_DEF, T)                              \
174   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0) {                \
175     return dlwrap::SYM_USE##_Trait::get()(x0);                                 \
176   }
177 #define DLWRAP_INSTANTIATE_2(SYM_USE, SYM_DEF, T)                              \
178   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
179                         typename T::template arg<1>::type x1) {                \
180     return dlwrap::SYM_USE##_Trait::get()(x0, x1);                             \
181   }
182 #define DLWRAP_INSTANTIATE_3(SYM_USE, SYM_DEF, T)                              \
183   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
184                         typename T::template arg<1>::type x1,                  \
185                         typename T::template arg<2>::type x2) {                \
186     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2);                         \
187   }
188 #define DLWRAP_INSTANTIATE_4(SYM_USE, SYM_DEF, T)                              \
189   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
190                         typename T::template arg<1>::type x1,                  \
191                         typename T::template arg<2>::type x2,                  \
192                         typename T::template arg<3>::type x3) {                \
193     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3);                     \
194   }
195 #define DLWRAP_INSTANTIATE_5(SYM_USE, SYM_DEF, T)                              \
196   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
197                         typename T::template arg<1>::type x1,                  \
198                         typename T::template arg<2>::type x2,                  \
199                         typename T::template arg<3>::type x3,                  \
200                         typename T::template arg<4>::type x4) {                \
201     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4);                 \
202   }
203 #define DLWRAP_INSTANTIATE_6(SYM_USE, SYM_DEF, T)                              \
204   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
205                         typename T::template arg<1>::type x1,                  \
206                         typename T::template arg<2>::type x2,                  \
207                         typename T::template arg<3>::type x3,                  \
208                         typename T::template arg<4>::type x4,                  \
209                         typename T::template arg<5>::type x5) {                \
210     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4, x5);             \
211   }
212 
213 #define DLWRAP_INSTANTIATE_7(SYM_USE, SYM_DEF, T)                              \
214   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
215                         typename T::template arg<1>::type x1,                  \
216                         typename T::template arg<2>::type x2,                  \
217                         typename T::template arg<3>::type x3,                  \
218                         typename T::template arg<4>::type x4,                  \
219                         typename T::template arg<5>::type x5,                  \
220                         typename T::template arg<6>::type x6) {                \
221     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4, x5, x6);         \
222   }
223 
224 #define DLWRAP_INSTANTIATE_8(SYM_USE, SYM_DEF, T)                              \
225   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
226                         typename T::template arg<1>::type x1,                  \
227                         typename T::template arg<2>::type x2,                  \
228                         typename T::template arg<3>::type x3,                  \
229                         typename T::template arg<4>::type x4,                  \
230                         typename T::template arg<5>::type x5,                  \
231                         typename T::template arg<6>::type x6,                  \
232                         typename T::template arg<7>::type x7) {                \
233     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4, x5, x6, x7);     \
234   }
235 #define DLWRAP_INSTANTIATE_9(SYM_USE, SYM_DEF, T)                              \
236   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
237                         typename T::template arg<1>::type x1,                  \
238                         typename T::template arg<2>::type x2,                  \
239                         typename T::template arg<3>::type x3,                  \
240                         typename T::template arg<4>::type x4,                  \
241                         typename T::template arg<5>::type x5,                  \
242                         typename T::template arg<6>::type x6,                  \
243                         typename T::template arg<7>::type x7,                  \
244                         typename T::template arg<8>::type x8) {                \
245     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4, x5, x6, x7, x8); \
246   }
247 #define DLWRAP_INSTANTIATE_10(SYM_USE, SYM_DEF, T)                             \
248   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
249                         typename T::template arg<1>::type x1,                  \
250                         typename T::template arg<2>::type x2,                  \
251                         typename T::template arg<3>::type x3,                  \
252                         typename T::template arg<4>::type x4,                  \
253                         typename T::template arg<5>::type x5,                  \
254                         typename T::template arg<6>::type x6,                  \
255                         typename T::template arg<7>::type x7,                  \
256                         typename T::template arg<8>::type x8,                  \
257                         typename T::template arg<9>::type x9) {                \
258     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4, x5, x6, x7, x8,  \
259                                           x9);                                 \
260   }
261 #define DLWRAP_INSTANTIATE_11(SYM_USE, SYM_DEF, T)                             \
262   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
263                         typename T::template arg<1>::type x1,                  \
264                         typename T::template arg<2>::type x2,                  \
265                         typename T::template arg<3>::type x3,                  \
266                         typename T::template arg<4>::type x4,                  \
267                         typename T::template arg<5>::type x5,                  \
268                         typename T::template arg<6>::type x6,                  \
269                         typename T::template arg<7>::type x7,                  \
270                         typename T::template arg<8>::type x8,                  \
271                         typename T::template arg<9>::type x9,                  \
272                         typename T::template arg<10>::type x10) {              \
273     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4, x5, x6, x7, x8,  \
274                                           x9, x10);                            \
275   }
276 
277 #endif
278