1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
/* pass 4
* - optimize INIT_FCALL_BY_NAME to DO_FCALL
*/
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
typedef struct _optimizer_call_info {
zend_function *func;
zend_op *opline;
} optimizer_call_info;
static void optimize_func_calls(zend_op_array *op_array, zend_persistent_script *script TSRMLS_DC) {
zend_op *opline = op_array->opcodes;
zend_op *end = opline + op_array->last;
int call = 0;
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
optimizer_call_info *call_stack = ecalloc(op_array->nested_calls + 1, sizeof(optimizer_call_info));
#else
int stack_size = 4;
optimizer_call_info *call_stack = ecalloc(stack_size, sizeof(optimizer_call_info));
#endif
while (opline < end) {
switch (opline->opcode) {
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
if (ZEND_OP2_TYPE(opline) == IS_CONST) {
zend_function *func;
zval *function_name = &op_array->literals[opline->op2.constant + 1].constant;
if ((zend_hash_quick_find(&script->function_table,
Z_STRVAL_P(function_name), Z_STRLEN_P(function_name) + 1,
Z_HASH_P(function_name), (void **)&func) == SUCCESS)) {
call_stack[call].func = func;
}
}
/* break missing intentionally */
case ZEND_NEW:
case ZEND_INIT_METHOD_CALL:
case ZEND_INIT_STATIC_METHOD_CALL:
call_stack[call].opline = opline;
call++;
#if ZEND_EXTENSION_API_NO < PHP_5_5_X_API_NO
if (call == stack_size) {
stack_size += 4;
call_stack = erealloc(call_stack, sizeof(optimizer_call_info) * stack_size);
memset(call_stack + 4, 0, 4 * sizeof(optimizer_call_info));
}
#endif
break;
case ZEND_DO_FCALL_BY_NAME:
call--;
if (call_stack[call].func && call_stack[call].opline) {
zend_op *fcall = call_stack[call].opline;
opline->opcode = ZEND_DO_FCALL;
ZEND_OP1_TYPE(opline) = IS_CONST;
opline->op1.constant = fcall->op2.constant + 1;
op_array->literals[fcall->op2.constant + 1].cache_slot = op_array->literals[fcall->op2.constant].cache_slot;
literal_dtor(&ZEND_OP2_LITERAL(fcall));
if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
literal_dtor(&op_array->literals[fcall->op2.constant + 2].constant);
}
MAKE_NOP(fcall);
} else if (opline->extended_value == 0 &&
call_stack[call].opline &&
call_stack[call].opline->opcode == ZEND_INIT_FCALL_BY_NAME &&
ZEND_OP2_TYPE(call_stack[call].opline) == IS_CONST) {
zend_op *fcall = call_stack[call].opline;
opline->opcode = ZEND_DO_FCALL;
ZEND_OP1_TYPE(opline) = IS_CONST;
opline->op1.constant = fcall->op2.constant + 1;
op_array->literals[fcall->op2.constant + 1].cache_slot = op_array->literals[fcall->op2.constant].cache_slot;
literal_dtor(&ZEND_OP2_LITERAL(fcall));
MAKE_NOP(fcall);
}
call_stack[call].func = NULL;
call_stack[call].opline = NULL;
break;
case ZEND_FETCH_FUNC_ARG:
case ZEND_FETCH_OBJ_FUNC_ARG:
case ZEND_FETCH_DIM_FUNC_ARG:
if (call_stack[call - 1].func) {
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
opline->extended_value &= ZEND_FETCH_TYPE_MASK;
opline->opcode -= 9;
} else {
opline->extended_value &= ZEND_FETCH_TYPE_MASK;
opline->opcode -= 12;
}
}
break;
case ZEND_SEND_VAL:
if (opline->extended_value == ZEND_DO_FCALL_BY_NAME && call_stack[call - 1].func) {
if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
/* We won't convert it into_DO_FCALL to emit error at run-time */
call_stack[call - 1].opline = NULL;
} else {
opline->extended_value = ZEND_DO_FCALL;
}
}
break;
case ZEND_SEND_VAR:
if (opline->extended_value == ZEND_DO_FCALL_BY_NAME && call_stack[call - 1].func) {
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
opline->opcode = ZEND_SEND_REF;
}
opline->extended_value = ZEND_DO_FCALL;
}
break;
case ZEND_SEND_VAR_NO_REF:
if (!(opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) && call_stack[call - 1].func) {
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
opline->extended_value |= ZEND_ARG_COMPILE_TIME_BOUND | ZEND_ARG_SEND_BY_REF;
} else if (opline->extended_value) {
opline->extended_value |= ZEND_ARG_COMPILE_TIME_BOUND;
} else {
opline->opcode = ZEND_SEND_VAR;
opline->extended_value = ZEND_DO_FCALL;
}
}
break;
case ZEND_SEND_REF:
if (opline->extended_value == ZEND_DO_FCALL_BY_NAME && call_stack[call - 1].func) {
/* We won't handle run-time pass by reference */
call_stack[call - 1].opline = NULL;
}
break;
#if ZEND_EXTENSION_API_NO > PHP_5_5_X_API_NO
case ZEND_SEND_UNPACK:
call_stack[call - 1].func = NULL;
call_stack[call - 1].opline = NULL;
break;
#endif
default:
break;
}
opline++;
}
efree(call_stack);
}
#endif
|