Skip to content

Commit 884f5bc

Browse files
committed
Merge branch 'master' into jit-dynasm
* master: Remove spurious `CG(context).in_finally` dingleberry Forgot to commit test file... Implement jumptable optimization Support more than two successors in opcache CFG Fix JMPZ+JMP => NOP+JMP optimization Fixed bug #74404 (wrong reflection on DateTimeZone::getTransitions) Update NEWS Fixed bug #74402 (segfault on random_bytes, bin2hex, openssl_seal) intergarte furher newer flag in FindFirstFileEx for win7+ only Fixed bug #74400 (phpdbg comparing socket function to int) Fixed condition check Fixed condition check another place Fixed issue php#2466 Invalid integer constant expression error in php.h
2 parents 04a13ea + e92896f commit 884f5bc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+960
-358
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ PHP NEWS
4848

4949
- Date:
5050
. Fixed bug #69587 (DateInterval properties and isset). (jhdxr)
51+
. Fixed bug #74404 (Wrong reflection on DateTimeZone::getTransitions).
52+
(krakjoe)
5153

5254
- DOM:
5355
. Fixed bug #67474 (getElementsByTagNameNS filter on default ns). (aboks)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
Switch on numeric strings
3+
--FILE--
4+
<?php
5+
6+
function test($value) {
7+
switch ($value) {
8+
case "01": return "01";
9+
case "1": return "1";
10+
11+
case " 2": return " 2";
12+
case "2": return "2";
13+
14+
case "10.0": return "10.0";
15+
case "1e1": return "1e1";
16+
17+
default: return "default";
18+
}
19+
}
20+
21+
var_dump(test("1"));
22+
var_dump(test("2"));
23+
var_dump(test("1e1"));
24+
25+
?>
26+
--EXPECT--
27+
string(2) "01"
28+
string(2) " 2"
29+
string(4) "10.0"

Zend/zend_compile.c

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context) /* {{{ */
234234
CG(context).vars_size = 0;
235235
CG(context).literals_size = 0;
236236
CG(context).backpatch_count = 0;
237-
CG(context).in_finally = 0;
238237
CG(context).fast_call_var = -1;
239238
CG(context).try_catch_offset = -1;
240239
CG(context).current_brk_cont = -1;
@@ -2081,6 +2080,8 @@ static void zend_check_live_ranges(zend_op *opline) /* {{{ */
20812080
} else if (opline->opcode == ZEND_FAST_RET) {
20822081
/* fast_calls don't have to be destroyed */
20832082
} else if (opline->opcode == ZEND_CASE ||
2083+
opline->opcode == ZEND_SWITCH_LONG ||
2084+
opline->opcode == ZEND_SWITCH_STRING ||
20842085
opline->opcode == ZEND_FE_FETCH_R ||
20852086
opline->opcode == ZEND_FE_FETCH_RW ||
20862087
opline->opcode == ZEND_FE_FREE ||
@@ -4603,6 +4604,58 @@ void zend_compile_if(zend_ast *ast) /* {{{ */
46034604
}
46044605
/* }}} */
46054606

