Skip to content

Commit 471bbed

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 471bbed

File tree

1 file changed

+131
-0
lines changed

1 file changed

+131
-0
lines changed

‎ext/opcache/Optimizer/block_pass.c

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,137 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
404404
}
405405
break;
406406

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

0 commit comments

Comments
 (0)