Skip to content

Commit 2f96eba

Browse files
committed
Optimize for not being indirect/undef/references
Locally, I didn't see that much of a performance boost in absolute terms compared to inlining zend_is_identical (compare+unlikely jump is probably 2 instructions)
1 parent 72ec3a6 commit 2f96eba

File tree

3 files changed

+1096
-90
lines changed

3 files changed

+1096
-90
lines changed

Zend/tests/bug70785.phpt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ try {
2424
undefined_function();
2525
} catch (Exception $e) {
2626
}
27+
try {
28+
$h !== $g; // ZEND_VM_NEXT_OPCODE
29+
undefined_function();
30+
} catch (Exception $e) {
31+
}
2732
?>
2833
okey
2934
--EXPECT--

Zend/zend_vm_def.h

Lines changed: 101 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -449,21 +449,57 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(16, ZEND_IS_IDENTICAL, CONST|TMP|VAR|CV, CONST|T
449449
zend_bool result;
450450

451451
SAVE_OPLINE();
452-
op1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
453-
op2 = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R);
452+
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
453+
op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
454+
ZEND_VM_C_LABEL(compare_values_any_type):
455+
454456
if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) {
455-
/* They are not identical, return false. This has to check for __destruct errors (and undefined variable errors when IS_NULL is possible) */
457+
/* Only VAR can be indirect */
458+
if (UNEXPECTED(((OP1_TYPE & IS_VAR) && Z_TYPE_P(op1) == IS_INDIRECT) || ((OP2_TYPE & IS_VAR) && Z_TYPE_P(op2) == IS_INDIRECT))) {
459+
ZVAL_DEINDIRECT(op1);
460+
ZVAL_DEINDIRECT(op2);
461+
ZEND_VM_C_GOTO(compare_values_any_type);
462+
}
463+
/* Only VAR and CV can be references */
464+
if (UNEXPECTED(((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(op1)) || ((OP2_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(op2)))) {
465+
ZVAL_DEREF(op1);
466+
ZVAL_DEREF(op2);
467+
if (Z_TYPE_P(op1) == Z_TYPE_P(op2)) {
468+
ZEND_VM_C_GOTO(compare_values);
469+
}
470+
}
471+
/* Only CV can be undef */
472+
if (UNEXPECTED(((OP1_TYPE & IS_CV) && Z_ISUNDEF_P(op1)) || ((OP2_TYPE & IS_CV) && Z_ISUNDEF_P(op2)) )) {
473+
/* Convert undef to null, check if they're identical */
474+
if (Z_TYPE_P(op1) == IS_UNDEF) {
475+
op1 = ZVAL_UNDEFINED_OP1();
476+
}
477+
if (Z_TYPE_P(op2) == IS_UNDEF) {
478+
op2 = ZVAL_UNDEFINED_OP2();
479+
}
480+
result = Z_TYPE_P(op1) == Z_TYPE_P(op2);
481+
ZEND_VM_C_GOTO(return_result_maythrow);
482+
}
483+
/* They are not identical, return false. This has to check for __destruct errors */
456484
FREE_OP1();
457485
FREE_OP2();
458486
ZEND_VM_SMART_BRANCH(0, 1);
459487
return;
460488
}
489+
ZEND_VM_C_LABEL(compare_values):
461490
if (Z_TYPE_P(op1) <= IS_TRUE) {
491+
if (((OP1_TYPE & IS_CV) || (OP2_TYPE & IS_CV)) && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
492+
/* They are both undefined - fetch them to emit the undefined variable warnings. */
493+
op1 = ZVAL_UNDEFINED_OP1();
494+
op2 = ZVAL_UNDEFINED_OP2();
495+
ZEND_VM_SMART_BRANCH(1, 1);
496+
return;
497+
}
462498
/* They are identical, return true */
463-
/* This has to check for undefined variable errors when IS_NULL is possible. */
499+
/* This has to check for undefined variable errors when IS_UNDEF is possible. (only warns for IS_CV) */
464500
FREE_OP1();
465501
FREE_OP2();
466-
ZEND_VM_SMART_BRANCH(1, 1);
502+
ZEND_VM_SMART_BRANCH(1, 0);
467503
return;
468504
}
469505
switch (Z_TYPE_P(op1)) {
@@ -491,9 +527,19 @@ ZEND_VM_C_LABEL(free_nothrow):
491527
case IS_OBJECT:
492528
result = (Z_OBJ_P(op1) == Z_OBJ_P(op2));
493529
break;
530+
case IS_REFERENCE:
531+
/* Both are references */
532+
op1 = Z_REFVAL_P(op1);
533+
op2 = Z_REFVAL_P(op2);
534+
ZEND_VM_C_GOTO(compare_values_any_type);
535+
case IS_INDIRECT:
536+
op1 = Z_INDIRECT_P(op1);
537+
op2 = Z_INDIRECT_P(op2);
538+
ZEND_VM_C_GOTO(compare_values_any_type);
494539
default:
495540
result = 1;
496541
}
542+
ZEND_VM_C_LABEL(return_result_maythrow):
497543
/* Check if freeing the operands (e.g. __destruct(), freeing resources (not sure about that), etc threw an exception before setting the result or branching */
498544
FREE_OP1();
499545
FREE_OP2();
@@ -503,26 +549,61 @@ ZEND_VM_C_LABEL(free_nothrow):
503549
ZEND_VM_COLD_CONSTCONST_HANDLER(17, ZEND_IS_NOT_IDENTICAL, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV, SPEC(COMMUTATIVE))
504550
{
505551
USE_OPLINE
506-
zval *op1, *op2;
552+
zval *op1, *op2;
507553
zend_bool result;
508554

509555
SAVE_OPLINE();
510-
op1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
511-
op2 = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R);
556+
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
557+
op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
558+
ZEND_VM_C_LABEL(compare_values_any_type):
512559

513560
if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) {
561+
/* Only VAR can be indirect */
562+
if (UNEXPECTED(((OP1_TYPE & IS_VAR) && Z_TYPE_P(op1) == IS_INDIRECT) || ((OP2_TYPE & IS_VAR) && Z_TYPE_P(op2) == IS_INDIRECT))) {
563+
ZVAL_DEINDIRECT(op1);
564+
ZVAL_DEINDIRECT(op2);
565+
ZEND_VM_C_GOTO(compare_values_any_type);
566+
}
567+
/* Only VAR and CV can be references */
568+
if (UNEXPECTED(((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(op1)) || ((OP2_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(op2)))) {
569+
ZVAL_DEREF(op1);
570+
ZVAL_DEREF(op2);
571+
if (Z_TYPE_P(op1) == Z_TYPE_P(op2)) {
572+
ZEND_VM_C_GOTO(compare_values);
573+
}
574+
}
575+
/* Only CV can be undef */
576+
if (UNEXPECTED(((OP1_TYPE & IS_CV) && Z_ISUNDEF_P(op1)) || ((OP2_TYPE & IS_CV) && Z_ISUNDEF_P(op2)) )) {
577+
/* Convert undef to null, check if they're not identical */
578+
if (Z_TYPE_P(op1) == IS_UNDEF) {
579+
op1 = ZVAL_UNDEFINED_OP1();
580+
}
581+
if (Z_TYPE_P(op2) == IS_UNDEF) {
582+
op2 = ZVAL_UNDEFINED_OP2();
583+
}
584+
result = Z_TYPE_P(op1) != Z_TYPE_P(op2);
585+
ZEND_VM_C_GOTO(return_result_maythrow);
586+
}
514587
/* They are not identical, return true. This has to check for __destruct errors (and undefined variable errors when IS_NULL is possible) */
515588
FREE_OP1();
516589
FREE_OP2();
517590
ZEND_VM_SMART_BRANCH(1, 1);
518591
return;
519592
}
593+
ZEND_VM_C_LABEL(compare_values):
520594
if (Z_TYPE_P(op1) <= IS_TRUE) {
595+
if (((OP1_TYPE & IS_CV) || (OP2_TYPE & IS_CV)) && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
596+
/* They are both undefined - fetch them to emit the undefined variable warnings. */
597+
op1 = ZVAL_UNDEFINED_OP1();
598+
op2 = ZVAL_UNDEFINED_OP2();
599+
ZEND_VM_SMART_BRANCH(0, 1);
600+
return;
601+
}
521602
/* They are identical, return false. */
522-
/* This has to check for undefined variable errors when IS_NULL is possible. */
603+
/* This has to check for undefined variable errors when IS_UNDEF is possible. (only warns for IS_CV) */
523604
FREE_OP1();
524605
FREE_OP2();
525-
ZEND_VM_SMART_BRANCH(0, 1);
606+
ZEND_VM_SMART_BRANCH(0, 0);
526607
return;
527608
}
528609
switch (Z_TYPE_P(op1)) {
@@ -550,9 +631,19 @@ ZEND_VM_C_LABEL(free_nothrow):
550631
case IS_OBJECT:
551632
result = (Z_OBJ_P(op1) != Z_OBJ_P(op2));
552633
break;
634+
case IS_REFERENCE:
635+
/* Both are references */
636+
op1 = Z_REFVAL_P(op1);
637+
op2 = Z_REFVAL_P(op2);
638+
ZEND_VM_C_GOTO(compare_values_any_type);
639+
case IS_INDIRECT:
640+
op1 = Z_INDIRECT_P(op1);
641+
op2 = Z_INDIRECT_P(op2);
642+
ZEND_VM_C_GOTO(compare_values_any_type);
553643
default:
554644
result = 1;
555645
}
646+
ZEND_VM_C_LABEL(return_result_maythrow):
556647
/* Check if freeing the operands (e.g. __destruct(), freeing resources (not sure about that), etc threw an exception before setting the result or branching */
557648
FREE_OP1();
558649
FREE_OP2();

0 commit comments

Comments
 (0)