@@ -2081,6 +2081,8 @@ static void zend_check_live_ranges(zend_op *opline) /* {{{ */
2081
2081
} else if (opline -> opcode == ZEND_FAST_RET ) {
2082
2082
/* fast_calls don't have to be destroyed */
2083
2083
} else if (opline -> opcode == ZEND_CASE ||
2084
+ opline -> opcode == ZEND_SWITCH_LONG ||
2085
+ opline -> opcode == ZEND_SWITCH_STRING ||
2084
2086
opline -> opcode == ZEND_FE_FETCH_R ||
2085
2087
opline -> opcode == ZEND_FE_FETCH_RW ||
2086
2088
opline -> opcode == ZEND_FE_FREE ||
@@ -4603,6 +4605,58 @@ void zend_compile_if(zend_ast *ast) /* {{{ */
4603
4605
}
4604
4606
/* }}} */
4605
4607
4608
+ static zend_uchar determine_switch_jumptable_type (zend_ast_list * cases ) {
4609
+ uint32_t i ;
4610
+ zend_uchar common_type = IS_UNDEF ;
4611
+ for (i = 0 ; i < cases -> children ; i ++ ) {
4612
+ zend_ast * case_ast = cases -> child [i ];
4613
+ zend_ast * * cond_ast = & case_ast -> child [0 ];
4614
+ zval * cond_zv ;
4615
+ if (!case_ast -> child [0 ]) {
4616
+ /* Skip default clause */
4617
+ continue ;
4618
+ }
4619
+
4620
+ zend_eval_const_expr (cond_ast );
4621
+ if ((* cond_ast )-> kind != ZEND_AST_ZVAL ) {
4622
+ /* Non-constant case */
4623
+ return IS_UNDEF ;
4624
+ }
4625
+
4626
+ cond_zv = zend_ast_get_zval (case_ast -> child [0 ]);
4627
+ if (Z_TYPE_P (cond_zv ) != IS_LONG && Z_TYPE_P (cond_zv ) != IS_STRING ) {
4628
+ /* We only optimize switched on integers and strings */
4629
+ return IS_UNDEF ;
4630
+ }
4631
+
4632
+ if (common_type == IS_UNDEF ) {
4633
+ common_type = Z_TYPE_P (cond_zv );
4634
+ } else if (common_type != Z_TYPE_P (cond_zv )) {
4635
+ /* Non-uniform case types */
4636
+ return IS_UNDEF ;
4637
+ }
4638
+
4639
+ if (Z_TYPE_P (cond_zv ) == IS_STRING
4640
+ && is_numeric_string (Z_STRVAL_P (cond_zv ), Z_STRLEN_P (cond_zv ), NULL , NULL , 0 )) {
4641
+ /* Numeric strings cannot be compared with a simple hash lookup */
4642
+ return IS_UNDEF ;
4643
+ }
4644
+ }
4645
+
4646
+ return common_type ;
4647
+ }
4648
+
4649
+ static zend_bool should_use_jumptable (zend_ast_list * cases , zend_uchar jumptable_type ) {
4650
+ /* Thresholds are chosen based on when the average switch time for equidistributed
4651
+ * input becomes smaller when using the jumptable optimization. */
4652
+ if (jumptable_type == IS_LONG ) {
4653
+ return cases -> children >= 5 ;
4654
+ } else {
4655
+ ZEND_ASSERT (jumptable_type == IS_STRING );
4656
+ return cases -> children >= 2 ;
4657
+ }
4658
+ }
4659
+
4606
4660
void zend_compile_switch (zend_ast * ast ) /* {{{ */
4607
4661
{
4608
4662
zend_ast * expr_ast = ast -> child [0 ];
@@ -4613,7 +4667,9 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
4613
4667
4614
4668
znode expr_node , case_node ;
4615
4669
zend_op * opline ;
4616
- uint32_t * jmpnz_opnums , opnum_default_jmp ;
4670
+ uint32_t * jmpnz_opnums , opnum_default_jmp , opnum_switch ;
4671
+ zend_uchar jumptable_type ;
4672
+ HashTable * jumptable = NULL ;
4617
4673
4618
4674
zend_compile_expr (& expr_node , expr_ast );
4619
4675
@@ -4622,6 +4678,24 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
4622
4678
case_node .op_type = IS_TMP_VAR ;
4623
4679
case_node .u .op .var = get_temporary_variable (CG (active_op_array ));
4624
4680
4681
+ jumptable_type = determine_switch_jumptable_type (cases );
4682
+ if (jumptable_type != IS_UNDEF && should_use_jumptable (cases , jumptable_type )) {
4683
+ znode jumptable_op ;
4684
+
4685
+ ALLOC_HASHTABLE (jumptable );
4686
+ zend_hash_init (jumptable , cases -> children , NULL , NULL , 0 );
4687
+ jumptable_op .op_type = IS_CONST ;
4688
+ ZVAL_ARR (& jumptable_op .u .constant , jumptable );
4689
+
4690
+ opline = zend_emit_op (NULL ,
4691
+ jumptable_type == IS_LONG ? ZEND_SWITCH_LONG : ZEND_SWITCH_STRING ,
4692
+ & expr_node , & jumptable_op );
4693
+ if (opline -> op1_type == IS_CONST ) {
4694
+ zval_copy_ctor (CT_CONSTANT (opline -> op1 ));
4695
+ }
4696
+ opnum_switch = opline - CG (active_op_array )-> opcodes ;
4697
+ }
4698
+
4625
4699
jmpnz_opnums = safe_emalloc (sizeof (uint32_t ), cases -> children , 0 );
4626
4700
for (i = 0 ; i < cases -> children ; ++ i ) {
4627
4701
zend_ast * case_ast = cases -> child [i ];
@@ -4666,15 +4740,39 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
4666
4740
4667
4741
if (cond_ast ) {
4668
4742
zend_update_jump_target_to_next (jmpnz_opnums [i ]);
4743
+
4744
+ if (jumptable ) {
4745
+ zval * cond_zv = zend_ast_get_zval (cond_ast );
4746
+ zval jmp_target ;
4747
+ ZVAL_LONG (& jmp_target , get_next_op_number (CG (active_op_array )));
4748
+
4749
+ ZEND_ASSERT (Z_TYPE_P (cond_zv ) == jumptable_type );
4750
+ if (Z_TYPE_P (cond_zv ) == IS_LONG ) {
4751
+ zend_hash_index_add (jumptable , Z_LVAL_P (cond_zv ), & jmp_target );
4752
+ } else {
4753
+ ZEND_ASSERT (Z_TYPE_P (cond_zv ) == IS_STRING );
4754
+ zend_hash_add (jumptable , Z_STR_P (cond_zv ), & jmp_target );
4755
+ }
4756
+ }
4669
4757
} else {
4670
4758
zend_update_jump_target_to_next (opnum_default_jmp );
4759
+
4760
+ if (jumptable ) {
4761
+ opline = & CG (active_op_array )-> opcodes [opnum_switch ];
4762
+ opline -> extended_value = get_next_op_number (CG (active_op_array ));
4763
+ }
4671
4764
}
4672
4765
4673
4766
zend_compile_stmt (stmt_ast );
4674
4767
}
4675
4768
4676
4769
if (!has_default_case ) {
4677
4770
zend_update_jump_target_to_next (opnum_default_jmp );
4771
+
4772
+ if (jumptable ) {
4773
+ opline = & CG (active_op_array )-> opcodes [opnum_switch ];
4774
+ opline -> extended_value = get_next_op_number (CG (active_op_array ));
4775
+ }
4678
4776
}
4679
4777
4680
4778
zend_end_loop (get_next_op_number (CG (active_op_array )), & expr_node );
0 commit comments