Skip to content

Commit f810c6f

Browse files
committed
Improved SSCP integration
1 parent d17ed88 commit f810c6f

File tree

3 files changed

+139
-120
lines changed

3 files changed

+139
-120
lines changed

ext/opcache/Optimizer/dfa_pass.c

Lines changed: 132 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,126 @@ static zend_bool opline_supports_assign_contraction(
319319
return 1;
320320
}
321321

322+
int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
323+
{
324+
zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
325+
int removed_ops = 0;
326+
327+
if (func_info->callee_info) {
328+
zend_call_info *call_info = func_info->callee_info;
329+
static zend_function *in_array_function = NULL;
330+
331+
if (!in_array_function) {
332+
in_array_function = zend_hash_str_find_ptr(CG(function_table), "in_array", sizeof("in_array")-1);
333+
}
334+
do {
335+
if (call_info->callee_func == in_array_function
336+
&& (call_info->caller_init_opline->extended_value == 2
337+
|| (call_info->caller_init_opline->extended_value == 3
338+
&& (call_info->caller_call_opline - 1)->opcode == ZEND_SEND_VAL
339+
&& (call_info->caller_call_opline - 1)->op1_type == IS_CONST))) {
340+
341+
zend_op *send_array;
342+
zend_op *send_needly;
343+
zend_bool strict = 0;
344+
345+
if (call_info->caller_init_opline->extended_value == 2) {
346+
send_array = call_info->caller_call_opline - 1;
347+
send_needly = call_info->caller_call_opline - 2;
348+
} else {
349+
if (zend_is_true(CT_CONSTANT_EX(op_array, (call_info->caller_call_opline - 1)->op1.constant))) {
350+
strict = 1;
351+
}
352+
send_array = call_info->caller_call_opline - 2;
353+
send_needly = call_info->caller_call_opline - 3;
354+
}
355+
356+
if (send_array->opcode == ZEND_SEND_VAL
357+
&& send_array->op1_type == IS_CONST
358+
&& Z_TYPE_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)) == IS_ARRAY
359+
&& (send_needly->opcode == ZEND_SEND_VAL
360+
|| send_needly->opcode == ZEND_SEND_VAR)
361+
) {
362+
int ok = 1;
363+
364+
HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, send_array->op1.constant));
365+
HashTable *dst;
366+
zval *val, tmp;
367+
zend_ulong idx;
368+
369+
ZVAL_TRUE(&tmp);
370+
dst = emalloc(sizeof(HashTable));
371+
zend_hash_init(dst, zend_hash_num_elements(src), NULL, ZVAL_PTR_DTOR, 0);
372+
if (strict) {
373+
ZEND_HASH_FOREACH_VAL(src, val) {
374+
if (Z_TYPE_P(val) == IS_STRING) {
375+
zend_hash_add(dst, Z_STR_P(val), &tmp);
376+
} else if (Z_TYPE_P(val) == IS_LONG) {
377+
zend_hash_index_add(dst, Z_LVAL_P(val), &tmp);
378+
} else {
379+
zend_array_destroy(dst);
380+
ok = 0;
381+
break;
382+
}
383+
} ZEND_HASH_FOREACH_END();
384+
} else {
385+
ZEND_HASH_FOREACH_VAL(src, val) {
386+
if (Z_TYPE_P(val) != IS_STRING || ZEND_HANDLE_NUMERIC(Z_STR_P(val), idx)) {
387+
zend_array_destroy(dst);
388+
ok = 0;
389+
break;
390+
}
391+
zend_hash_add(dst, Z_STR_P(val), &tmp);
392+
} ZEND_HASH_FOREACH_END();
393+
}
394+
395+
if (ok) {
396+
uint32_t op_num = send_needly - op_array->opcodes;
397+
zend_ssa_op *ssa_op = ssa->ops + op_num;
398+
399+
if (ssa_op->op1_use >= 0) {
400+
/* Reconstruct SSA */
401+
int var_num = ssa_op->op1_use;
402+
zend_ssa_var *var = ssa->vars + var_num;
403+
404+
ZEND_ASSERT(ssa_op->op1_def < 0);
405+
zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use);
406+
ssa_op->op1_use = -1;
407+
ssa_op->op1_use_chain = -1;
408+
op_num = call_info->caller_call_opline - op_array->opcodes;
409+
ssa_op = ssa->ops + op_num;
410+
ssa_op->op1_use = var_num;
411+
ssa_op->op1_use_chain = var->use_chain;
412+
var->use_chain = op_num;
413+
}
414+
415+
ZVAL_ARR(&tmp, dst);
416+
417+
/* Update opcode */
418+
call_info->caller_call_opline->opcode = ZEND_IN_ARRAY;
419+
call_info->caller_call_opline->extended_value = strict;
420+
call_info->caller_call_opline->op1_type = send_needly->op1_type;
421+
call_info->caller_call_opline->op1.num = send_needly->op1.num;
422+
call_info->caller_call_opline->op2_type = IS_CONST;
423+
call_info->caller_call_opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
424+
if (call_info->caller_init_opline->extended_value == 3) {
425+
MAKE_NOP(call_info->caller_call_opline - 1);
426+
}
427+
MAKE_NOP(call_info->caller_init_opline);
428+
MAKE_NOP(send_needly);
429+
MAKE_NOP(send_array);
430+
removed_ops++;
431+
432+
}
433+
}
434+
}
435+
call_info = call_info->next_callee;
436+
} while (call_info);
437+
}
438+
439+
return removed_ops;
440+
}
441+
322442
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map)
323443
{
324444
if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
@@ -332,8 +452,18 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
332452
zend_op *opline;
333453
zval tmp;
334454

335-
if (sccp_optimize_op_array(op_array, ssa, call_map)) {
336-
remove_nops = 1;
455+
if (ZEND_OPTIMIZER_PASS_8 & ctx->optimization_level) {
456+
if (sccp_optimize_op_array(op_array, ssa, call_map)) {
457+
remove_nops = 1;
458+
}
459+
if (ZEND_FUNC_INFO(op_array)) {
460+
if (zend_dfa_optimize_calls(op_array, ssa)) {
461+
remove_nops = 1;
462+
}
463+
}
464+
if (ctx->debug_level & ZEND_DUMP_AFTER_SCCP_PASS) {
465+
zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after sccp pass", ssa);
466+
}
337467
}
338468

339469
for (v = op_array->last_var; v < ssa->vars_count; v++) {
@@ -581,122 +711,6 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
581711
}
582712
}
583713

584-
if (ZEND_FUNC_INFO(op_array)) {
585-
zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
586-
587-
if (func_info->callee_info) {
588-
zend_call_info *call_info = func_info->callee_info;
589-
static zend_function *in_array_function = NULL;
590-
591-
if (!in_array_function) {
592-
in_array_function = zend_hash_str_find_ptr(CG(function_table), "in_array", sizeof("in_array")-1);
593-
}
594-
do {
595-
if (call_info->callee_func == in_array_function
596-
&& (call_info->caller_init_opline->extended_value == 2
597-
|| (call_info->caller_init_opline->extended_value == 3
598-
&& (call_info->caller_call_opline - 1)->opcode == ZEND_SEND_VAL
599-
&& (call_info->caller_call_opline - 1)->op1_type == IS_CONST))) {
600-
601-
zend_op *send_array;
602-
zend_op *send_needly;
603-
zend_bool strict = 0;
604-
605-
if (call_info->caller_init_opline->extended_value == 2) {
606-
send_array = call_info->caller_call_opline - 1;
607-
send_needly = call_info->caller_call_opline - 2;
608-
} else {
609-
if (zend_is_true(CT_CONSTANT_EX(op_array, (call_info->caller_call_opline - 1)->op1.constant))) {
610-
strict = 1;
611-
}
612-
send_array = call_info->caller_call_opline - 2;
613-
send_needly = call_info->caller_call_opline - 3;
614-
}
615-
616-
if (send_array->opcode == ZEND_SEND_VAL
617-
&& send_array->op1_type == IS_CONST
618-
&& Z_TYPE_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)) == IS_ARRAY
619-
&& (send_needly->opcode == ZEND_SEND_VAL
620-
|| send_needly->opcode == ZEND_SEND_VAR)
621-
) {
622-
int ok = 1;
623-
624-
HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, send_array->op1.constant));
625-
HashTable *dst;
626-
zval *val, tmp;
627-
zend_ulong idx;
628-
629-
ZVAL_TRUE(&tmp);
630-
dst = emalloc(sizeof(HashTable));
631-
zend_hash_init(dst, zend_hash_num_elements(src), NULL, ZVAL_PTR_DTOR, 0);
632-
if (strict) {
633-
ZEND_HASH_FOREACH_VAL(src, val) {
634-
if (Z_TYPE_P(val) == IS_STRING) {
635-
zend_hash_add(dst, Z_STR_P(val), &tmp);
636-
} else if (Z_TYPE_P(val) == IS_LONG) {
637-
zend_hash_index_add(dst, Z_LVAL_P(val), &tmp);
638-
} else {
639-
zend_array_destroy(dst);
640-
ok = 0;
641-
break;
642-
}
643-
} ZEND_HASH_FOREACH_END();
644-
} else {
645-
ZEND_HASH_FOREACH_VAL(src, val) {
646-
if (Z_TYPE_P(val) != IS_STRING || ZEND_HANDLE_NUMERIC(Z_STR_P(val), idx)) {
647-
zend_array_destroy(dst);
648-
ok = 0;
649-
break;
650-
}
651-
zend_hash_add(dst, Z_STR_P(val), &tmp);
652-
} ZEND_HASH_FOREACH_END();
653-
}
654-
655-
if (ok) {
656-
uint32_t op_num = send_needly - op_array->opcodes;
657-
zend_ssa_op *ssa_op = ssa->ops + op_num;
658-
659-
if (ssa_op->op1_use >= 0) {
660-
/* Reconstruct SSA */
661-
int var_num = ssa_op->op1_use;
662-
zend_ssa_var *var = ssa->vars + var_num;
663-
664-
ZEND_ASSERT(ssa_op->op1_def < 0);
665-
zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use);
666-
ssa_op->op1_use = -1;
667-
ssa_op->op1_use_chain = -1;
668-
op_num = call_info->caller_call_opline - op_array->opcodes;
669-
ssa_op = ssa->ops + op_num;
670-
ssa_op->op1_use = var_num;
671-
ssa_op->op1_use_chain = var->use_chain;
672-
var->use_chain = op_num;
673-
}
674-
675-
ZVAL_ARR(&tmp, dst);
676-
677-
/* Update opcode */
678-
call_info->caller_call_opline->opcode = ZEND_IN_ARRAY;
679-
call_info->caller_call_opline->extended_value = strict;
680-
call_info->caller_call_opline->op1_type = send_needly->op1_type;
681-
call_info->caller_call_opline->op1.num = send_needly->op1.num;
682-
call_info->caller_call_opline->op2_type = IS_CONST;
683-
call_info->caller_call_opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
684-
if (call_info->caller_init_opline->extended_value == 3) {
685-
MAKE_NOP(call_info->caller_call_opline - 1);
686-
}
687-
MAKE_NOP(call_info->caller_init_opline);
688-
MAKE_NOP(send_needly);
689-
MAKE_NOP(send_array);
690-
remove_nops = 1;
691-
692-
}
693-
}
694-
}
695-
call_info = call_info->next_callee;
696-
} while (call_info);
697-
}
698-
}
699-
700714
if (remove_nops) {
701715
zend_ssa_remove_nops(op_array, ssa);
702716
}

