libffi
authorMichael Wallner <mike@php.net>
Wed, 21 Oct 2015 17:11:45 +0000 (19:11 +0200)
committerMichael Wallner <mike@php.net>
Wed, 21 Oct 2015 17:11:45 +0000 (19:11 +0200)
config.m4
php_psi.h
src/libffi.c [new file with mode: 0644]
src/libffi.h [new file with mode: 0644]
src/module.c

index fe5603f81ec54fd9aa92bfabfa91ddf8b6257219..8e6c1d121253445a11d50ad0d0fbdbea2392ffda 100644 (file)
--- a/config.m4
+++ b/config.m4
@@ -6,8 +6,30 @@ if test "$PHP_PSI" != "no"; then
        AC_ARG_VAR(LEMON, The lemon parser generator of the SQLite project)
        AC_PATH_PROG(LEMON, lemon, ./lemon)
        PHP_SUBST(LEMON)
+       
+       if test -z "$PKG_CONFIG"
+       then
+               AC_PATH_PROG(PKG_CONFIG, pkg-config, false)
+       fi
+       
+       export PKG_CONFIG_PATH="/opt/libffi/lib/pkgconfig:$PKG_CONFIG_PATH"
+       
+       AC_CACHE_CHECK(for libffi, PSI_cv_LIBFFI, [
+       if $PKG_CONFIG --exists libffi
+       then
+               PSI_cv_LIBFFI=true
+       else
+               PSI_cv_LIBFFI=false
+       fi])
+       if $PSI_cv_LIBFFI
+       then
+               PHP_EVAL_INCLINE(`$PKG_CONFIG --cflags libffi`)
+               PHP_EVAL_LIBLINE(`$PKG_CONFIG --libs libffi`, PSI_SHARED_LIBADD)
+       else
+               AC_MSG_ERROR([Could not find libjit, please provide the base install path])
+       fi
 
