Skip to content

Commit b1577a3

Browse files
committed
Optimize false === is_string($x) in opcache
This optimization acts on opcodes that are guaranteed to return booleans. (It's based on the below case for ZEND_BOOL and ZEND_BOOL_NOT) Code such as `return $this->prop !== null` will benefit from it when opcache is enabled. A contrived example of a worst-case scenario is below. ```php // Before this PR: Took 0.459 seconds // After this PR: Takes 0.362 seconds loop_is_float(0, 20000000); function loop_is_float($a, $b) { $values = []; for ($i = $a; $i < $b; $i++) { $values[false === is_float($i)] = true; } return $values; } ``` This PR uses bit operations, so that this optimization will also work for `false !== is_string($x)` and any future combinations generated for ZEND_TYPE_CHECK. Similar to phpGH-4906 (can be merged independently)
1 parent 36afe4e commit b1577a3

File tree

1 file changed

+130
-1
lines changed

1 file changed

+130
-1
lines changed

‎ext/opcache/Optimizer/block_pass.c

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,136 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
403403
goto optimize_bool;
404404
}
405405
break;
406-
406+
case ZEND_TYPE_CHECK:
407+
/* Optimize checks such as e === true, e !== true, e !== false, etc. when e is from an opcode that only results in booleans */
408+
/* If opcache combines ZEND_TYPE_CHECKs into is_true_or_string($x) in the future, this is designed to properly handle that. */
409+
if ((opline->extended_value & MAY_BE_FALSE) ^ (opline->extended_value & MAY_BE_TRUE)) {
410+
if (opline->op1_type == IS_TMP_VAR &&
411+
!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
412+
src = VAR_SOURCE(opline->op1);
413+
if (src) {
414+
switch (src->opcode) {
415+
case ZEND_BOOL_NOT:
416+
VAR_SOURCE(opline->op1) = NULL;
417+
COPY_NODE(opline->op1, src->op1);
418+
opline->opcode = (opline->extended_value & MAY_BE_FALSE) ? ZEND_BOOL : ZEND_BOOL_NOT;
419+
MAKE_NOP(src);
420+
++(*opt_count);
421+
goto optimize_bool;
422+
case ZEND_BOOL:
423+
VAR_SOURCE(opline->op1) = NULL;
424+
COPY_NODE(opline->op1, src->op1);
425+
opline->opcode = (opline->extended_value & MAY_BE_FALSE) ? ZEND_BOOL_NOT : ZEND_BOOL;
426+
MAKE_NOP(src);
427+
++(*opt_count);
428+
goto optimize_bool;
429+
case ZEND_IS_EQUAL:
430+
if (opline->extended_value & MAY_BE_FALSE) {
431+
src->opcode = ZEND_IS_NOT_EQUAL;
432+
}
433+
COPY_NODE(src->result, opline->result);
434+
SET_VAR_SOURCE(src);
435+
MAKE_NOP(opline);
436+
++(*opt_count);
437+
break;
438+
case ZEND_IS_NOT_EQUAL:
439+
if (opline->extended_value & MAY_BE_FALSE) {
440+
src->opcode = ZEND_IS_EQUAL;
441+
}
442+
COPY_NODE(src->result, opline->result);
443+
SET_VAR_SOURCE(src);
444+
MAKE_NOP(opline);
445+
++(*opt_count);
446+
break;
447+
case ZEND_IS_IDENTICAL:
448+
if (opline->extended_value & MAY_BE_FALSE) {
449+
src->opcode = ZEND_IS_NOT_IDENTICAL;
450+
}
451+
COPY_NODE(src->result, opline->result);
452+
SET_VAR_SOURCE(src);
453+
MAKE_NOP(opline);
454+
++(*opt_count);
455+
break;
456+
case ZEND_IS_NOT_IDENTICAL:
457+
if (opline->extended_value & MAY_BE_FALSE) {
458+
src->opcode = ZEND_IS_IDENTICAL;
459+
}
460+
COPY_NODE(src->result, opline->result);
461+
SET_VAR_SOURCE(src);
462+
MAKE_NOP(opline);
463+
++(*opt_count);
464+
break;
465+
case ZEND_TYPE_CHECK:
466+
if (opline->extended_value & MAY_BE_FALSE) {
467+
if (src->extended_value == MAY_BE_RESOURCE || src->extended_value == (MAY_BE_ANY - MAY_BE_RESOURCE)) {
468+
/* is_resource() is a special case - it returns false if the resource is closed. Don't convert to/from it. */
469+
break;
470+
}
471+
src->extended_value = MAY_BE_ANY - src->extended_value;
472+
}
473+
COPY_NODE(src->result, opline->result);
474+
SET_VAR_SOURCE(src);
475+
MAKE_NOP(opline);
476+
++(*opt_count);
477+
break;
478+
case ZEND_IS_SMALLER:
479+
if (opline->extended_value & MAY_BE_FALSE) {
480+
zend_uchar tmp_type;
481+
uint32_t tmp;
482+
483+
src->opcode = ZEND_IS_SMALLER_OR_EQUAL;
484+
tmp_type = src->op1_type;
485+
src->op1_type = src->op2_type;
486+
src->op2_type = tmp_type;
487+
tmp = src->op1.num;
488+
src->op1.num = src->op2.num;
489+
src->op2.num = tmp;
490+
}
491+
COPY_NODE(src->result, opline->result);
492+
SET_VAR_SOURCE(src);
493+
MAKE_NOP(opline);
494+
++(*opt_count);
495+
break;
496+
case ZEND_IS_SMALLER_OR_EQUAL:
497+
if (opline->extended_value & MAY_BE_FALSE) {
498+
zend_uchar tmp_type;
499+
uint32_t tmp;
500+
501+
src->opcode = ZEND_IS_SMALLER;
502+
tmp_type = src->op1_type;
503+
src->op1_type = src->op2_type;
504+
src->op2_type = tmp_type;
505+
tmp = src->op1.num;
506+
src->op1.num = src->op2.num;
507+
src->op2.num = tmp;
508+
}
509+
COPY_NODE(src->result, opline->result);
510+
SET_VAR_SOURCE(src);
511+
MAKE_NOP(opline);
512+
++(*opt_count);
513+
break;
514+
case ZEND_ISSET_ISEMPTY_CV:
515+
case ZEND_ISSET_ISEMPTY_VAR:
516+
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
517+
case ZEND_ISSET_ISEMPTY_PROP_OBJ:
518+
case ZEND_ISSET_ISEMPTY_STATIC_PROP:
519+
case ZEND_INSTANCEOF:
520+
case ZEND_DEFINED:
521+
case ZEND_IN_ARRAY:
522+
case ZEND_ARRAY_KEY_EXISTS:
523+
if (opline->extended_value & MAY_BE_FALSE) {
524+
break;
525+
}
526+
COPY_NODE(src->result, opline->result);
527+
SET_VAR_SOURCE(src);
528+
MAKE_NOP(opline);
529+
++(*opt_count);
530+
break;
531+
}
532+
}
533+
}
534+
}
535+
break;
407536
case ZEND_BOOL:
408537
case ZEND_BOOL_NOT:
409538
optimize_bool:

0 commit comments

Comments
 (0)