/
vlbdb.hpp
350 lines (307 loc) · 11.6 KB
/
vlbdb.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/* VLBDB: Very late bitcode and data binder
*
*/
#ifndef VLBDB_HPP
#define VLBDB_HPP
#include <tr1/memory>
#include <stdarg.h>
extern "C" {
#include "vlbdb.h"
}
/* binding_unit_t is the largest granularity at which runtime-binding
* happens.
*
* A binding_unit is created from a bitcode file. Only functions
* defined in that bitcode file can be specialized and compiled at
* runtime.
*
* Users will usually directly pass pointers to functions, and not
* function names. Instead, ld.so is used to map from names in the
* bitcode to addresses. The register methods can be used to
* supplement that mapping.
*
* The binding_unit is more than just a runtime partial applier,
* however. In addition to caching specialization results, it also
* preserves backward-translation information. Thus, calls to
* specialized functions can be de-optimized into calls of functions
* known to the binding_unit (and to LLVM). This de-optimization
* process is useful because it lets us replace callees with cached
* specializations, and even inline some calls.
*
* Each function (native or specialized) may have some metadata
* associated with it. Currently, the only metadata is the maximum
* number of automatically-specialized arguments. It defaults to 0,
* but increasing that value can result in useful implicit recursive
* specialization.
*
* It's often the case that C functions receive values as pointers.
* In order to better support specialization of such functions
* binding_unit have the ability to mark certain address ranges as
* immutable (and thus subject to constant folding). When the address
* is irrelevant, it is preferable to instead intern a byte range,
* which will increase the odds of successful caching.
*
* Finally, binding_units also have support for blocks, the
* clang/Apple extension. These closures are internally represented
* as pointers to structures. The specialized parses these structures
* and ends up specializing calls in which the first argument is an
* interned byte range.
*/
/* binder_t is used to programmatically build the constant argument
* list to a given function or block.
*
* binding_unit provides some convenience template methods for known
* number and types of arguments. However, neither the template or
* the format-string methods are well-suited to variable number or
* types of arguments. The binder interface is the most general
* interface, and the convenience methods are just that.
*/
class binder_t;
class binding_unit_t {
struct binding_unit_impl;
std::tr1::shared_ptr<binding_unit_impl> impl;
explicit binding_unit_t (binding_unit_impl *);
public:
static binding_unit_t create_from_bitcode(const char *,
void * context = 0);
template <typename T>
binder_t create_binder(T function);
binder_t create_binder(void * function);
template <typename T>
binder_t create_block_binder(T block);
binder_t create_block_binder(void * block);
unsigned register_all_functions();
template <typename T>
void register_function(T function, size_t metadata = 0, const char * = 0);
void register_function(void * function, size_t metadata = 0, const char * = 0);
void register_function(const char *, size_t metadata = 0);
template <typename T>
void register_block(T block, size_t metadata = 0);
void register_block(void * block, size_t metadata = 0);
template <typename T>
void * specializef(T function, const char *, ...);
void * specializef(void * function, const char *, ...);
template <typename T>
void * vspecializef(T function, const char *, va_list args);
void * vspecializef(void * function, const char *, va_list args);
template <typename T>
void * block_specializef(T block, const char *, ...);
void * block_specializef(void * block, const char *, ...);
template <typename T>
void * vblock_specializef(T block, const char *, va_list args);
void * vblock_specializef(void * block, const char *, va_list args);
template <typename T>
void * specialize(T function);
template <typename T, typename A1>
void * specialize(T function, const A1& arg1);
template <typename T, typename A1, typename A2>
void * specialize(T function, const A1& arg1, const A2& arg2);
template <typename T, typename A1, typename A2, typename A3>
void * specialize(T function, const A1& arg1, const A2& arg2,
const A3& arg3);
template <typename T, typename A1, typename A2, typename A3,
typename A4>
void * specialize(T function, const A1& arg1, const A2& arg2,
const A3& arg3, const A4 &arg4);
template <typename T>
void * specialize_block (T block);
template <typename T, typename A1>
void * specialize_block (T block, const A1& arg1);
template <typename T, typename A1, typename A2>
void * specialize_block (T block, const A1& arg1, const A2& arg2);
template <typename T, typename A1, typename A2, typename A3>
void * specialize_block (T block, const A1& arg1,
const A2& arg2, const A3& arg3);
template <typename T, typename A1, typename A2,
typename A3, typename A4>
void * specialize_block (T block, const A1& arg1,
const A2& arg2, const A3& arg3,
const A4 &arg4);
template <typename T>
int intern_range (const T * address, size_t size = sizeof(T));
int intern_range (const void * address, size_t size);
template <typename T>
int register_range (const T * address, size_t size = sizeof(T));
int register_range (const void * address, size_t size);
};
class binder_t {
struct binder_impl;
std::tr1::shared_ptr<binder_impl> impl;
explicit binder_t (binder_impl *);
public:
binder_t clone (const binder_t &);
static binder_t create_from_unit(binding_unit_t &unit,
void * function);
void bind_uint (unsigned long long);
void bind_int (long long);
void bind_fp (double);
void bind_ptr (void *);
void bind_range (const void *, size_t, bool intern = true);
void * specialize();
void bind (unsigned char x);
void bind (unsigned short x);
void bind (unsigned int x);
void bind (unsigned long x);
void bind (unsigned long long x);
void bind (char x);
void bind (short x);
void bind (int x);
void bind (long x);
void bind (long long x);
void bind (double x);
void bind (float x);
template <typename T>
void bind (T * x);
template <typename T>
void bind (const T * x, size_t = sizeof(T), bool intern = true);
};
// Template implementation noise...
template <typename T>
binder_t binding_unit_t::create_binder (T function)
{
return create_binder((void*)function);
}
template <typename T>
binder_t binding_unit_t::create_block_binder (T block)
{
return create_block_binder((void*)block);
}
template <typename T>
void binding_unit_t::register_function(T function, size_t metadata, const char * name)
{
return register_function((void*)function, metadata, name);
}
template <typename T>
void binding_unit_t::register_block(T block, size_t metadata)
{
return register_block((void*)block, metadata);
}
template <typename T>
void * binding_unit_t::specializef (T function, const char *format, ...)
{
va_list ap;
va_start(ap, format);
return vspecializef((void *)function, format, ap);
}
template <typename T>
void * binding_unit_t::vspecializef (T function, const char *format, va_list ap)
{
return vspecializef((void *)function, format, ap);
}
template <typename T>
void * binding_unit_t::block_specializef (T block, const char *format, ...)
{
va_list ap;
va_start(ap, format);
return vblock_specializef((void *)block, format, ap);
}
template <typename T>
void * binding_unit_t::vblock_specializef (T block, const char *format, va_list ap)
{
return vblock_specializef((void *)block, format, ap);
}
template <typename T>
void * binding_unit_t::specialize (T fun)
{
binder_t binder(create_binder(fun));
return binder.specialize();
}
template <typename T, typename A1>
void * binding_unit_t::specialize (T fun, const A1& arg1)
{
binder_t binder(create_binder(fun));
binder.bind(arg1);
return binder.specialize();
}
template <typename T, typename A1, typename A2>
void * binding_unit_t::specialize (T fun, const A1& arg1, const A2 &arg2)
{
binder_t binder(create_binder(fun));
binder.bind(arg1);
binder.bind(arg2);
return binder.specialize();
}
template <typename T, typename A1, typename A2, typename A3>
void * binding_unit_t::specialize (T fun, const A1& arg1, const A2 &arg2,
const A3 &arg3)
{
binder_t binder(create_binder(fun));
binder.bind(arg1);
binder.bind(arg2);
binder.bind(arg3);
return binder.specialize();
}
template <typename T, typename A1, typename A2, typename A3, typename A4>
void * binding_unit_t::specialize (T fun, const A1& arg1, const A2 &arg2,
const A3 &arg3, const A4 &arg4)
{
binder_t binder(create_binder(fun));
binder.bind(arg1);
binder.bind(arg2);
binder.bind(arg3);
binder.bind(arg4);
return binder.specialize();
}
template <typename T>
void * binding_unit_t::specialize_block (T block)
{
binder_t binder(create_block_binder(block));
return binder.specialize();
}
template <typename T, typename A1>
void * binding_unit_t::specialize_block (T block, const A1& arg1)
{
binder_t binder(create_block_binder(block));
binder.bind(arg1);
return binder.specialize();
}
template <typename T, typename A1, typename A2>
void * binding_unit_t::specialize_block (T block, const A1& arg1, const A2 &arg2)
{
binder_t binder(create_block_binder(block));
binder.bind(arg1);
binder.bind(arg2);
return binder.specialize();
}
template <typename T, typename A1, typename A2, typename A3>
void * binding_unit_t::specialize_block (T block, const A1& arg1, const A2 &arg2,
const A3 &arg3)
{
binder_t binder(create_block_binder(block));
binder.bind(arg1);
binder.bind(arg2);
binder.bind(arg3);
return binder.specialize();
}
template <typename T, typename A1, typename A2, typename A3, typename A4>
void * binding_unit_t::specialize_block (T block, const A1& arg1, const A2 &arg2,
const A3 &arg3, const A4 &arg4)
{
binder_t binder(create_block_binder(block));
binder.bind(arg1);
binder.bind(arg2);
binder.bind(arg3);
binder.bind(arg4);
return binder.specialize();
}
template <typename T>
int binding_unit_t::intern_range (const T * address, size_t size)
{
return intern_range((const void*)address, size);
}
template <typename T>
int binding_unit_t::register_range (const T * address, size_t size)
{
return register_range((const void*)address, size);
}
template <typename T>
void binder_t::bind (T * x)
{
return bind_ptr((void*)x);
}
template <typename T>
void binder_t::bind (const T * x, size_t range, bool intern)
{
return bind_range((const void *)x, range, intern);
}
#endif