37703e4606d8e9cafa63b7c018863ef4cd3ae1b8
[m6w6/ext-psi] / src / libjit.c
1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
25
26 #include "php_psi_stdinc.h"
27 #include "context.h"
28 #include "call.h"
29 #include "php.h"
30
31 #ifdef HAVE_LIBJIT
32
33 #include <jit/jit.h>
34
35 #if HAVE_INT128
36 static jit_type_t jit_type_llong;
37 static jit_type_t jit_type_ullong;
38 #endif
39
40 struct psi_jit_context {
41 jit_context_t jit;
42 jit_type_t signature;
43 };
44
45 struct psi_jit_callback_info {
46 struct psi_jit_impl_info *impl_info;
47 struct psi_let_exp *let_exp;
48
49 void *closure;
50 };
51
52 struct psi_jit_decl_info {
53 jit_type_t signature;
54 struct psi_jit_struct_info *rv_array;
55 void *params[1];
56 };
57
58 struct psi_jit_extvar_info {
59 struct {
60 jit_type_t signature;
61 void *closure;
62 } get;
63 struct {
64 jit_type_t signature;
65 jit_type_t params[1];
66 void *closure;
67 } set;
68 };
69
70 struct psi_jit_impl_info {
71 struct psi_context *context;
72 struct psi_call_frame *frame;
73
74 void *closure;
75 };
76
77 struct psi_jit_struct_info {
78 jit_type_t strct;
79 struct psi_plist *eles;
80 };
81
82 static inline jit_type_t psi_jit_token_type(token_t t)
83 {
84 switch (t) {
85 default:
86 assert(0);
87 /* no break */
88 case PSI_T_VOID:
89 return jit_type_void;
90 case PSI_T_INT8:
91 return jit_type_sbyte;
92 case PSI_T_UINT8:
93 return jit_type_ubyte;
94 case PSI_T_INT16:
95 return jit_type_short;
96 case PSI_T_UINT16:
97 return jit_type_ushort;
98 case PSI_T_INT32:
99 return jit_type_int;
100 case PSI_T_UINT32:
101 return jit_type_uint;
102 case PSI_T_INT64:
103 return jit_type_long;
104 case PSI_T_UINT64:
105 return jit_type_ulong;
106 #if HAVE_INT128
107 case PSI_T_INT128:
108 return jit_type_llong;
109 case PSI_T_UINT128:
110 return jit_type_ullong;
111 #endif
112 case PSI_T_BOOL:
113 return jit_type_sys_bool;
114 case PSI_T_ENUM:
115 return jit_type_sys_int;
116 case PSI_T_FLOAT:
117 return jit_type_sys_float;
118 case PSI_T_DOUBLE:
119 return jit_type_sys_double;
120 #ifdef HAVE_LONG_DOUBLE
121 case PSI_T_LONG_DOUBLE:
122 return jit_type_sys_long_double;
123 #endif
124 case PSI_T_POINTER:
125 case PSI_T_FUNCTION:
126 return jit_type_void_ptr;
127 }
128 }
129 static inline jit_type_t psi_jit_impl_type(token_t impl_type)
130 {
131 switch (impl_type) {
132 case PSI_T_BOOL:
133 return jit_type_sbyte;
134 case PSI_T_INT:
135 return jit_type_long;
136 case PSI_T_STRING:
137 return jit_type_void_ptr;
138 case PSI_T_FLOAT:
139 case PSI_T_DOUBLE:
140 return jit_type_sys_double;
141 EMPTY_SWITCH_DEFAULT_CASE();
142 }
143 return NULL;
144 }
145
146 static void psi_jit_type_free(jit_type_t *typ_ptr)
147 {
148 jit_type_free(*typ_ptr);
149 }
150
151 static inline jit_abi_t psi_jit_abi(zend_string *convention)
152 {
153 if (zend_string_equals_literal(convention, "stdcall")) {
154 return jit_abi_stdcall;
155 }
156 if (zend_string_equals_literal(convention, "fastcall")) {
157 return jit_abi_fastcall;
158 }
159 return jit_abi_cdecl;
160 }
161
162 static void psi_jit_handler(jit_type_t sig, void *result, void **args, void *data)
163 {
164 struct psi_impl *impl = data;
165 struct psi_jit_impl_info *info = impl->info;
166
167 psi_context_call(info->context, *(zend_execute_data **)args[0], *(zval **) args[1], impl);
168 }
169
170 static void psi_jit_callback(jit_type_t sig, void *result, void **args, void *data)
171 {
172 struct psi_jit_callback_info *cb_info = data;
173 struct psi_call_frame_callback cb_data;
174
175 assert(cb_info->impl_info->frame);
176
177 cb_data.cb = cb_info->let_exp;
178 cb_data.argc = jit_type_num_params(sig);
179 cb_data.argv = args;
180 cb_data.rval = result;
181
182 psi_call_frame_do_callback(cb_info->impl_info->frame, &cb_data);
183 }
184
185 static bool psi_jit_load(void)
186 {
187 #if HAVE_INT128
188 jit_type_t ll_fields[2], ull_fields[2];
189
190 ll_fields[0] = ll_fields[1] = jit_type_long;
191 jit_type_llong = jit_type_create_struct(ll_fields, 2, 1);
192
193 ull_fields[0] = ull_fields[1] = jit_type_ulong;
194 jit_type_ullong = jit_type_create_struct(ull_fields, 2, 1);
195 #endif
196 return true;
197 }
198
199 static void psi_jit_free(void)
200 {
201 #if HAVE_INT128
202 jit_type_free(jit_type_llong);
203 jit_type_free(jit_type_ullong);
204 #endif
205 }
206
207 static bool psi_jit_init(struct psi_context *C)
208 {
209 struct psi_jit_context *context = pecalloc(1, sizeof(*context), 1);
210 jit_type_t params[] = {jit_type_void_ptr, jit_type_void_ptr};
211
212 context->jit = jit_context_create();
213 if (!context->jit) {
214 pefree(context, 1);
215 return false;
216 }
217
218 context->signature = jit_type_create_signature(jit_abi_fastcall, jit_type_void,
219 params, 2, 1);
220 if (!context->signature) {
221 jit_context_destroy(context->jit);
222 pefree(context, 1);
223 return false;
224 }
225
226 C->context = context;
227 return true;
228 }
229
230 static void psi_jit_dtor(struct psi_context *C)
231 {
232 if (C->context) {
233 struct psi_jit_context *context = C->context;
234
235 jit_type_free(context->signature);
236 jit_context_destroy(context->jit);
237
238 pefree(C->context, 1);
239 C->context = NULL;
240 }
241 }
242
243
244 static bool psi_jit_composite_init(struct psi_context *C,
245 struct psi_decl_arg *darg)
246 {
247 struct psi_jit_struct_info *info;
248
249 if (darg->engine.type) {
250 return true;
251 }
252
253 info = pecalloc(1, sizeof(*info), 1);
254 info->eles = psi_plist_init((psi_plist_dtor) psi_jit_type_free);
255 psi_context_composite_type_elements(C, darg, &info->eles);
256 info->strct = jit_type_create_struct((jit_type_t *)
257 psi_plist_eles(info->eles),
258 psi_plist_count(info->eles), 0);
259
260 darg->engine.info = info;
261 darg->engine.type = info->strct;
262
263 return true;
264 }
265
266 static void psi_jit_composite_dtor(struct psi_context *C,
267 struct psi_decl_arg *darg)
268 {
269 struct psi_jit_struct_info *info = darg->engine.info;
270
271 if (info) {
272 darg->engine.info = NULL;
273 darg->engine.type = NULL;
274
275 jit_type_free(info->strct);
276 /* just free */
277 pefree(info->eles, 1);
278 pefree(info, 1);
279 }
280 }
281
282 static void psi_jit_extvar_get(jit_type_t sig, void *result, void **args, void *data)
283 {
284 struct psi_decl_extvar *evar = data;
285
286 psi_decl_extvar_get(evar, result);
287 }
288
289 static void psi_jit_extvar_set(jit_type_t sig, void *result, void **args, void *data)
290 {
291 struct psi_decl_extvar *evar = data;
292
293 psi_decl_extvar_set(evar, args[0]);
294 }
295
296 static bool psi_jit_decl_init(struct psi_context *, struct psi_decl *);
297
298 static bool psi_jit_extvar_init(struct psi_context *C,
299 struct psi_decl_extvar *evar)
300 {
301 struct psi_jit_context *ctx = C->context;
302 struct psi_jit_extvar_info *info = pecalloc(1, sizeof(*info), 1);
303
304 evar->info = info;
305
306 psi_jit_decl_init(C, evar->getter);
307 psi_jit_decl_init(C, evar->setter);
308
309 jit_context_build_start(ctx->jit);
310
311 info->get.signature = jit_type_create_signature(jit_abi_cdecl,
312 psi_context_decl_arg_call_type(C, evar->getter->func), NULL, 0, 1);
313 if (!info->get.signature) {
314 goto failure;
315 }
316 info->get.closure = jit_closure_create(ctx->jit, info->get.signature,
317 psi_jit_extvar_get, evar);
318 if (!info->get.closure) {
319 goto failure;
320 }
321
322 info->set.params[0] = psi_context_decl_arg_call_type(C, evar->arg);
323 info->set.signature = jit_type_create_signature(jit_abi_cdecl,
324 psi_context_decl_arg_call_type(C, evar->setter->func),
325 info->set.params, 1, 1);
326 if (!info->set.signature) {
327 goto failure;
328 }
329 info->set.closure = jit_closure_create(ctx->jit, info->set.signature,
330 psi_jit_extvar_set, evar);
331 if (!info->set.closure) {
332 goto failure;
333 }
334
335 evar->getter->sym = info->get.closure;
336 evar->setter->sym = info->set.closure;
337
338 jit_context_build_end(ctx->jit);
339 return true;
340 failure: ;
341 jit_context_build_end(ctx->jit);
342 return false;
343 }
344
345 static void psi_jit_extvar_dtor(struct psi_context *C,
346 struct psi_decl_extvar *evar) {
347 if (evar->info) {
348 pefree(evar->info, 1);
349 evar->info = NULL;
350 }
351 }
352
353 static bool psi_jit_decl_init(struct psi_context *C, struct psi_decl *decl)
354 {
355 if (!decl->info) {
356 struct psi_jit_context *ctx = C->context;
357 size_t i, c = psi_plist_count(decl->args);
358 struct psi_decl_arg *arg;
359 struct psi_jit_decl_info *info = pecalloc(1,
360 sizeof(*info) + 2 * c * sizeof(void *), 1);
361
362 decl->info = info;
363
364 jit_context_build_start(ctx->jit);
365
366 for (i = 0; psi_plist_get(decl->args, i, &arg); ++i) {
367 info->params[i] = psi_context_decl_arg_call_type(C, arg);
368 }
369 info->params[c] = NULL;
370
371 info->signature = jit_type_create_signature(
372 psi_jit_abi(decl->abi->convention),
373 psi_context_decl_arg_full_type(C, decl->func),
374 (jit_type_t *) info->params, c, 1);
375
376 jit_context_build_end(ctx->jit);
377
378 if (!info->signature) {
379 pefree(info, 1);
380 decl->info = NULL;
381 return false;
382 }
383 }
384
385 return true;
386 }
387
388 static void psi_jit_decl_dtor(struct psi_context *C, struct psi_decl *decl) {
389 if (decl->info) {
390 struct psi_jit_decl_info *info = decl->info;
391
392 jit_type_free(info->signature);
393 pefree(info, 1);
394 decl->info = NULL;
395 }
396 }
397
398 static bool psi_jit_impl_init(struct psi_context *C,
399 struct psi_impl *impl, zif_handler *zh)
400 {
401 struct psi_jit_context *context = C->context;
402 struct psi_jit_impl_info *info = calloc(1, sizeof(*info));
403
404 impl->info = info;
405 info->context = C;
406
407 info->closure = jit_closure_create(context->jit, context->signature,
408 &psi_jit_handler, impl);
409
410 if (!info->closure) {
411 goto failure;
412 }
413
414 *zh = info->closure;
415 return true;
416
417 failure: ;
418 impl->info = NULL;
419 pefree(info, 1);
420 return false;
421 }
422
423
424 static void psi_jit_impl_dtor(struct psi_context *C, struct psi_impl *impl) {
425 struct psi_jit_impl_info *info = impl->info;
426
427 if (info) {
428 /* The memory for the closure will be reclaimed
429 * when the context is destroyed.
430 */
431 pefree(info, 1);
432 impl->info = NULL;
433 }
434 }
435
436 static bool psi_jit_cb_init(struct psi_context *C,
437 struct psi_let_exp *exp, struct psi_impl *impl)
438 {
439 struct psi_jit_context *context = C->context;
440 struct psi_jit_callback_info *cb_info;
441 struct psi_jit_decl_info *decl_info;
442
443 assert(exp->kind == PSI_LET_CALLBACK);
444
445 if (!psi_jit_decl_init(C, exp->data.callback->decl)) {
446 return false;
447 }
448
449 cb_info = pecalloc(1, sizeof(*cb_info), 1);
450 cb_info->impl_info = impl->info;
451 cb_info->let_exp = exp;
452
453 decl_info = exp->data.callback->decl->info;
454 cb_info->closure = jit_closure_create(context->jit, decl_info->signature,
455 &psi_jit_callback, cb_info);
456
457 if (!cb_info->closure) {
458 free(cb_info);
459 return false;
460 }
461
462 assert(!exp->data.callback->decl->sym);
463 exp->data.callback->info = cb_info;
464 exp->data.callback->decl->sym = cb_info->closure;
465
466 return true;
467 }
468
469 static void psi_jit_cb_dtor(struct psi_context *C,
470 struct psi_let_exp *let_exp, struct psi_impl *impl)
471 {
472 assert(let_exp->kind == PSI_LET_CALLBACK);
473
474 psi_jit_decl_dtor(C, let_exp->data.callback->decl);
475
476 if (let_exp->data.callback->info) {
477 struct psi_jit_callback_info *info = let_exp->data.callback->info;
478
479 /* The memory for the closure will be reclaimed
480 * when the context is destroyed.
481 */
482 pefree(info, 1);
483 let_exp->data.callback->info = NULL;
484 }
485 }
486
487 static void psi_jit_call(struct psi_call_frame *frame) {
488 struct psi_decl *decl = psi_call_frame_get_decl(frame);
489 struct psi_impl *impl = psi_call_frame_get_impl(frame);
490 struct psi_jit_decl_info *decl_info = decl->info;
491 struct psi_jit_impl_info *impl_info;
492 struct psi_call_frame *prev;
493
494 if (impl) {
495 impl_info = impl->info;
496 prev = impl_info->frame;
497 impl_info->frame = frame;
498 }
499 jit_apply(decl_info->signature, decl->sym,
500 psi_call_frame_get_arg_pointers(frame), psi_plist_count(decl->args),
501 psi_call_frame_get_rpointer(frame));
502 if (impl) {
503 impl_info->frame = prev;
504 }
505 }
506
507 static void psi_jit_call_va(struct psi_call_frame *frame) {
508 jit_type_t signature;
509 struct psi_call_frame *prev;
510 struct psi_decl *decl = psi_call_frame_get_decl(frame);
511 struct psi_impl *impl = psi_call_frame_get_impl(frame);
512 struct psi_jit_decl_info *decl_info = decl->info;
513 struct psi_jit_impl_info *impl_info;
514 size_t i, va_count, argc;
515 jit_type_t *param_types;
516
517 argc = psi_plist_count(decl->args);
518 va_count = psi_call_frame_num_var_args(frame);
519 param_types = ecalloc(argc + va_count + 1, sizeof(jit_type_t));
520 memcpy(param_types, decl_info->params, argc * sizeof(jit_type_t));
521 for (i = 0; i < va_count; ++i) {
522 struct psi_call_frame_argument *frame_arg;
523
524 frame_arg = psi_call_frame_get_var_argument(frame, i);
525 param_types[argc + i] = psi_jit_impl_type(frame_arg->va_type);
526 }
527
528 signature = jit_type_create_signature(jit_abi_vararg,
529 jit_type_get_return(decl_info->signature),
530 param_types, argc + va_count, 1);
531 assert(signature);
532
533 if (impl) {
534 impl_info = impl->info;
535 prev = impl_info->frame;
536 impl_info->frame = frame;
537 }
538 jit_apply(signature, decl->sym,
539 psi_call_frame_get_arg_pointers(frame), argc,
540 psi_call_frame_get_rpointer(frame));
541 if (impl) {
542 impl_info->frame = prev;
543 }
544
545 jit_type_free(signature);
546
547 efree(param_types);
548 }
549
550 static void *psi_jit_typeof_impl(struct psi_context *C, token_t impl_type)
551 {
552 return psi_jit_impl_type(impl_type);
553 }
554
555 static void *psi_jit_typeof_decl(struct psi_context *C, token_t decl_type)
556 {
557 return psi_jit_token_type(decl_type);
558 }
559
560 static void *psi_jit_copyof_type(struct psi_context *C, void *orig_type)
561 {
562 return jit_type_copy(orig_type);
563 }
564
565 static void psi_jit_layoutof_type(struct psi_context *C, void *orig_type,
566 struct psi_layout *l)
567 {
568 l->pos = jit_type_get_alignment(orig_type);
569 l->len = jit_type_get_size(orig_type);
570
571 assert(l->pos);
572 assert(l->len);
573 }
574
575 static struct psi_context_ops ops = {
576 "libjit",
577 psi_jit_load,
578 psi_jit_free,
579 psi_jit_init,
580 psi_jit_dtor,
581 psi_jit_composite_init,
582 psi_jit_composite_dtor,
583 psi_jit_extvar_init,
584 psi_jit_extvar_dtor,
585 psi_jit_decl_init,
586 psi_jit_decl_dtor,
587 psi_jit_impl_init,
588 psi_jit_impl_dtor,
589 psi_jit_cb_init,
590 psi_jit_cb_dtor,
591 psi_jit_call,
592 psi_jit_call_va,
593 psi_jit_typeof_impl,
594 psi_jit_typeof_decl,
595 psi_jit_copyof_type,
596 psi_jit_layoutof_type,
597 };
598
599 struct psi_context_ops *psi_libjit_ops(void)
600 {
601 return &ops;
602 }
603
604 #endif /* HAVE_LIBJIT */