-       AC_CACHE_CHECK(for libjit install root, PSI_cv_LIBJIT_DIR, [
+       AC_CACHE_CHECK(for libjit, PSI_cv_LIBJIT_DIR, [
        for PSI_cv_LIBJIT_DIR in $PHP_PSI {/usr{,/local},/opt}{,libjit}
        do
                if test -e $PSI_cv_LIBJIT_DIR/include/jit/jit.h
@@ -16,13 +38,14 @@ if test "$PHP_PSI" != "no"; then
                fi
                PSI_cv_LIBJIT_DIR=
        done])
-       if test -z "$PSI_cv_LIBJIT_DIR"
+       if test -n "$PSI_cv_LIBJIT_DIR"
        then
+               PHP_ADD_INCLUDE($PSI_cv_LIBJIT_DIR/include)
+               PHP_ADD_LIBRARY_WITH_PATH(jit, $PSI_cv_LIBJIT_DIR/$PHP_LIBDIR, PSI_SHARED_LIBADD)
+       else
                AC_MSG_ERROR([Could not find libjit, please provide the base install path])
        fi
-
-       PHP_ADD_INCLUDE($PSI_cv_LIBJIT_DIR/include)
-       PHP_ADD_LIBRARY_WITH_PATH(jit, $PSI_cv_LIBJIT_DIR/$PHP_LIBDIR, PSI_SHARED_LIBADD)
+       
        PHP_SUBST(PSI_SHARED_LIBADD)
 
        PHP_PSI_SRCDIR=PHP_EXT_SRCDIR(psi)
@@ -33,7 +56,7 @@ if test "$PHP_PSI" != "no"; then
 
        PHP_PSI_HEADERS=`(cd $PHP_PSI_SRCDIR/src && echo *.h)`
        PHP_PSI_SOURCES="src/parser_proc.c src/parser.c src/validator.c src/module.c src/context.c"
-       PHP_PSI_SOURCES="$PHP_PSI_SOURCES src/libjit.c"
+       PHP_PSI_SOURCES="$PHP_PSI_SOURCES src/libjit.c src/libffi.c"
 
        PHP_NEW_EXTENSION(psi, $PHP_PSI_SOURCES, $ext_shared)
        PHP_INSTALL_HEADERS(ext/psi, php_psi.h $PHP_PSI_HEADERS)
index 280e786363eb1840396ed3d8eae18b88d570cf56..29ecd283c102f8f183805ce3f6bc103f97908d45 100644 (file)
--- a/php_psi.h
+++ b/php_psi.h
@@ -36,6 +36,7 @@ void psi_do_free(free_stmt *fre);
 void psi_do_clean(impl *impl);
 
 ZEND_BEGIN_MODULE_GLOBALS(psi)
+       char *engine;
        char *directory;
        PSI_Context context;
 ZEND_END_MODULE_GLOBALS(psi);
diff --git a/src/libffi.c b/src/libffi.c
new file mode 100644 (file)
index 0000000..1dbc080
--- /dev/null
@@ -0,0 +1,252 @@
+#include "php.h"
+#include "php_psi.h"
+#include "libffi.h"
+
+#include <ffi.h>
+
+static void handler(ffi_cif *signature, void *_result, void **_args, void *_data);
+
+static inline ffi_abi psi_ffi_abi(const char *convention) {
+       return FFI_DEFAULT_ABI;
+}
+static inline ffi_type *psi_ffi_type(token_t t) {
+       switch (t) {
+       default:
+               ZEND_ASSERT(0);
+               /* no break */
+       case PSI_T_VOID:
+               return &ffi_type_void;
+       case PSI_T_SINT8:
+               return &ffi_type_sint8;
+       case PSI_T_UINT8:
+               return &ffi_type_uint8;
+       case PSI_T_SINT16:
+               return &ffi_type_sint16;
+       case PSI_T_UINT16:
+               return &ffi_type_uint16;
+       case PSI_T_SINT32:
+               return &ffi_type_sint32;
+       case PSI_T_UINT32:
+               return &ffi_type_uint32;
+       case PSI_T_SINT64:
+               return &ffi_type_sint64;
+       case PSI_T_UINT64:
+               return &ffi_type_uint64;
+       case PSI_T_BOOL:
+               return &ffi_type_uchar;
+       case PSI_T_CHAR:
+               return &ffi_type_schar;
+       case PSI_T_SHORT:
+               return &ffi_type_sshort;
+       case PSI_T_INT:
+               return &ffi_type_sint;
+       case PSI_T_LONG:
+               return &ffi_type_slong;
+       case PSI_T_FLOAT:
+               return &ffi_type_float;
+       case PSI_T_DOUBLE:
+               return &ffi_type_double;
+       }
+}
+static inline ffi_type *psi_ffi_decl_type(decl_type *type) {
+       return psi_ffi_type(real_decl_type(type)->type);
+}
+static inline ffi_type *psi_ffi_decl_arg_type(decl_arg *darg) {
+       if (darg->var->pointer_level) {
+               return &ffi_type_pointer;
+       } else {
+               return psi_ffi_decl_type(darg->type);
+       }
+}
+
+typedef struct PSI_LibffiContext {
+       ffi_cif signature;
+       ffi_type *params[2];
+       struct {
+               struct PSI_LibffiData **list;
+               size_t count;
+       } data;
+} PSI_LibffiContext;
+
+typedef struct PSI_LibffiData {
+       PSI_LibffiContext *context;
+       impl *impl;
+       zend_internal_arg_info *arginfo;
+       void *code;
+       ffi_closure *closure;
+       ffi_cif signature;
+       ffi_type *params[1];
+} PSI_LibffiData;
+
+static inline PSI_LibffiData *PSI_LibffiDataAlloc(PSI_LibffiContext *context, impl *impl) {
+       ffi_status rc;
+       size_t i, c = impl->decl->args->count;
+       PSI_LibffiData *data = malloc(sizeof(*data) + c * sizeof(ffi_type *));
+
+       data->context = context;
+       data->impl = impl;
+       data->arginfo = psi_internal_arginfo(impl);
+       for (i = 0; i < c; ++i) {
+               data->params[i] = psi_ffi_decl_arg_type(impl->decl->args->args[i]);
+       }
+       data->params[c] = NULL;
+
+       rc = ffi_prep_cif(
+                       &data->signature,
+                       psi_ffi_abi(data->impl->decl->abi->convention),
+                       c,
+                       psi_ffi_decl_arg_type(data->impl->decl->func),
+                       data->params);
+       ZEND_ASSERT(FFI_OK == rc);
+       data->closure = ffi_closure_alloc(sizeof(ffi_closure), &data->code);
+       rc = ffi_prep_closure_loc(
+                       data->closure,
+                       &context->signature,
+                       handler,
+                       data,
+                       data->code);
+       ZEND_ASSERT(FFI_OK == rc);
+
+       context->data.list = realloc(context->data.list, ++context->data.count * sizeof(*context->data.list));
+       context->data.list[context->data.count-1] = data;
+
+       return data;
+}
+
+static inline void PSI_LibffiDataFree(PSI_LibffiData *data) {
+       free(data->arginfo);
+       ffi_closure_free(data->closure);
+       free(data);
+}
+
+static inline PSI_LibffiContext *PSI_LibffiContextInit(PSI_LibffiContext *L) {
+       ffi_status rc;
+
+       if (!L) {
+               L = malloc(sizeof(*L));
+       }
+       memset(L, 0, sizeof(*L));
+
+       L->params[0] = &ffi_type_pointer;
+       L->params[1] = &ffi_type_pointer;
+       rc = ffi_prep_cif(&L->signature, FFI_DEFAULT_ABI, 2, &ffi_type_void, L->params);
+       ZEND_ASSERT(rc == FFI_OK);
+
+       return L;
+}
+
+static inline void PSI_LibffiContextDtor(PSI_LibffiContext *L) {
+       size_t i;
+
+       for (i = 0; i < L->data.count; ++i) {
+               PSI_LibffiDataFree(L->data.list[i]);
+       }
+       if (L->data.list) {
+               free(L->data.list);
+       }
+}
+
+static inline void PSI_LibffiContextFree(PSI_LibffiContext **L) {
+       if (*L) {
+               PSI_LibffiContextDtor(*L);
+               free(*L);
+               *L = NULL;
+       }
+}
+
+static void handler(ffi_cif *_sig, void *_result, void **_args, void *_data)
+{
+       PSI_LibffiData *data = _data;
+       size_t i;
+       void **arg_ptr = NULL, **arg_prm = NULL;
+       impl_val ret_val;
+
+       if (SUCCESS != psi_parse_args(*(zend_execute_data **)_args[0], data->impl)) {
+               return;
+       }
+
+       if (data->impl->decl->args->count) {
+               arg_ptr = malloc(data->impl->decl->args->count * sizeof(*arg_ptr));
+               arg_prm = malloc(data->impl->decl->args->count * sizeof(*arg_prm));
+
+               for (i = 0; i < data->impl->decl->args->count; ++i) {
+                       decl_arg *darg = data->impl->decl->args->args[i];
+
+                       arg_ptr[i] = psi_do_let(darg);
+                       arg_prm[i] = darg->let->val->is_reference ? &arg_ptr[i] : arg_ptr[i];
+
+                       darg->let->ptr = arg_ptr[i];
+               }
+       }
+
+       ffi_call(&data->signature, FFI_FN(data->impl->decl->dlptr), &ret_val, arg_prm);
+
+       psi_do_return(data->impl, &ret_val, *(zval **)_args[1]);
+
+       for (i = 0; i < data->impl->stmts->set.count; ++i) {
+               set_stmt *set = data->impl->stmts->set.list[i];
+
+               psi_do_set(set->arg->_zv, set->val->func, set->val->vars);
+       }
+
+       for (i = 0; i < data->impl->stmts->fre.count; ++i) {
+               free_stmt *fre = data->impl->stmts->fre.list[i];
+
+               psi_do_free(fre);
+       }
+
+       psi_do_clean(data->impl);
+
+       if (arg_ptr) {
+               free(arg_ptr);
+       }
+       if (arg_prm) {
+               free(arg_prm);
+       }
+}
+
+static void init(PSI_Context *C)
+{
+       C->context = PSI_LibffiContextInit(NULL);
+}
+
+static void dtor(PSI_Context *C)
+{
+       PSI_LibffiContextFree((void *) &C->context);
+}
+
+static zend_function_entry *compile(PSI_Context *C, PSI_Data *D)
+{
+       size_t i, j = 0;
+       zend_function_entry *zfe = calloc(D->impls->count + 1, sizeof(*zfe));
+       PSI_LibffiContext *ctx = C->context;
+
+       for (i = 0; i < D->impls->count; ++i) {
+               zend_function_entry *zf = &zfe[j];
+               PSI_LibffiData *data;
+
+               if (!D->impls->list[i]->decl) {
+                       continue;
+               }
+
+               data = PSI_LibffiDataAlloc(ctx, D->impls->list[i]);
+               zf->fname = D->impls->list[i]->func->name + (D->impls->list[i]->func->name[0] == '\\');
+               zf->num_args = D->impls->list[i]->func->args->count;
+               zf->handler = data->code;
+               zf->arg_info = data->arginfo;
+               ++j;
+       }
+
+       return zfe;
+}
+
+static PSI_ContextOps ops = {
+       init,
+       dtor,
+       compile,
+};
+
+PSI_ContextOps *PSI_Libffi(void)
+{
+       return &ops;
+}
diff --git a/src/libffi.h b/src/libffi.h
new file mode 100644 (file)
index 0000000..e8b0553
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _PSI_LIBFFI_H
+#define _PSI_LIBFFI_H
+
+#include "context.h"
+
+PSI_ContextOps *PSI_Libffi(void);
+
+#endif
index 4e7f4ce61aa59ff0b8c2628055e38092de62abd2..166596434ae8a4447bcebcf362c8e5d5b85751e8 100644 (file)
 #include "parser.h"
 
 #include "libjit.h"
+#include "libffi.h"
 
 ZEND_DECLARE_MODULE_GLOBALS(psi);
 
 PHP_INI_BEGIN()
-       STD_PHP_INI_ENTRY("psi.directory", "psis", PHP_INI_ALL, OnUpdateString, directory, zend_psi_globals, psi_globals)
+       STD_PHP_INI_ENTRY("psi.engine", "ffi", PHP_INI_SYSTEM, OnUpdateString, engine, zend_psi_globals, psi_globals)
+       STD_PHP_INI_ENTRY("psi.directory", "psis", PHP_INI_SYSTEM, OnUpdateString, directory, zend_psi_globals, psi_globals)
 PHP_INI_END();
 
 void psi_error(int type, const char *msg, ...)
@@ -313,9 +315,17 @@ void psi_do_clean(impl *impl)
 
 PHP_MINIT_FUNCTION(psi)
 {
+       PSI_ContextOps *ops;
+
        REGISTER_INI_ENTRIES();
 
-       PSI_ContextInit(&PSI_G(context), PSI_Libjit(), psi_error);
+       if (!strcasecmp(PSI_G(engine), "jit")) {
+               ops = PSI_Libjit();
+       } else {
+               ops = PSI_Libffi();
+       }
+
+       PSI_ContextInit(&PSI_G(context), ops, psi_error);
        PSI_ContextBuild(&PSI_G(context), PSI_G(directory));
 
        return SUCCESS;