+static inline void psi_ffi_call_ex(struct psi_call_frame *frame) {
+ struct psi_decl *decl = psi_call_frame_get_decl(frame);
+ struct psi_impl *impl = psi_call_frame_get_impl(frame);
+ struct psi_ffi_decl_info *decl_info = decl->info;
+ struct psi_ffi_impl_info *impl_info;
+ struct psi_call_frame *prev;
+
+ if (impl) {
+ impl_info = impl->info;
+ prev = impl_info->frame;
+ impl_info->frame = frame;
+ }
+ ffi_call(&decl_info->signature, FFI_FN(decl->sym),
+ psi_call_frame_get_rpointer(frame),
+ psi_call_frame_get_arg_pointers(frame));
+ if (impl) {
+ impl_info->frame = prev;
+ }
+}
+
+static inline void psi_ffi_call_va(struct psi_call_frame *frame) {
+ ffi_cif signature;
+ struct psi_call_frame *prev;
+ struct psi_decl *decl = psi_call_frame_get_decl(frame);
+ struct psi_impl *impl = psi_call_frame_get_impl(frame);
+ struct psi_ffi_decl_info *decl_info = decl->info;
+ struct psi_ffi_impl_info *impl_info;
+ size_t i, va_count, argc;
+ ffi_type **param_types;
+
+ argc = psi_plist_count(decl->args);
+ va_count = psi_call_frame_num_var_args(frame);
+ param_types = ecalloc(argc + va_count + 1, sizeof(ffi_type *));
+ memcpy(param_types, decl_info->params, argc * sizeof(ffi_type *));
+ for (i = 0; i < va_count; ++i) {
+ struct psi_call_frame_argument *frame_arg;
+
+ frame_arg = psi_call_frame_get_var_argument(frame, i);
+ param_types[argc + i] = psi_ffi_impl_type(frame_arg->va_type);
+ }
+
+ psi_ffi_prep_va(&decl_info->signature, &signature, argc, va_count, param_types);
+
+ if (impl) {
+ impl_info = impl->info;
+ prev = impl_info->frame;
+ impl_info->frame = frame;
+ }
+ ffi_call(&signature, FFI_FN(decl->sym),
+ psi_call_frame_get_rpointer(frame),
+ psi_call_frame_get_arg_pointers(frame));
+ if (impl) {
+ impl_info->frame = prev;
+ }
+
+ efree(param_types);
+}
+
+static void psi_ffi_call(struct psi_call_frame *frame) {
+ if (psi_call_frame_num_var_args(frame)) {
+ psi_ffi_call_va(frame);
+ } else {
+ psi_ffi_call_ex(frame);
+ }
+}
+
+static void *psi_ffi_query(struct psi_context *C, enum psi_context_query q, void *arg) {
+ switch (q) {
+ case PSI_CONTEXT_QUERY_SELF:
+ return "ffi";
+ case PSI_CONTEXT_QUERY_TYPE:
+ return psi_ffi_impl_type(*(token_t *) arg);
+ }
+ return NULL;
+}
+
+static ZEND_RESULT_CODE psi_ffi_load()
+{
+#if HAVE_INT128
+ ffi_type *i128, *u128;
+
+ i128 = pecalloc(1, 3*sizeof(ffi_type), 1);
+ i128->type = FFI_TYPE_STRUCT;
+ i128->size = 0;
+ i128->elements = (ffi_type **) (i128 + 1);
+ i128->elements[0] = &ffi_type_sint64;
+ i128->elements[1] = &ffi_type_sint64;
+
+ ffi_type_sint128 = i128;
+
+ u128 = pecalloc(1, 3*sizeof(ffi_type), 1);
+ u128->type = FFI_TYPE_STRUCT;
+ u128->size = 0;
+ u128->elements = (ffi_type **) (u128 + 1);
+ u128->elements[0] = &ffi_type_uint64;
+ u128->elements[1] = &ffi_type_uint64;
+
+ ffi_type_uint128 = u128;
+#endif
+ return SUCCESS;
+}
+
+static void psi_ffi_free()
+{
+#if HAVE_INT128
+ free(ffi_type_sint128);
+ free(ffi_type_uint128);
+#endif
+}
+
+static struct psi_context_ops ops = {
+ psi_ffi_load,
+ psi_ffi_free,
+ psi_ffi_init,
+ psi_ffi_dtor,
+ psi_ffi_compile,
+ psi_ffi_call,
+ psi_ffi_query,