Skip to content

Commit 78d56e0

Browse files
authored
JIT: Optimize array checks in comparisons (#24084)
There are two optimizations: 1. This patch implements the JIT counterpart of the VM change 2d6b869 to optimize comparisons against empty arrays. 2. This patch ensures that if a type can be array, the element count check happens inline rather than going to a slow path. PHP demo code for #1: ```php function f(array $a) { if ($a) return 0; return 1; } ``` Before #1 (for func JIT in this case): ```asm .L1: movq $0x418d88c0, (%r14) ; EX(opline) = opline leaq 0x50(%r14), %rdi ; arg = &CV0($a) movabsq $zend_is_true, %rax callq *%rax testb %al, %al je .L2 ``` After #1: ```asm .L1: movq 0x50(%r14), %rax ; Z_ARRVAL($a) cmpl $0, 0x1c(%rax) ; compare nNumOfElements je .L2 ``` PHP demo code for #2: ```php function is_empty(array $a) { if ($a === []) return 1; return 2; } ``` Before #2: ```asm .L1: leaq 0x50(%r14), %rdi ; arg1 = &CV0($a) movl $0x418d8ad8, %esi ; arg2 = &literal [] movabsq $zend_is_identical, %rax callq *%rax testb %al, %al je .L2 ``` After #2: ```asm .L1: movq 0x50(%r14), %rax ; Z_ARRVAL($a) cmpl $0, 0x1c(%rax) ; compare nNumOfElements jne .L2 ``` It ain't much, but it should help. I think many small frontend improvements can make a nice change in total together.
1 parent 222c13f commit 78d56e0

1 file changed

Lines changed: 29 additions & 2 deletions

File tree

‎ext/opcache/jit/zend_jit_ir.c‎

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7263,6 +7263,13 @@ static int zend_jit_cmp(zend_jit_ctx *jit,
72637263
return 1;
72647264
}
72657265

7266+
static bool zend_jit_is_const_empty_array(zend_jit_addr addr)
7267+
{
7268+
return Z_MODE(addr) == IS_CONST_ZVAL
7269+
&& Z_TYPE_P(Z_ZV(addr)) == IS_ARRAY
7270+
&& zend_hash_num_elements(Z_ARRVAL_P(Z_ZV(addr))) == 0;
7271+
}
7272+
72667273
static int zend_jit_identical(zend_jit_ctx *jit,
72677274
const zend_op *opline,
72687275
uint32_t op1_info,
@@ -7424,6 +7431,14 @@ static int zend_jit_identical(zend_jit_ctx *jit,
74247431
zval *val = Z_ZV(op2_addr);
74257432

74267433
ref = ir_EQ(jit_Z_TYPE(jit, op1_addr), ir_CONST_U8(Z_TYPE_P(val)));
7434+
} else if (zend_jit_is_const_empty_array(op1_addr) && (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
7435+
// JIT: zend_hash_num_elements(Z_ARRVAL_P(op2)) == 0
7436+
ref = ir_EQ(ir_LOAD_U32(ir_ADD_OFFSET(jit_Z_PTR(jit, op2_addr), offsetof(HashTable, nNumOfElements))),
7437+
ir_CONST_U32(0));
7438+
} else if (zend_jit_is_const_empty_array(op2_addr) && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
7439+
// JIT: zend_hash_num_elements(Z_ARRVAL_P(op1)) == 0
7440+
ref = ir_EQ(ir_LOAD_U32(ir_ADD_OFFSET(jit_Z_PTR(jit, op1_addr), offsetof(HashTable, nNumOfElements))),
7441+
ir_CONST_U32(0));
74277442
} else {
74287443
if (Z_MODE(op1_addr) == IS_REG) {
74297444
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
@@ -7762,8 +7777,20 @@ static int zend_jit_bool_jmpznz(zend_jit_ctx *jit, const zend_op *opline, uint32
77627777
}
77637778

77647779
if (op1_info & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))) {
7765-
jit_SET_EX_OPLINE(jit, opline);
7766-
ref = ir_CALL_1(IR_BOOL, ir_CONST_FC_FUNC(zend_is_true), jit_ZVAL_ADDR(jit, op1_addr));
7780+
if ((op1_info & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))) == MAY_BE_ARRAY) {
7781+
/* CV deref handled above. These opcodes only take CONST|TMP|CV operands, and TMPs never hold references. */
7782+
ZEND_ASSERT(opline->op1_type == IS_CV || !(op1_info & MAY_BE_REF));
7783+
// JIT: zend_hash_num_elements(Z_ARRVAL_P(op1)) != 0
7784+
ref = ir_NE(ir_LOAD_U32(ir_ADD_OFFSET(jit_Z_PTR(jit, op1_addr), offsetof(HashTable, nNumOfElements))),
7785+
ir_CONST_U32(0));
7786+
if (opline->op1_type == IS_CV) {
7787+
/* The check above can't throw, and a CV isn't freed. */
7788+
may_throw = 0;
7789+
}
7790+
} else {
7791+
jit_SET_EX_OPLINE(jit, opline);
7792+
ref = ir_CALL_1(IR_BOOL, ir_CONST_FC_FUNC(zend_is_true), jit_ZVAL_ADDR(jit, op1_addr));
7793+
}
77677794
jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, NULL);
77687795
if (may_throw) {
77697796
zend_jit_check_exception_undef_result(jit, opline);

0 commit comments

Comments
 (0)