4607+
static zend_uchar determine_switch_jumptable_type(zend_ast_list *cases) {
4608+
uint32_t i;
4609+
zend_uchar common_type = IS_UNDEF;
4610+
for (i = 0; i < cases->children; i++) {
4611+
zend_ast *case_ast = cases->child[i];
4612+
zend_ast **cond_ast = &case_ast->child[0];
4613+
zval *cond_zv;
4614+
if (!case_ast->child[0]) {
4615+
/* Skip default clause */
4616+
continue;
4617+
}
4618+
4619+
zend_eval_const_expr(cond_ast);
4620+
if ((*cond_ast)->kind != ZEND_AST_ZVAL) {
4621+
/* Non-constant case */
4622+
return IS_UNDEF;
4623+
}
4624+
4625+
cond_zv = zend_ast_get_zval(case_ast->child[0]);
4626+
if (Z_TYPE_P(cond_zv) != IS_LONG && Z_TYPE_P(cond_zv) != IS_STRING) {
4627+
/* We only optimize switched on integers and strings */
4628+
return IS_UNDEF;
4629+
}
4630+
4631+
if (common_type == IS_UNDEF) {
4632+
common_type = Z_TYPE_P(cond_zv);
4633+
} else if (common_type != Z_TYPE_P(cond_zv)) {
4634+
/* Non-uniform case types */
4635+
return IS_UNDEF;
4636+
}
4637+
4638+
if (Z_TYPE_P(cond_zv) == IS_STRING
4639+
&& is_numeric_string(Z_STRVAL_P(cond_zv), Z_STRLEN_P(cond_zv), NULL, NULL, 0)) {
4640+
/* Numeric strings cannot be compared with a simple hash lookup */
4641+
return IS_UNDEF;
4642+
}
4643+
}
4644+
4645+
return common_type;
4646+
}
4647+
4648+
static zend_bool should_use_jumptable(zend_ast_list *cases, zend_uchar jumptable_type) {
4649+
/* Thresholds are chosen based on when the average switch time for equidistributed
4650+
* input becomes smaller when using the jumptable optimization. */
4651+
if (jumptable_type == IS_LONG) {
4652+
return cases->children >= 5;
4653+
} else {
4654+
ZEND_ASSERT(jumptable_type == IS_STRING);
4655+
return cases->children >= 2;
4656+
}
4657+
}
4658+
46064659
void zend_compile_switch(zend_ast *ast) /* {{{ */
46074660
{
46084661
zend_ast *expr_ast = ast->child[0];
@@ -4613,7 +4666,9 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
46134666

46144667
znode expr_node, case_node;
46154668
zend_op *opline;
4616-
uint32_t *jmpnz_opnums, opnum_default_jmp;
4669+
uint32_t *jmpnz_opnums, opnum_default_jmp, opnum_switch;
4670+
zend_uchar jumptable_type;
4671+
HashTable *jumptable = NULL;
46174672

46184673
zend_compile_expr(&expr_node, expr_ast);
46194674

@@ -4622,6 +4677,24 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
46224677
case_node.op_type = IS_TMP_VAR;
46234678
case_node.u.op.var = get_temporary_variable(CG(active_op_array));
46244679

4680+
jumptable_type = determine_switch_jumptable_type(cases);
4681+
if (jumptable_type != IS_UNDEF && should_use_jumptable(cases, jumptable_type)) {
4682+
znode jumptable_op;
4683+
4684+
ALLOC_HASHTABLE(jumptable);
4685+
zend_hash_init(jumptable, cases->children, NULL, NULL, 0);
4686+
jumptable_op.op_type = IS_CONST;
4687+
ZVAL_ARR(&jumptable_op.u.constant, jumptable);
4688+
4689+
opline = zend_emit_op(NULL,
4690+
jumptable_type == IS_LONG ? ZEND_SWITCH_LONG : ZEND_SWITCH_STRING,
4691+
&expr_node, &jumptable_op);
4692+
if (opline->op1_type == IS_CONST) {
4693+
zval_copy_ctor(CT_CONSTANT(opline->op1));
4694+
}
4695+
opnum_switch = opline - CG(active_op_array)->opcodes;
4696+
}
4697+
46254698
jmpnz_opnums = safe_emalloc(sizeof(uint32_t), cases->children, 0);
46264699
for (i = 0; i < cases->children; ++i) {
46274700
zend_ast *case_ast = cases->child[i];
@@ -4666,15 +4739,39 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
46664739

46674740
if (cond_ast) {
46684741
zend_update_jump_target_to_next(jmpnz_opnums[i]);
4742+
4743+
if (jumptable) {
4744+
zval *cond_zv = zend_ast_get_zval(cond_ast);
4745+
zval jmp_target;
4746+
ZVAL_LONG(&jmp_target, get_next_op_number(CG(active_op_array)));
4747+
4748+
ZEND_ASSERT(Z_TYPE_P(cond_zv) == jumptable_type);
4749+
if (Z_TYPE_P(cond_zv) == IS_LONG) {
4750+
zend_hash_index_add(jumptable, Z_LVAL_P(cond_zv), &jmp_target);
4751+
} else {
4752+
ZEND_ASSERT(Z_TYPE_P(cond_zv) == IS_STRING);
4753+
zend_hash_add(jumptable, Z_STR_P(cond_zv), &jmp_target);
4754+
}
4755+
}
46694756
} else {
46704757
zend_update_jump_target_to_next(opnum_default_jmp);
4758+
4759+
if (jumptable) {
4760+
opline = &CG(active_op_array)->opcodes[opnum_switch];
4761+
opline->extended_value = get_next_op_number(CG(active_op_array));
4762+
}
46714763
}
46724764

46734765
zend_compile_stmt(stmt_ast);
46744766
}
46754767

46764768
if (!has_default_case) {
46774769
zend_update_jump_target_to_next(opnum_default_jmp);
4770+
4771+
if (jumptable) {
4772+
opline = &CG(active_op_array)->opcodes[opnum_switch];
4773+
opline->extended_value = get_next_op_number(CG(active_op_array));
4774+
}
46784775
}
46794776

46804777
zend_end_loop(get_next_op_number(CG(active_op_array)), &expr_node);
@@ -4838,9 +4935,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
48384935

48394936
zend_emit_op(NULL, ZEND_JMP, NULL, NULL);
48404937

4841-
CG(context).in_finally++;
48424938
zend_compile_stmt(finally_ast);
4843-
CG(context).in_finally--;
48444939

48454940
CG(active_op_array)->try_catch_array[try_catch_offset].finally_op = opnum_jmp + 1;
48464941
CG(active_op_array)->try_catch_array[try_catch_offset].finally_end

Zend/zend_compile.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ typedef struct _zend_oparray_context {
191191
int vars_size;
192192
int literals_size;
193193
int backpatch_count;
194-
int in_finally;
195194
uint32_t fast_call_var;
196195
uint32_t try_catch_offset;
197196
int current_brk_cont;

Zend/zend_opcode.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,19 @@ ZEND_API int pass_two(zend_op_array *op_array)
670670
opline->opcode = ZEND_GENERATOR_RETURN;
671671
}
672672
break;
673+
case ZEND_SWITCH_LONG:
674+
case ZEND_SWITCH_STRING:
675+
{
676+
/* absolute indexes to relative offsets */
677+
HashTable *jumptable = Z_ARRVAL_P(CT_CONSTANT(opline->op2));
678+
zval *zv;
679+
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
680+
Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, Z_LVAL_P(zv));
681+
} ZEND_HASH_FOREACH_END();
682+
683+
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
684+
break;
685+
}
673686
}
674687
if (opline->op1_type == IS_CONST) {
675688
ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);

