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 template <size_t Requested, size_t Required> constexpr void verboseAssert() {
131   static_assert(Requested == Required, "Arity Error");
132 }
133 
134 } // namespace dlwrap
135 
136 #define DLWRAP_INSTANTIATE(SYM_USE, SYM_DEF, ARITY)                            \
137   DLWRAP_INSTANTIATE_##ARITY(SYM_USE, SYM_DEF,                                 \
138                              dlwrap::trait<decltype(&SYM_USE)>)
139 
140 #define DLWRAP_FINALIZE_IMPL()                                                 \
141   static size_t dlwrap::size() { return DLWRAP_ID(); }                         \
142   static const char *dlwrap::symbol(size_t i) {                                \
143     static constexpr const std::array<const char *, DLWRAP_ID()>               \
144         dlwrap_symbols = getSymbolArray<DLWRAP_ID()>(                          \
145             std::make_index_sequence<DLWRAP_ID()>());                          \
146     return dlwrap_symbols[i];                                                  \
147   }                                                                            \
148   static void **dlwrap::pointer(size_t i) {                                    \
149     static std::array<void *, DLWRAP_ID()> dlwrap_pointers;                    \
150     return &dlwrap_pointers.data()[i];                                         \
151   }
152 
153 #define DLWRAP_COMMON(SYMBOL, ARITY)                                           \
154   DLWRAP_INC();                                                                \
155   DLWRAP_SYMBOL(SYMBOL, DLWRAP_ID() - 1);                                      \
156   namespace dlwrap {                                                           \
157   struct SYMBOL##_Trait : public dlwrap::trait<decltype(&SYMBOL)> {            \
158     using T = dlwrap::trait<decltype(&SYMBOL)>;                                \
159     static T::FunctionType get() {                                             \
160       verboseAssert<ARITY, trait<decltype(&SYMBOL)>::nargs>();                 \
161       constexpr size_t Index = DLWRAP_ID() - 1;                                \
162       void *P = *dlwrap::pointer(Index);                                       \
163       return reinterpret_cast<T::FunctionType>(P);                             \
164     }                                                                          \
165   };                                                                           \
166   }
167 
168 #define DLWRAP_IMPL(SYMBOL, ARITY)                                             \
169   DLWRAP_COMMON(SYMBOL, ARITY);                                                \
170   DLWRAP_INSTANTIATE(SYMBOL, SYMBOL, ARITY)
171 
172 #define DLWRAP_INTERNAL_IMPL(SYMBOL, ARITY)                                    \
173   DLWRAP_COMMON(SYMBOL, ARITY);                                                \
174   static DLWRAP_INSTANTIATE(SYMBOL, dlwrap_##SYMBOL, ARITY)
175 
176 #define DLWRAP_INSTANTIATE_0(SYM_USE, SYM_DEF, T)                              \
177   T::ReturnType SYM_DEF() { return dlwrap::SYM_USE##_Trait::get()(); }
178 #define DLWRAP_INSTANTIATE_1(SYM_USE, SYM_DEF, T)                              \
179   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0) {                \
180     return dlwrap::SYM_USE##_Trait::get()(x0);                                 \
181   }
182 #define DLWRAP_INSTANTIATE_2(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     return dlwrap::SYM_USE##_Trait::get()(x0, x1);                             \
186   }
187 #define DLWRAP_INSTANTIATE_3(SYM_USE, SYM_DEF, T)                              \
188   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
189                         typename T::template arg<1>::type x1,                  \
190                         typename T::template arg<2>::type x2) {                \
191     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2);                         \
192   }
193 #define DLWRAP_INSTANTIATE_4(SYM_USE, SYM_DEF, T)                              \
194   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
195                         typename T::template arg<1>::type x1,                  \
196                         typename T::template arg<2>::type x2,                  \
197                         typename T::template arg<3>::type x3) {                \
198     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3);                     \
199   }
200 #define DLWRAP_INSTANTIATE_5(SYM_USE, SYM_DEF, T)                              \
201   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
202                         typename T::template arg<1>::type x1,                  \
203                         typename T::template arg<2>::type x2,                  \
204                         typename T::template arg<3>::type x3,                  \
205                         typename T::template arg<4>::type x4) {                \
206     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4);                 \
207   }
208 #define DLWRAP_INSTANTIATE_6(SYM_USE, SYM_DEF, T)                              \
209   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
210                         typename T::template arg<1>::type x1,                  \
211                         typename T::template arg<2>::type x2,                  \
212                         typename T::template arg<3>::type x3,                  \
213                         typename T::template arg<4>::type x4,                  \
214                         typename T::template arg<5>::type x5) {                \
215     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4, x5);             \
216   }
217 
218 #define DLWRAP_INSTANTIATE_7(SYM_USE, SYM_DEF, T)                              \
219   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
220                         typename T::template arg<1>::type x1,                  \
221                         typename T::template arg<2>::type x2,                  \
222                         typename T::template arg<3>::type x3,                  \
223                         typename T::template arg<4>::type x4,                  \
224                         typename T::template arg<5>::type x5,                  \
225                         typename T::template arg<6>::type x6) {                \
226     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4, x5, x6);         \
227   }
228 
229 #define DLWRAP_INSTANTIATE_8(SYM_USE, SYM_DEF, T)                              \
230   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
231                         typename T::template arg<1>::type x1,                  \
232                         typename T::template arg<2>::type x2,                  \
233                         typename T::template arg<3>::type x3,                  \
234                         typename T::template arg<4>::type x4,                  \
235                         typename T::template arg<5>::type x5,                  \
236                         typename T::template arg<6>::type x6,                  \
237                         typename T::template arg<7>::type x7) {                \
238     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4, x5, x6, x7);     \
239   }
240 #define DLWRAP_INSTANTIATE_9(SYM_USE, SYM_DEF, T)                              \
241   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
242                         typename T::template arg<1>::type x1,                  \
243                         typename T::template arg<2>::type x2,                  \
244                         typename T::template arg<3>::type x3,                  \
245                         typename T::template arg<4>::type x4,                  \
246                         typename T::template arg<5>::type x5,                  \
247                         typename T::template arg<6>::type x6,                  \
248                         typename T::template arg<7>::type x7,                  \
249                         typename T::template arg<8>::type x8) {                \
250     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4, x5, x6, x7, x8); \
251   }
252 #define DLWRAP_INSTANTIATE_10(SYM_USE, SYM_DEF, T)                             \
253   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
254                         typename T::template arg<1>::type x1,                  \
255                         typename T::template arg<2>::type x2,                  \
256                         typename T::template arg<3>::type x3,                  \
257                         typename T::template arg<4>::type x4,                  \
258                         typename T::template arg<5>::type x5,                  \
259                         typename T::template arg<6>::type x6,                  \
260                         typename T::template arg<7>::type x7,                  \
261                         typename T::template arg<8>::type x8,                  \
262                         typename T::template arg<9>::type x9) {                \
263     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4, x5, x6, x7, x8,  \
264                                           x9);                                 \
265   }
266 #define DLWRAP_INSTANTIATE_11(SYM_USE, SYM_DEF, T)                             \
267   T::ReturnType SYM_DEF(typename T::template arg<0>::type x0,                  \
268                         typename T::template arg<1>::type x1,                  \
269                         typename T::template arg<2>::type x2,                  \
270                         typename T::template arg<3>::type x3,                  \
271                         typename T::template arg<4>::type x4,                  \
272                         typename T::template arg<5>::type x5,                  \
273                         typename T::template arg<6>::type x6,                  \
274                         typename T::template arg<7>::type x7,                  \
275                         typename T::template arg<8>::type x8,                  \
276                         typename T::template arg<9>::type x9,                  \
277                         typename T::template arg<10>::type x10) {              \
278     return dlwrap::SYM_USE##_Trait::get()(x0, x1, x2, x3, x4, x5, x6, x7, x8,  \
279                                           x9, x10);                            \
280   }
281 
282 #endif
283