4 # include "php_config.h"
15 #undef PACKAGE_BUGREPORT
18 #undef PACKAGE_TARNAME
19 #undef PACKAGE_VERSION
23 #ifndef PSI_HAVE_FFI_CLOSURE_ALLOC
28 # include <sys/mman.h>
29 # ifndef MAP_ANONYMOUS
30 # define MAP_ANONYMOUS MAP_ANON
35 static void *psi_ffi_closure_alloc(size_t s
, void **code
)
37 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
38 return ffi_closure_alloc(s
, code
);
40 *code
= mmap(NULL
, s
, PROT_EXEC
|PROT_WRITE
|PROT_READ
,
41 MAP_PRIVATE
|MAP_ANONYMOUS
, -1, 0);
42 if (MAP_FAILED
== *code
) {
47 # error "Neither ffi_closure_alloc() nor mmap() available"
51 static ffi_status
psi_ffi_prep_closure(ffi_closure
**closure
, void **code
, ffi_cif
*sig
, void (*handler
)(ffi_cif
*,void*,void**,void*), void *data
) {
52 *closure
= psi_ffi_closure_alloc(sizeof(ffi_closure
), code
);
53 ZEND_ASSERT(*closure
!= NULL
);
55 #if PSI_HAVE_FFI_PREP_CLOSURE_LOC
56 return ffi_prep_closure_loc(*closure
, sig
, handler
, data
, *code
);
58 #elif PSI_HAVE_FFI_PREP_CLOSURE
59 return ffi_prep_closure(*code
, sig
, handler
, data
);
61 # error "Neither ffi_prep_closure() nor ffi_prep_closure_loc() is available"
66 static void psi_ffi_closure_free(void *c
)
68 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
71 munmap(c
, sizeof(ffi_closure
));
75 static void psi_ffi_handler(ffi_cif
*_sig
, void *_result
, void **_args
, void *_data
)
77 psi_call(*(zend_execute_data
**)_args
[0], *(zval
**)_args
[1], _data
);
80 static void psi_ffi_callback(ffi_cif
*_sig
, void *_result
, void **_args
, void *_data
)
82 psi_callback(_data
, _result
, _sig
->nargs
, _args
);
85 static inline ffi_type
*psi_ffi_decl_arg_type(decl_arg
*darg
);
87 struct psi_ffi_context
{
96 void *params
[1]; /* [type1, type2, NULL, arg1, arg2] ... */
99 static inline ffi_abi
psi_ffi_abi(const char *convention
) {
100 return FFI_DEFAULT_ABI
;
103 static inline struct psi_ffi_call
*psi_ffi_call_alloc(struct psi_context
*C
, decl
*decl
) {
105 size_t i
, c
= decl
->args
? decl
->args
->count
: 0;
106 struct psi_ffi_call
*call
= calloc(1, sizeof(*call
) + 2 * c
* sizeof(void *));
108 for (i
= 0; i
< c
; ++i
) {
109 call
->params
[i
] = psi_ffi_decl_arg_type(decl
->args
->args
[i
]);
111 call
->params
[c
] = NULL
;
113 decl
->call
.info
= call
;
114 decl
->call
.rval
= &decl
->func
->ptr
;
116 decl
->call
.args
= (void **) &call
->params
[c
+1];
118 rc
= ffi_prep_cif(&call
->signature
, psi_ffi_abi(decl
->abi
->convention
),
119 c
, psi_ffi_decl_arg_type(decl
->func
), (ffi_type
**) call
->params
);
120 ZEND_ASSERT(FFI_OK
== rc
);
125 static inline ffi_status
psi_ffi_call_init_closure(struct psi_context
*C
, struct psi_ffi_call
*call
, impl
*impl
) {
126 struct psi_ffi_context
*context
= C
->context
;
128 return psi_ffi_prep_closure(&call
->closure
, &call
->code
, &context
->signature
, psi_ffi_handler
, impl
);
131 static inline ffi_status
psi_ffi_call_init_callback_closure(struct psi_context
*C
, struct psi_ffi_call
*call
, let_callback
*cb
) {
132 return psi_ffi_prep_closure(&call
->closure
, &call
->code
, &call
->signature
, psi_ffi_callback
, cb
);
135 static inline void psi_ffi_call_free(struct psi_ffi_call
*call
) {
137 psi_ffi_closure_free(call
->closure
);
142 static inline ffi_type
*psi_ffi_token_type(token_t t
) {
148 return &ffi_type_void
;
150 return &ffi_type_sint8
;
152 return &ffi_type_uint8
;
154 return &ffi_type_sint16
;
156 return &ffi_type_uint16
;
158 return &ffi_type_sint32
;
160 return &ffi_type_uint32
;
162 return &ffi_type_sint64
;
164 return &ffi_type_uint64
;
166 return &ffi_type_uchar
;
169 return &ffi_type_sint
;
171 return &ffi_type_slong
;
173 return &ffi_type_float
;
175 return &ffi_type_double
;
176 #ifdef HAVE_LONG_DOUBLE
177 case PSI_T_LONG_DOUBLE
:
178 return &ffi_type_longdouble
;
182 return &ffi_type_pointer
;
185 static inline ffi_type
*psi_ffi_impl_type(token_t impl_type
) {
188 return &ffi_type_sint8
;
190 return &ffi_type_sint64
;
192 return &ffi_type_pointer
;
195 return &ffi_type_double
;
196 EMPTY_SWITCH_DEFAULT_CASE();
200 static void psi_ffi_struct_type_dtor(void *type
) {
201 ffi_type
*strct
= type
;
203 if (strct
->elements
) {
206 for (ptr
= strct
->elements
; *ptr
; ++ptr
) {
209 free(strct
->elements
);
214 static size_t psi_ffi_struct_type_pad(ffi_type
**els
, size_t padding
) {
217 for (i
= 0; i
< padding
; ++i
) {
218 ffi_type
*pad
= malloc(sizeof(*pad
));
220 memcpy(pad
, &ffi_type_schar
, sizeof(*pad
));
227 static ffi_type
**psi_ffi_struct_type_elements(decl_struct
*strct
) {
228 size_t i
, argc
= strct
->args
->count
, nels
= 0, offset
= 0, maxalign
= 0;
229 ffi_type
**els
= calloc(argc
+ 1, sizeof(*els
));
231 for (i
= 0; i
< strct
->args
->count
; ++i
) {
232 decl_arg
*darg
= strct
->args
->args
[i
];
233 ffi_type
*type
= malloc(sizeof(*type
));
236 memcpy(type
, psi_ffi_decl_arg_type(darg
), sizeof(*type
));
238 ZEND_ASSERT(type
->size
== darg
->layout
->len
);
240 if (type
->alignment
> maxalign
) {
241 maxalign
= type
->alignment
;
244 if ((padding
= psi_offset_padding(darg
->layout
->pos
- offset
, type
->alignment
))) {
245 if (nels
+ padding
+ 1 > argc
) {
247 els
= realloc(els
, (argc
+ 1) * sizeof(*els
));
250 psi_ffi_struct_type_pad(&els
[nels
], padding
);
254 ZEND_ASSERT(offset
== darg
->layout
->pos
);
256 offset
= (offset
+ darg
->layout
->len
+ type
->alignment
- 1) & ~(type
->alignment
- 1);
260 /* apply struct alignment padding */
261 offset
= (offset
+ maxalign
- 1) & ~(maxalign
- 1);
263 ZEND_ASSERT(offset
<= strct
->size
);
264 if (offset
< strct
->size
) {
265 psi_ffi_struct_type_pad(&els
[nels
], strct
->size
- offset
);
270 static inline ffi_type
*psi_ffi_decl_type(decl_type
*type
) {
271 decl_type
*real
= real_decl_type(type
);
273 switch (real
->type
) {
275 if (!real
->real
.strct
->engine
.type
) {
276 ffi_type
*strct
= calloc(1, sizeof(ffi_type
));
278 strct
->type
= FFI_TYPE_STRUCT
;
280 strct
->elements
= psi_ffi_struct_type_elements(real
->real
.strct
);
282 real
->real
.strct
->engine
.type
= strct
;
283 real
->real
.strct
->engine
.dtor
= psi_ffi_struct_type_dtor
;
286 return real
->real
.strct
->engine
.type
;
289 return psi_ffi_decl_arg_type(real
->real
.unn
->args
->args
[0]);
292 return psi_ffi_token_type(real
->type
);
295 static inline ffi_type
*psi_ffi_decl_arg_type(decl_arg
*darg
) {
296 if (darg
->var
->pointer_level
) {
297 return &ffi_type_pointer
;
299 return psi_ffi_decl_type(darg
->type
);
304 static inline struct psi_ffi_context
*psi_ffi_context_init(struct psi_ffi_context
*L
) {
308 L
= malloc(sizeof(*L
));
310 memset(L
, 0, sizeof(*L
));
312 L
->params
[0] = &ffi_type_pointer
;
313 L
->params
[1] = &ffi_type_pointer
;
314 rc
= ffi_prep_cif(&L
->signature
, FFI_DEFAULT_ABI
, 2, &ffi_type_void
, L
->params
);
315 ZEND_ASSERT(rc
== FFI_OK
);
320 static inline void psi_ffi_context_free(struct psi_ffi_context
**L
) {
327 static void psi_ffi_init(struct psi_context
*C
)
329 C
->context
= psi_ffi_context_init(NULL
);
332 static inline void psi_ffi_destroy_callbacks(struct psi_context
*C
, let_val
*let_val
) {
336 switch (let_val
->kind
) {
337 case PSI_LET_CALLBACK
:
338 cb
= let_val
->data
.callback
;
340 if (cb
->decl
&& cb
->decl
->call
.info
) {
341 psi_ffi_call_free(cb
->decl
->call
.info
);
347 fn
= let_val
->data
.func
;
353 for (i
= 0; i
< fn
->inner
->count
; ++i
) {
354 psi_ffi_destroy_callbacks(C
, fn
->inner
->vals
[i
]);
363 static void psi_ffi_dtor(struct psi_context
*C
)
368 for (i
= 0; i
< C
->decls
->count
; ++i
) {
369 decl
*decl
= C
->decls
->list
[i
];
371 if (decl
->call
.info
) {
372 psi_ffi_call_free(decl
->call
.info
);
380 for (i
= 0; i
< C
->impls
->count
; ++i
) {
381 impl
*impl
= C
->impls
->list
[i
];
383 for (j
= 0; j
< impl
->stmts
->let
.count
; ++j
) {
384 let_stmt
*let
= impl
->stmts
->let
.list
[j
];
386 if (let
->val
&& let
->val
->kind
== PSI_LET_CALLBACK
) {
387 let_callback
*cb
= let
->val
->data
.callback
;
389 if (cb
->decl
&& cb
->decl
->call
.info
) {
390 psi_ffi_call_free(cb
->decl
->call
.info
);
396 psi_ffi_context_free((void *) &C
->context
);
399 static inline void psi_ffi_compile_callbacks(struct psi_context
*C
, let_val
*let_val
) {
400 struct psi_ffi_call
*call
;
404 switch (let_val
->kind
) {
405 case PSI_LET_CALLBACK
:
406 cb
= let_val
->data
.callback
;
407 if ((call
= psi_ffi_call_alloc(C
, cb
->decl
))) {
408 if (FFI_OK
!= psi_ffi_call_init_callback_closure(C
, call
, cb
)) {
409 psi_ffi_call_free(call
);
413 cb
->decl
->call
.sym
= call
->code
;
419 fn
= let_val
->data
.func
;
424 for (i
= 0; i
< fn
->inner
->count
; ++i
) {
425 psi_ffi_compile_callbacks(C
, fn
->inner
->vals
[i
]);
434 static zend_function_entry
*psi_ffi_compile(struct psi_context
*C
)
437 zend_function_entry
*zfe
;
443 zfe
= calloc(C
->impls
->count
+ 1, sizeof(*zfe
));
444 for (i
= 0; i
< C
->impls
->count
; ++i
) {
445 zend_function_entry
*zf
= &zfe
[j
];
446 struct psi_ffi_call
*call
;
447 impl
*impl
= C
->impls
->list
[i
];
453 if ((call
= psi_ffi_call_alloc(C
, impl
->decl
))) {
454 if (FFI_OK
!= psi_ffi_call_init_closure(C
, call
, impl
)) {
455 psi_ffi_call_free(call
);
460 zf
->fname
= impl
->func
->name
+ (impl
->func
->name
[0] == '\\');
461 zf
->num_args
= impl
->func
->args
->count
;
462 zf
->handler
= call
->code
;
463 zf
->arg_info
= psi_internal_arginfo(impl
);
466 for (c
= 0; c
< impl
->stmts
->let
.count
; ++c
) {
467 psi_ffi_compile_callbacks(C
, impl
->stmts
->let
.list
[c
]->val
);
471 for (i
= 0; i
< C
->decls
->count
; ++i
) {
472 decl
*decl
= C
->decls
->list
[i
];
474 if (decl
->call
.info
) {
478 psi_ffi_call_alloc(C
, decl
);
484 static void psi_ffi_call(struct psi_context
*C
, decl_callinfo
*decl_call
, impl_vararg
*va
) {
485 struct psi_ffi_call
*call
= decl_call
->info
;
490 size_t i
, nfixedargs
= decl_call
->argc
, ntotalargs
= nfixedargs
+ va
->args
->count
;
491 void **params
= calloc(2 * ntotalargs
+ 2, sizeof(void *));
493 for (i
= 0; i
< nfixedargs
; ++i
) {
494 params
[i
] = call
->params
[i
];
495 params
[i
+ ntotalargs
+ 1] = call
->params
[i
+ nfixedargs
+ 1];
497 for (i
= 0; i
< va
->args
->count
; ++i
) {
498 params
[nfixedargs
+ i
] = psi_ffi_impl_type(va
->types
[i
]);
499 params
[nfixedargs
+ i
+ ntotalargs
+ 1] = &va
->values
[i
];
501 #ifdef PSI_HAVE_FFI_PREP_CIF_VAR
502 rc
= ffi_prep_cif_var(&signature
, call
->signature
.abi
,
503 nfixedargs
, ntotalargs
,
504 call
->signature
.rtype
, (ffi_type
**) params
);
506 /* FIXME: test in config.m4; assume we can just call anyway */
507 rc
= ffi_prep_cif(&signature
, call
->signature
.abi
, ntotalargs
,
508 call
->signature
.rtype
, (ffi_type
**) params
);
510 ZEND_ASSERT(FFI_OK
== rc
);
511 ffi_call(&signature
, FFI_FN(decl_call
->sym
), *decl_call
->rval
, ¶ms
[ntotalargs
+ 1]);
514 ffi_call(&call
->signature
, FFI_FN(decl_call
->sym
), *decl_call
->rval
, decl_call
->args
);
518 static struct psi_context_ops ops
= {
525 struct psi_context_ops
*psi_libffi_ops(void)
530 #endif /* HAVE_LIBFFI */