Skip to content

Commit dc68c96

Browse files
committed
JIT for FFI::cast()
1 parent 3305bb5 commit dc68c96

File tree

5 files changed

+305
-30
lines changed

5 files changed

+305
-30
lines changed

ext/opcache/jit/zend_jit.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,45 @@ static zend_ffi_type* zend_jit_ffi_supported_new(const zend_op *opline, HashTabl
169169
return NULL;
170170
}
171171

172+
static zend_ffi_type* zend_jit_ffi_supported_cast(const zend_op *opline, HashTable *ffi_symbols, const zend_jit_trace_rec *p)
173+
{
174+
zend_ffi_type *type = NULL;
175+
176+
if (opline->extended_value == 2
177+
&& (p+1)->op == ZEND_JIT_TRACE_INIT_CALL
178+
&& (p+2)->op == ZEND_JIT_TRACE_VM) {
179+
if (((p+2)->opline->opcode == ZEND_SEND_VAL
180+
|| (p+2)->opline->opcode == ZEND_SEND_VAL_EX)
181+
&& (p+2)->opline->op1_type == IS_CONST) {
182+
zval *zv = RT_CONSTANT((p+2)->opline, (p+2)->opline->op1);
183+
184+
if (Z_TYPE_P(zv) == IS_STRING) {
185+
zend_ffi_dcl *dcl = zend_ffi_api->cache_type_get(Z_STR_P(zv), ffi_symbols);
186+
187+
if (dcl) {
188+
type = dcl->type;
189+
p += 3;
190+
}
191+
}
192+
} else if ((p+2)->opline->opcode == ZEND_SEND_VAR_EX
193+
&& (p+3)->op == ZEND_JIT_TRACE_OP1_TYPE
194+
&& (p+3)->ce == zend_ffi_api->ctype_ce
195+
&& (p+4)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) {
196+
type = (zend_ffi_type*)(p+4)->ptr;
197+
p += 5;
198+
}
199+
}
200+
201+
if (type
202+
&& !ZEND_FFI_TYPE_IS_OWNED(type)
203+
&& (type->attr & ZEND_FFI_ATTR_PERSISTENT)
204+
&& type->size != 0) {
205+
return type;
206+
}
207+
208+
return NULL;
209+
}
210+
172211
static bool zend_jit_ffi_compatible(zend_ffi_type *dst_type, uint32_t src_info, zend_ffi_type *src_type)
173212
{
174213
dst_type = ZEND_FFI_TYPE(dst_type);

ext/opcache/jit/zend_jit_helpers.c

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3382,24 +3382,31 @@ static void ZEND_FASTCALL zend_jit_zval_stringl(zval *zv, const char *str, size_
33823382
}
33833383
}
33843384