Zend/zend_vm_def.h

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8043,6 +8043,64 @@ ZEND_VM_HANDLER(51, ZEND_MAKE_REF, VAR|CV, UNUSED)
80438043
ZEND_VM_NEXT_OPCODE();
80448044
}
80458045

8046+
ZEND_VM_HANDLER(187, ZEND_SWITCH_LONG, CONST|TMPVAR|CV, CONST, JMP_ADDR)
8047+
{
8048+
USE_OPLINE
8049+
zend_free_op free_op1, free_op2;
8050+
zval *op, *jump_zv;
8051+
HashTable *jumptable;
8052+
8053+
op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
8054+
jumptable = Z_ARRVAL_P(GET_OP2_ZVAL_PTR(BP_VAR_R));
8055+
8056+
if (Z_TYPE_P(op) != IS_LONG) {
8057+
ZVAL_DEREF(op);
8058+
if (Z_TYPE_P(op) != IS_LONG) {
8059+
/* Wrong type, fall back to ZEND_CASE chain */
8060+
ZEND_VM_NEXT_OPCODE();
8061+
}
8062+
}
8063+
8064+
jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op));
8065+
if (jump_zv != NULL) {
8066+
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
8067+
ZEND_VM_CONTINUE();
8068+
} else {
8069+
/* default */
8070+
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
8071+
ZEND_VM_CONTINUE();
8072+
}
8073+
}
8074+
8075+
ZEND_VM_HANDLER(188, ZEND_SWITCH_STRING, CONST|TMPVAR|CV, CONST, JMP_ADDR)
8076+
{
8077+
USE_OPLINE
8078+
zend_free_op free_op1, free_op2;
8079+
zval *op, *jump_zv;
8080+
HashTable *jumptable;
8081+
8082+
op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
8083+
jumptable = Z_ARRVAL_P(GET_OP2_ZVAL_PTR(BP_VAR_R));
8084+
8085+
if (Z_TYPE_P(op) != IS_STRING) {
8086+
ZVAL_DEREF(op);
8087+
if (Z_TYPE_P(op) != IS_STRING) {
8088+
/* Wrong type, fall back to ZEND_CASE chain */
8089+
ZEND_VM_NEXT_OPCODE();
8090+
}
8091+
}
8092+
8093+
jump_zv = zend_hash_find(jumptable, Z_STR_P(op));
8094+
if (jump_zv != NULL) {
8095+
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
8096+
ZEND_VM_CONTINUE();
8097+
} else {
8098+
/* default */
8099+
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
8100+
ZEND_VM_CONTINUE();
8101+
}
8102+
}
8103+
80468104
ZEND_VM_TYPE_SPEC_HANDLER(ZEND_ADD, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_ADD_LONG_NO_OVERFLOW, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
80478105
{
80488106
USE_OPLINE

0 commit comments

Comments
 (0)