ext/opcache/Optimizer/sccp.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1280,9 +1280,13 @@ static int replace_constant_operands(sccp_ctx *ctx) {
12801280
&& var->phi_use_chain == NULL) {
12811281
if (opline->opcode == ZEND_DO_ICALL) {
12821282
/* Call instruction -> remove opcodes that are part of the call */
1283-
zend_call_info *call = ctx->call_map[var->definition];
1283+
zend_call_info *call;
12841284
int i;
12851285

1286+
ZEND_ASSERT(ctx->call_map);
1287+
call = ctx->call_map[var->definition];
1288+
ZEND_ASSERT(call);
1289+
ZEND_ASSERT(call->caller_call_opline == opline);
12861290
zend_ssa_remove_result_def(ssa, ssa_op);
12871291
zend_ssa_remove_instr(ssa, opline, ssa_op);
12881292
zend_ssa_remove_instr(ssa, call->caller_init_opline,

ext/opcache/Optimizer/zend_optimizer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */
3333
#define ZEND_OPTIMIZER_PASS_6 (1<<5) /* DFA based optimization */
3434
#define ZEND_OPTIMIZER_PASS_7 (1<<6) /* CALL GRAPH optimization */
35-
#define ZEND_OPTIMIZER_PASS_8 (1<<7)
35+
#define ZEND_OPTIMIZER_PASS_8 (1<<7) /* SCCP (constant propagation) */
3636
#define ZEND_OPTIMIZER_PASS_9 (1<<8) /* TMP VAR usage */
3737
#define ZEND_OPTIMIZER_PASS_10 (1<<9) /* NOP removal */
3838
#define ZEND_OPTIMIZER_PASS_11 (1<<10) /* Merge equal constants */
@@ -77,6 +77,7 @@
7777
#define ZEND_DUMP_DFA_PHI (1<<26)
7878
#define ZEND_DUMP_DFA_SSA (1<<27)
7979
#define ZEND_DUMP_DFA_SSA_VARS (1<<28)
80+
#define ZEND_DUMP_AFTER_SCCP_PASS (1<<29)
8081

8182
typedef struct _zend_script {
8283
zend_string *filename;

0 commit comments

Comments
 (0)