3385+
static zend_ffi_cdata* ZEND_FASTCALL zend_jit_ffi_create_ptr(zend_ffi_type *type, void *ptr)
3386+
{
3387+
ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_POINTER);
3388+
zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata));
3389+
3390+
// inlined zend_ffi_object_init()
3391+
GC_SET_REFCOUNT(&cdata->std, 1);
3392+
GC_TYPE_INFO(&cdata->std) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT);
3393+
cdata->std.extra_flags = 0;
3394+
cdata->std.ce = zend_ffi_api->cdata_ce;
3395+
cdata->std.handlers = zend_ffi_api->cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */
3396+
cdata->std.properties = NULL;
3397+
zend_objects_store_put(&cdata->std);
3398+
cdata->type = type;
3399+
cdata->flags = 0;
3400+
cdata->ptr = (void*)&cdata->ptr_holder;
3401+
cdata->ptr_holder = ptr;
3402+
return cdata;
3403+
}
3404+
33853405
static void ZEND_FASTCALL zend_jit_zval_ffi_ptr(zval *zv, zend_ffi_type *type, void *ptr)
33863406
{
33873407
ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_POINTER);
33883408
if (ptr) {
3389-
zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata));
3390-
3391-
// inlined zend_ffi_object_init()
3392-
GC_SET_REFCOUNT(&cdata->std, 1);
3393-
GC_TYPE_INFO(&cdata->std) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT);
3394-
cdata->std.extra_flags = 0;
3395-
cdata->std.ce = zend_ffi_api->cdata_ce;
3396-
cdata->std.handlers = zend_ffi_api->cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */
3397-
cdata->std.properties = NULL;
3398-
zend_objects_store_put(&cdata->std);
3399-
cdata->type = type;
3400-
cdata->flags = 0;
3401-
cdata->ptr = (void*)&cdata->ptr_holder;
3402-
cdata->ptr_holder = ptr;
3409+
zend_ffi_cdata *cdata = zend_jit_ffi_create_ptr(type, ptr);
34033410
ZVAL_OBJ(zv, &cdata->std);
34043411
} else {
34053412
ZVAL_NULL(zv);

ext/opcache/jit/zend_jit_ir.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3107,6 +3107,7 @@ static void zend_jit_setup_disasm(void)
31073107
REGISTER_HELPER(zend_jit_zval_ffi_obj);
31083108
REGISTER_HELPER(zend_jit_zval_ffi_addr);
31093109
REGISTER_HELPER(zend_jit_zval_ffi_addr_var);
3110+
REGISTER_HELPER(zend_jit_ffi_create_ptr);
31103111
#endif
31113112

31123113
#ifndef ZTS

ext/opcache/jit/zend_jit_ir_ffi.c

Lines changed: 172 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,40 @@ static ir_ref zend_jit_ffi_guard(zend_jit_ctx *jit,
5151
zend_ffi_type *ffi_type,
5252
zend_jit_ffi_info *ffi_info);
5353

54+
static int zend_jit_ffi_write(zend_jit_ctx *jit,
55+
zend_ffi_type *ffi_type,
56+
ir_ref ptr,
57+
uint32_t val_info,
58+
zend_jit_addr val_addr,
59+
zend_ffi_type *val_ffi_type,
60+
zend_jit_addr res_addr);
61+
62+
static ir_ref zend_jit_calloc(zend_jit_ctx *jit,
63+
size_t size,
64+
bool persistent,
65+
const zend_op *opline,
66+
const zend_op_array *op_array)
67+
{
68+
ir_ref ref;
69+
70+
if (persistent) {
71+
ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(calloc), ir_CONST_LONG(1), ir_CONST_ADDR(size));
72+
} else {
73+
#if ZEND_DEBUG
74+
ref = ir_CALL_6(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc),
75+
ir_CONST_LONG(1),
76+
ir_CONST_ADDR(size),
77+
op_array->filename ? ir_CONST_ADDR(op_array->filename->val) : IR_NULL,
78+
ir_CONST_U32(opline ? opline->lineno : 0),
79+
IR_NULL,
80+
ir_CONST_U32(0));
81+
#else
82+
ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc), ir_CONST_LONG(1), ir_CONST_ADDR(size));
83+
#endif
84+
}
85+
return ref;
86+
}
87+
5488
static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit,
5589
const zend_op *opline,
5690
const zend_op_array *op_array,
@@ -349,10 +383,135 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit,
349383
} else if (opline->op2.num == 2) {
350384
ZEND_ASSERT(opline->op1_type == IS_CONST);
351385
SET_STACK_TYPE(stack, 1, zend_is_true(RT_CONSTANT(opline, opline->op1)) ? IS_TRUE : IS_FALSE, 0);
352-
} else if (opline->op2.num == 3) {
386+
} else {
387+
ZEND_ASSERT(opline->op2.num == 3);
353388
ZEND_ASSERT(opline->op1_type == IS_CONST);
354389
SET_STACK_TYPE(stack, 2, zend_is_true(RT_CONSTANT(opline, opline->op1)) ? IS_TRUE : IS_FALSE, 0);
355390
}
391+
} else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_CAST) {
392+
if (opline->op2.num == 1) {
393+
if (op1_ffi_type && (opline->op1_type & (IS_VAR|IS_TMP_VAR))) {
394+
arg_flags |= ZREG_FFI_ZVAL_DTOR;
395+
if (op1_info & MAY_BE_REF) {
396+
arg_flags |= ZREG_FFI_ZVAL_DEREF;
397+
}
398+
ref = jit_Z_PTR(jit, op1_addr);
399+
SET_STACK_TYPE(stack, 0, IS_OBJECT, 0);
400+
SET_STACK_REF_EX(stack, 0, ref, arg_flags);
401+
}
402+
} else {
403+
ZEND_ASSERT(opline->op2.num == 2);
404+
405+
/* Call FFI::cast() right here and pass result */
406+
if (op1_ffi_type) {
407+
408+
ref = jit_Z_PTR(jit, op1_addr);
409+
if (op1_info & MAY_BE_REF) {
410+
ir_ref if_ref = ir_IF(ir_EQ(jit_Z_TYPE(jit, op1_addr), ir_CONST_U8(IS_REFERENCE)));
411+
ir_IF_TRUE(if_ref);
412+
ir_ref ref2 = jit_Z_PTR_ref(jit, ir_ADD_OFFSET(ref, offsetof(zend_reference, val)));
413+
ir_MERGE_WITH_EMPTY_FALSE(if_ref);
414+
ref = ir_PHI_2(IR_ADDR, ref2, ref);
415+
}
416+
417+
if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER
418+
&& type->kind != ZEND_FFI_TYPE_POINTER
419+
&& ZEND_FFI_TYPE(op1_ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID) {
420+
/* automatically dereference void* pointers ??? */
421+
// JIT: cdata->ptr = *(void**)arg->ptr
422+
ref = ir_LOAD_A(jit_FFI_CDATA_PTR(jit, ref));
423+
ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type));
424+
} else if (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY
425+
&& type->kind == ZEND_FFI_TYPE_POINTER
426+
&& zend_ffi_api->is_compatible_type(ZEND_FFI_TYPE(op1_ffi_type->array.type), ZEND_FFI_TYPE(type->pointer.type))) {
427+
ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_ffi_create_ptr),
428+
ir_CONST_ADDR(type), jit_FFI_CDATA_PTR(jit, ref));
429+
SET_STACK_TYPE(stack, 1, IS_OBJECT, 0);
430+
SET_STACK_REF_EX(stack, 1, ref, 0);
431+
} else if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER
432+
&& type->kind == ZEND_FFI_TYPE_ARRAY
433+
&& zend_ffi_api->is_compatible_type(ZEND_FFI_TYPE(op1_ffi_type->pointer.type), ZEND_FFI_TYPE(type->array.type))) {
434+
// JIT: cdata->ptr = *arg->ptr;
435+
ref = ir_LOAD_A(jit_FFI_CDATA_PTR(jit, ref));
436+
ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type));
437+
} else if (type->size > op1_ffi_type->size) {
438+
ZEND_ASSERT(type->size <= op1_ffi_type->size);
439+
} else {
440+
// TODO: holder usage ???
441+
ref = jit_FFI_CDATA_PTR(jit, ref);
442+
ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type));
443+
}
444+
SET_STACK_TYPE(stack, 1, IS_OBJECT, 0);
445+
SET_STACK_REF_EX(stack, 1, ref, 0);
446+
if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
447+
/* release argument and transfer ownweship */
448+
ir_ref src = jit_Z_PTR(jit, op1_addr);
449+
450+
// JIT: if (!GC_DELREF(src))
451+
ir_ref if_not_zero = ir_IF(jit_GC_DELREF(jit, src));
452+
ir_IF_FALSE(if_not_zero);
453+
454+
// JIT: if (Z_TYPE_P(arg) == IS_REFERENCE)
455+
ir_ref if_ref = ir_IF(ir_EQ(jit_Z_TYPE(jit, op1_addr), ir_CONST_U8(IS_REFERENCE)));
456+
ir_IF_TRUE(if_ref);
457+
458+
// JIT: arg = Z_PTR(Z_REFVAL_P(arg));
459+
ir_ref src_obj = jit_Z_PTR_ref(jit, ir_ADD_OFFSET(src, offsetof(zend_reference, val)));
460+
461+
// JIT: if (GC_REFCOUNT(arg_obj) == 1)
462+
ir_ref if_one = ir_IF(ir_EQ(jit_GC_REFCOUNT(jit, src_obj), ir_CONST_U32(1)));
463+
ir_IF_TRUE(if_one);
464+
ir_MERGE_WITH_EMPTY_FALSE(if_ref);
465+
src_obj = ir_PHI_2(IR_ADDR, src_obj, src);
466+
467+
// JIT: if (old_cdata->flags & ZEND_FFI_FLAG_OWNED)
468+
ir_ref a = ir_ADD_OFFSET(src_obj, offsetof(zend_ffi_cdata, flags));
469+
ir_ref f = ir_LOAD_U32(a);
470+
ir_ref if_owned = ir_IF(ir_AND_U32(f, ir_CONST_U32(ZEND_FFI_FLAG_OWNED)));
471+
ir_IF_TRUE(if_owned);
472+
473+
/* transfer ownership */
474+
// JIT: old_cdata->flags &= ~ZEND_FFI_FLAG_OWNED;
475+
ir_STORE(a, ir_AND_U32(f, ir_CONST_U32(~ZEND_FFI_FLAG_OWNED)));
476+
// JIT: cdata->flags |= ZEND_FFI_FLAG_OWNED;
477+
a = ir_ADD_OFFSET(ref, offsetof(zend_ffi_cdata, flags));
478+
ir_STORE(a, ir_OR_U32(ir_LOAD_U32(a), ir_CONST_U32(ZEND_FFI_FLAG_OWNED)));
479+
480+
ir_MERGE_WITH_EMPTY_FALSE(if_owned);
481+
ir_MERGE_WITH_EMPTY_FALSE(if_one);
482+
483+
/* release */
484+
jit_ZVAL_DTOR(jit, src, op1_info, opline);
485+
ir_MERGE_WITH_EMPTY_TRUE(if_not_zero);
486+
}
487+
} else if (type->kind < ZEND_FFI_TYPE_POINTER) {
488+
/* numeric conversion */
489+
ref = zend_jit_calloc(jit, type->size, 0, opline, op_array);
490+
if (!zend_jit_ffi_write(jit, type, ref, op1_info, op1_addr, op1_ffi_type, 0)) {
491+
return 0;
492+
}
493+
ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type));
494+
ir_STORE(ir_ADD_OFFSET(ref, offsetof(zend_ffi_cdata, flags)), ir_CONST_U32(ZEND_FFI_FLAG_OWNED));
495+
SET_STACK_TYPE(stack, 1, IS_OBJECT, 0);
496+
SET_STACK_REF_EX(stack, 1, ref, 0);
497+
} else if (type->kind == ZEND_FFI_TYPE_POINTER) {
498+
if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_NULL) {
499+
ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_ffi_create_ptr),
500+
ir_CONST_ADDR(type), IR_NULL);
501+
SET_STACK_TYPE(stack, 1, IS_OBJECT, 0);
502+
SET_STACK_REF_EX(stack, 1, ref, 0);
503+
} else if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) {
504+
ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_ffi_create_ptr),
505+
ir_CONST_ADDR(type), jit_Z_LVAL(jit, op1_addr));
506+
SET_STACK_TYPE(stack, 1, IS_OBJECT, 0);
507+
SET_STACK_REF_EX(stack, 1, ref, 0);
508+
} else {
509+
ZEND_UNREACHABLE();
510+
}
511+
} else {
512+
ZEND_UNREACHABLE();
513+
}
514+
}
356515
} else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) {
357516
ZEND_ASSERT(opline->op2.num == 1);
358517
if (op1_ffi_type && (opline->op1_type & (IS_VAR|IS_TMP_VAR))) {
@@ -811,26 +970,22 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit,
811970
flags |= ZEND_FFI_FLAG_PERSISTENT;
812971
}
813972
// TODO: ZEND_FFI_FLAG_CONST flag ???
814-
if (flags & ZEND_FFI_FLAG_PERSISTENT) {
815-
ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(calloc), ir_CONST_LONG(1), ir_CONST_ADDR(type->size));
816-
} else {
817-
#if ZEND_DEBUG
818-
ref = ir_CALL_6(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc),
819-
ir_CONST_LONG(1),
820-
ir_CONST_ADDR(type->size),
821-
op_array->filename ? ir_CONST_ADDR(op_array->filename->val) : IR_NULL,
822-
ir_CONST_U32(opline ? opline->lineno : 0),
823-
IR_NULL,
824-
ir_CONST_U32(0));
825-
#else
826-
ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc), ir_CONST_LONG(1), ir_CONST_ADDR(type->size));
827-
#endif
828-
}
973+
ref = zend_jit_calloc(jit, type->size, (flags & ZEND_FFI_FLAG_PERSISTENT) != 0, opline, op_array);
829974
ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type));
830975
ir_STORE(ir_ADD_OFFSET(ref, offsetof(zend_ffi_cdata, flags)), ir_CONST_U32(flags));
831976
jit_set_Z_PTR(jit, res_addr, ref);
832977
jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX);
833978
}
979+
} else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_CAST) {
980+
ZEND_ASSERT(STACK_TYPE(stack, 1) == IS_OBJECT);
981+
ref = STACK_REF(stack, 1);
982+
if (res_addr) {
983+
jit_set_Z_PTR(jit, res_addr, ref);
984+
jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX);
985+
} else {
986+
// TODO: release object ???
987+
ZEND_UNREACHABLE();
988+
}
834989
} else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) {
835990
if (res_addr) {
836991
ref = ir_CONST_ADDR(type);
@@ -1060,6 +1215,7 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit,
10601215

10611216
if (!TRACE_FRAME_FFI_FUNC(call)
10621217
|| TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_NEW
1218+
|| TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_CAST
10631219
|| TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) {
10641220
func_ref = (intptr_t)(void*)call->ce;
10651221
if (func_ref && !IR_IS_CONST_REF(func_ref)) {

0 commit comments

Comments
 (0)