static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script) { int already_stored = 0; zend_op *persist_ptr; zval *orig_literals = NULL; if (op_array->type != ZEND_USER_FUNCTION) { return; } if (--(*op_array->refcount) == 0) { efree(op_array->refcount); } op_array->refcount = NULL; if (main_persistent_script) { zend_execute_data *orig_execute_data = EG(current_execute_data); zend_execute_data fake_execute_data; zval *offset; memset(&fake_execute_data, 0, sizeof(fake_execute_data)); fake_execute_data.func = (zend_function*)op_array; EG(current_execute_data) = &fake_execute_data; if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) { main_persistent_script->compiler_halt_offset = Z_LVAL_P(offset); } EG(current_execute_data) = orig_execute_data; } if (op_array->static_variables) { zend_hash_persist(op_array->static_variables, zend_persist_zval); zend_accel_store(op_array->static_variables, sizeof(HashTable)); } if (zend_shared_alloc_get_xlat_entry(op_array->opcodes)) { already_stored = 1; } if (op_array->literals) { if (already_stored) { orig_literals = zend_shared_alloc_get_xlat_entry(op_array->literals); ZEND_ASSERT(orig_literals != NULL); op_array->literals = orig_literals; } else { zval *p = zend_accel_memdup(op_array->literals, sizeof(zval) * op_array->last_literal); zval *end = p + op_array->last_literal; orig_literals = op_array->literals; op_array->literals = p; while (p < end) { zend_persist_zval(p); p++; } efree(orig_literals); } } if (already_stored) { persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->opcodes); ZEND_ASSERT(persist_ptr != NULL); op_array->opcodes = persist_ptr; } else { zend_op *new_opcodes = zend_accel_memdup(op_array->opcodes, sizeof(zend_op) * op_array->last); #if ZEND_USE_ABS_CONST_ADDR || ZEND_USE_ABS_JMP_ADDR zend_op *opline = new_opcodes; zend_op *end = new_opcodes + op_array->last; int offset = 0; for (; opline < end ; opline++, offset++) { # if ZEND_USE_ABS_CONST_ADDR if (ZEND_OP1_TYPE(opline) == IS_CONST) { opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)orig_literals)); } if (ZEND_OP2_TYPE(opline) == IS_CONST) { opline->op2.zv = (zval*)((char*)opline->op2.zv + ((char*)op_array->literals - (char*)orig_literals)); } # endif # if ZEND_USE_ABS_JMP_ADDR if (ZEND_DONE_PASS_TWO(op_array)) { /* fix jumps to point to new array */ switch (opline->opcode) { case ZEND_JMP: case ZEND_GOTO: case ZEND_FAST_CALL: ZEND_OP1(opline).jmp_addr = &new_opcodes[ZEND_OP1(opline).jmp_addr - op_array->opcodes]; break; case ZEND_JMPZNZ: /* relative extended_value don't have to be changed */ /* break omitted intentionally */ case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: case ZEND_FE_RESET: case ZEND_FE_FETCH: ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes]; break; } } # endif } #endif efree(op_array->opcodes); op_array->opcodes = new_opcodes; if (op_array->run_time_cache) { efree(op_array->run_time_cache); op_array->run_time_cache = NULL; } } if (op_array->function_name && !IS_ACCEL_INTERNED(op_array->function_name)) { zend_string *new_name; if (already_stored) { new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name); ZEND_ASSERT(new_name != NULL); op_array->function_name = new_name; } else { zend_accel_store_string(op_array->function_name); } } if (op_array->filename) { /* do not free! PHP has centralized filename storage, compiler will free it */ zend_accel_memdup_string(op_array->filename); } if (op_array->arg_info) { if (already_stored) { zend_arg_info *new_ptr = zend_shared_alloc_get_xlat_entry(op_array->arg_info); ZEND_ASSERT(new_ptr != NULL); op_array->arg_info = new_ptr; } else { uint32_t i, num_args; num_args = op_array->num_args; if (op_array->fn_flags & ZEND_ACC_VARIADIC) { num_args++; } zend_accel_store(op_array->arg_info, sizeof(zend_arg_info) * num_args); for (i = 0; i < num_args; i++) { if (op_array->arg_info[i].name) { zend_accel_store_interned_string(op_array->arg_info[i].name); } if (op_array->arg_info[i].class_name) { zend_accel_store_interned_string(op_array->arg_info[i].class_name); } } } } if (op_array->brk_cont_array) { zend_accel_store(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont); } if (op_array->scope) { op_array->scope = zend_shared_alloc_get_xlat_entry(op_array->scope); } if (op_array->doc_comment) { if (ZCG(accel_directives).save_comments) { if (already_stored) { op_array->doc_comment = zend_shared_alloc_get_xlat_entry(op_array->doc_comment); ZEND_ASSERT(op_array->doc_comment != NULL); } else { zend_accel_store_string(op_array->doc_comment); } } else { if (!already_stored) { zend_string_release(op_array->doc_comment); } op_array->doc_comment = NULL; } } if (op_array->try_catch_array) { zend_accel_store(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); } if (op_array->vars) { if (already_stored) { persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars); ZEND_ASSERT(persist_ptr != NULL); op_array->vars = (zend_string**)persist_ptr; } else { int i; zend_accel_store(op_array->vars, sizeof(zend_string*) * op_array->last_var); for (i = 0; i < op_array->last_var; i++) { zend_accel_store_interned_string(op_array->vars[i]); } } } /* "prototype" may be undefined if "scope" isn't set */ if (op_array->scope && op_array->prototype) { if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype))) { op_array->prototype = (union _zend_function*)persist_ptr; /* we use refcount to show that op_array is referenced from several places */ op_array->prototype->op_array.refcount++; } } else { op_array->prototype = NULL; } }
void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx) { int T = op_array->T; int offset = op_array->last_var; char *taken_T; /* T index in use */ zend_op **start_of_T; /* opline where T is first used */ char *valid_T; /* Is the map_T valid */ int *map_T; /* Map's the T to its new index */ zend_op *opline, *end; int currT; int i; int max = -1; int var_to_free = -1; void *checkpoint = zend_arena_checkpoint(ctx->arena); taken_T = (char *) zend_arena_alloc(&ctx->arena, T); start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *)); valid_T = (char *) zend_arena_alloc(&ctx->arena, T); map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int)); end = op_array->opcodes; opline = &op_array->opcodes[op_array->last - 1]; /* Find T definition points */ while (opline >= end) { if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline; } /* special puprose variable to keep HashPointer on VM stack */ if (opline->opcode == ZEND_OP_DATA && (opline-1)->opcode == ZEND_FE_FETCH && opline->op1_type == IS_TMP_VAR) { start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline; if (sizeof(HashPointer) > sizeof(zval)) { /* Make shure 1 zval is enough for HashPointer (2 must be enough) */ start_of_T[VAR_NUM(ZEND_OP1(opline).var) + 1 - offset] = opline; } } opline--; } memset(valid_T, 0, T); memset(taken_T, 0, T); end = op_array->opcodes; opline = &op_array->opcodes[op_array->last - 1]; while (opline >= end) { if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) { /* special puprose variable to keep HashPointer on VM stack */ if (opline->opcode == ZEND_OP_DATA && (opline-1)->opcode == ZEND_FE_FETCH && opline->op1_type == IS_TMP_VAR) { max++; ZEND_OP1(opline).var = NUM_VAR(max + offset); if (sizeof(HashPointer) > sizeof(zval)) { /* Make shure 1 zval is enough for HashPointer (2 must be enough) */ max++; } } else { currT = VAR_NUM(ZEND_OP1(opline).var) - offset; if (!valid_T[currT]) { GET_AVAILABLE_T(); map_T[currT] = i; valid_T[currT] = 1; } ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); } } /* Skip OP_DATA */ if (opline->opcode == ZEND_OP_DATA && (opline-1)->opcode == ZEND_ASSIGN_DIM) { opline--; continue; } if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) { currT = VAR_NUM(ZEND_OP2(opline).var) - offset; if (!valid_T[currT]) { GET_AVAILABLE_T(); map_T[currT] = i; valid_T[currT] = 1; } ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset); } if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS || opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) { currT = VAR_NUM(opline->extended_value) - offset; if (!valid_T[currT]) { GET_AVAILABLE_T(); map_T[currT] = i; valid_T[currT] = 1; } opline->extended_value = NUM_VAR(map_T[currT] + offset); } /* Allocate OP_DATA->op2 after "operands", but before "result" */ if (opline->opcode == ZEND_ASSIGN_DIM && (opline + 1)->opcode == ZEND_OP_DATA && ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) { currT = VAR_NUM(ZEND_OP2(opline + 1).var) - offset; GET_AVAILABLE_T(); map_T[currT] = i; valid_T[currT] = 1; taken_T[i] = 0; ZEND_OP2(opline + 1).var = NUM_VAR(i + offset); var_to_free = i; } if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { currT = VAR_NUM(ZEND_RESULT(opline).var) - offset; if (valid_T[currT]) { if (start_of_T[currT] == opline) { taken_T[map_T[currT]] = 0; } ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset); } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */ GET_AVAILABLE_T(); if (RESULT_UNUSED(opline)) { taken_T[i] = 0; } else { /* Code which gets here is using a wrongly built opcode such as RECV() */ map_T[currT] = i; valid_T[currT] = 1; } ZEND_RESULT(opline).var = NUM_VAR(i + offset); } } if (var_to_free >= 0) { taken_T[var_to_free] = 0; var_to_free = -1; } opline--; } zend_arena_release(&ctx->arena, checkpoint); op_array->T = max + 1; }
static void zend_accel_optimize(zend_op_array *op_array, zend_optimizer_ctx *ctx) { zend_op *opline, *end; /* Revert pass_two() */ opline = op_array->opcodes; end = opline + op_array->last; while (opline < end) { if (opline->op1_type == IS_CONST) { ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op1); } if (opline->op2_type == IS_CONST) { ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op2); } switch (opline->opcode) { case ZEND_JMP: case ZEND_FAST_CALL: case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP1(opline)); break; case ZEND_JMPZNZ: /* relative offset into absolute index */ opline->extended_value = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value); /* break omitted intentionally */ case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_ASSERT_CHECK: ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP2(opline)); break; case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: opline->extended_value = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value); break; } opline++; } /* Do actual optimizations */ zend_optimize(op_array, ctx); /* Redo pass_two() */ opline = op_array->opcodes; end = opline + op_array->last; while (opline < end) { if (opline->op1_type == IS_CONST) { ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1); } if (opline->op2_type == IS_CONST) { ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2); } switch (opline->opcode) { case ZEND_JMP: case ZEND_FAST_CALL: case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP1(opline)); break; case ZEND_JMPZNZ: /* absolute index to relative offset */ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value); /* break omitted intentionally */ case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_ASSERT_CHECK: ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP2(opline)); break; case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value); break; } ZEND_VM_SET_OPCODE_HANDLER(opline); opline++; } }
void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx) { int T = op_array->T; int offset = op_array->last_var; char *taken_T; /* T index in use */ zend_op **start_of_T; /* opline where T is first used */ char *valid_T; /* Is the map_T valid */ int *map_T; /* Map's the T to its new index */ zend_op *opline, *end; int currT; int i; int max = -1; int var_to_free = -1; void *checkpoint = zend_arena_checkpoint(ctx->arena); taken_T = (char *) zend_arena_alloc(&ctx->arena, T); start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *)); valid_T = (char *) zend_arena_alloc(&ctx->arena, T); map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int)); end = op_array->opcodes; opline = &op_array->opcodes[op_array->last - 1]; /* Find T definition points */ while (opline >= end) { if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline; } opline--; } memset(valid_T, 0, T); memset(taken_T, 0, T); end = op_array->opcodes; opline = &op_array->opcodes[op_array->last - 1]; while (opline >= end) { if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) { currT = VAR_NUM(ZEND_OP1(opline).var) - offset; if (opline->opcode == ZEND_ROPE_END) { int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); int var; var = max; while (var >= 0 && !taken_T[var]) { var--; } max = MAX(max, var + num); var = var + 1; map_T[currT] = var; valid_T[currT] = 1; taken_T[var] = 1; ZEND_OP1(opline).var = NUM_VAR(var + offset); while (num > 1) { num--; taken_T[var + num] = 1; } } else { if (!valid_T[currT]) { GET_AVAILABLE_T(); map_T[currT] = i; valid_T[currT] = 1; } ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); } } /* Skip OP_DATA */ if (opline->opcode == ZEND_OP_DATA && (opline-1)->opcode == ZEND_ASSIGN_DIM) { opline--; continue; } if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) { currT = VAR_NUM(ZEND_OP2(opline).var) - offset; if (!valid_T[currT]) { GET_AVAILABLE_T(); map_T[currT] = i; valid_T[currT] = 1; } ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset); } if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS || opline->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS || opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) { currT = VAR_NUM(opline->extended_value) - offset; if (!valid_T[currT]) { GET_AVAILABLE_T(); map_T[currT] = i; valid_T[currT] = 1; } opline->extended_value = NUM_VAR(map_T[currT] + offset); } /* Allocate OP_DATA->op2 after "operands", but before "result" */ if (opline->opcode == ZEND_ASSIGN_DIM && (opline + 1)->opcode == ZEND_OP_DATA && ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) { currT = VAR_NUM(ZEND_OP2(opline + 1).var) - offset; GET_AVAILABLE_T(); map_T[currT] = i; valid_T[currT] = 1; taken_T[i] = 0; ZEND_OP2(opline + 1).var = NUM_VAR(i + offset); var_to_free = i; } if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { currT = VAR_NUM(ZEND_RESULT(opline).var) - offset; if (valid_T[currT]) { if (start_of_T[currT] == opline) { taken_T[map_T[currT]] = 0; } ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset); if (opline->opcode == ZEND_ROPE_INIT) { if (start_of_T[currT] == opline) { uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); while (num > 1) { num--; taken_T[map_T[currT]+num] = 0; } } } } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */ GET_AVAILABLE_T(); if (RESULT_UNUSED(opline)) { taken_T[i] = 0; } else { /* Code which gets here is using a wrongly built opcode such as RECV() */ map_T[currT] = i; valid_T[currT] = 1; } ZEND_RESULT(opline).var = NUM_VAR(i + offset); } } if (var_to_free >= 0) { taken_T[var_to_free] = 0; var_to_free = -1; } opline--; } zend_arena_release(&ctx->arena, checkpoint); op_array->T = max + 1; }
void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx) { int T = op_array->T; int offset = op_array->last_var; uint32_t bitset_len; zend_bitset taken_T; /* T index in use */ zend_op **start_of_T; /* opline where T is first used */ zend_bitset valid_T; /* Is the map_T valid */ int *map_T; /* Map's the T to its new index */ zend_op *opline, *end; int currT; int i; int max = -1; int var_to_free = -1; void *checkpoint = zend_arena_checkpoint(ctx->arena); bitset_len = zend_bitset_len(T); taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE); start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *)); valid_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE); map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int)); end = op_array->opcodes; opline = &op_array->opcodes[op_array->last - 1]; /* Find T definition points */ while (opline >= end) { if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline; } opline--; } zend_bitset_clear(valid_T, bitset_len); zend_bitset_clear(taken_T, bitset_len); end = op_array->opcodes; opline = &op_array->opcodes[op_array->last - 1]; while (opline >= end) { if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) { currT = VAR_NUM(ZEND_OP1(opline).var) - offset; if (opline->opcode == ZEND_ROPE_END) { int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); int var; var = max; while (var >= 0 && !zend_bitset_in(taken_T, var)) { var--; } max = MAX(max, var + num); var = var + 1; map_T[currT] = var; zend_bitset_incl(valid_T, currT); zend_bitset_incl(taken_T, var); ZEND_OP1(opline).var = NUM_VAR(var + offset); while (num > 1) { num--; zend_bitset_incl(taken_T, var + num); } } else { if (!zend_bitset_in(valid_T, currT)) { int use_new_var = 0; /* Code in "finally" blocks may modify temorary variables. * We allocate new temporaries for values that need to * relive FAST_CALLs. */ if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) && (opline->opcode == ZEND_RETURN || opline->opcode == ZEND_GENERATOR_RETURN || opline->opcode == ZEND_RETURN_BY_REF || opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE)) { zend_op *curr = opline; while (--curr >= end) { if (curr->opcode == ZEND_FAST_CALL) { use_new_var = 1; break; } else if (curr->opcode != ZEND_FREE && curr->opcode != ZEND_FE_FREE && curr->opcode != ZEND_VERIFY_RETURN_TYPE && curr->opcode != ZEND_DISCARD_EXCEPTION) { break; } } } if (use_new_var) { i = ++max; zend_bitset_incl(taken_T, i); } else { GET_AVAILABLE_T(); } map_T[currT] = i; zend_bitset_incl(valid_T, currT); } ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); } } if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) { currT = VAR_NUM(ZEND_OP2(opline).var) - offset; if (!zend_bitset_in(valid_T, currT)) { GET_AVAILABLE_T(); map_T[currT] = i; zend_bitset_incl(valid_T, currT); } ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset); } if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { currT = VAR_NUM(ZEND_RESULT(opline).var) - offset; if (zend_bitset_in(valid_T, currT)) { if (start_of_T[currT] == opline) { /* ZEND_FAST_CALL can not share temporary var with others * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION * which could be ahead of it */ if (opline->opcode != ZEND_FAST_CALL) { zend_bitset_excl(taken_T, map_T[currT]); } } ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset); if (opline->opcode == ZEND_ROPE_INIT) { if (start_of_T[currT] == opline) { uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); while (num > 1) { num--; zend_bitset_excl(taken_T, map_T[currT]+num); } } } } else { /* Code which gets here is using a wrongly built opcode such as RECV() */ GET_AVAILABLE_T(); map_T[currT] = i; zend_bitset_incl(valid_T, currT); ZEND_RESULT(opline).var = NUM_VAR(i + offset); } } if (var_to_free >= 0) { zend_bitset_excl(taken_T, var_to_free); var_to_free = -1; } opline--; } if (op_array->live_range) { for (i = 0; i < op_array->last_live_range; i++) { op_array->live_range[i].var = NUM_VAR(map_T[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK) - offset] + offset) | (op_array->live_range[i].var & ZEND_LIVE_MASK); } } zend_arena_release(&ctx->arena, checkpoint); op_array->T = max + 1; }
int zend_optimizer_replace_by_const(zend_op_array *op_array, zend_op *opline, zend_uchar type, uint32_t var, zval *val) { zend_op *end = op_array->opcodes + op_array->last; while (opline < end) { if (ZEND_OP1_TYPE(opline) == type && ZEND_OP1(opline).var == var) { switch (opline->opcode) { case ZEND_FETCH_DIM_W: case ZEND_FETCH_DIM_RW: case ZEND_FETCH_DIM_FUNC_ARG: case ZEND_FETCH_DIM_UNSET: case ZEND_ASSIGN_DIM: case ZEND_SEPARATE: return 0; case ZEND_SEND_VAR: opline->extended_value = 0; opline->opcode = ZEND_SEND_VAL; break; case ZEND_SEND_VAR_EX: opline->extended_value = 0; opline->opcode = ZEND_SEND_VAL_EX; break; case ZEND_SEND_VAR_NO_REF: if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) { if (opline->extended_value & ZEND_ARG_SEND_BY_REF) { zval_dtor(val); return 0; } opline->extended_value = 0; opline->opcode = ZEND_SEND_VAL_EX; } else { opline->extended_value = 0; opline->opcode = ZEND_SEND_VAL; } break; /* In most cases IS_TMP_VAR operand may be used only once. * The operands are usually destroyed by the opcode handler. * ZEND_CASE is an exception, that keeps operand unchanged, * and allows its reuse. The number of ZEND_CASE instructions * usually terminated by ZEND_FREE that finally kills the value. */ case ZEND_FREE: case ZEND_CASE: { zend_op *m, *n; int brk = op_array->last_brk_cont; zend_bool in_switch = 0; while (brk--) { if (op_array->brk_cont_array[brk].start <= (opline - op_array->opcodes) && op_array->brk_cont_array[brk].brk > (opline - op_array->opcodes)) { in_switch = 1; break; } } if (!in_switch) { ZEND_ASSERT(opline->opcode == ZEND_FREE); MAKE_NOP(opline); zval_dtor(val); return 1; } m = opline; n = op_array->opcodes + op_array->brk_cont_array[brk].brk + 1; while (m < n) { if (ZEND_OP1_TYPE(m) == type && ZEND_OP1(m).var == var) { if (m->opcode == ZEND_CASE) { zval old_val; ZVAL_COPY_VALUE(&old_val, val); zval_copy_ctor(val); zend_optimizer_update_op1_const(op_array, m, val); ZVAL_COPY_VALUE(val, &old_val); } else if (m->opcode == ZEND_FREE) { MAKE_NOP(m); } else { ZEND_ASSERT(0); } } m++; } zval_dtor(val); return 1; } case ZEND_VERIFY_RETURN_TYPE: { zend_arg_info *ret_info = op_array->arg_info - 1; ZEND_ASSERT((opline + 1)->opcode == ZEND_RETURN || (opline + 1)->opcode == ZEND_RETURN_BY_REF); if (ret_info->class_name || ret_info->type_hint == IS_CALLABLE || !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(val)) || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) { zval_dtor(val); return 0; } MAKE_NOP(opline); zend_optimizer_update_op1_const(op_array, opline + 1, val); return 1; } default: break; } zend_optimizer_update_op1_const(op_array, opline, val); break; } if (ZEND_OP2_TYPE(opline) == type && ZEND_OP2(opline).var == var) { switch (opline->opcode) { case ZEND_ASSIGN_REF: zval_dtor(val); return 0; default: break; } zend_optimizer_update_op2_const(op_array, opline, val); break; } opline++; } return 1; }
static void nop_removal(zend_op_array *op_array) { zend_op *end, *opline; zend_uint new_count, i, shift; int j; zend_uint *shiftlist; ALLOCA_FLAG(use_heap); shiftlist = (zend_uint *)DO_ALLOCA(sizeof(zend_uint) * op_array->last); i = new_count = shift = 0; end = op_array->opcodes+op_array->last; for (opline = op_array->opcodes; opline < end; opline++) { #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO /* GOTO target is unresolved yet. We can't optimize. */ if (opline->opcode == ZEND_GOTO && Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { /* TODO: in general we can avoid this restriction */ FREE_ALLOCA(shiftlist); return; } #endif /* Kill JMP-over-NOP-s */ if (opline->opcode == ZEND_JMP && ZEND_OP1(opline).opline_num > i) { /* check if there are only NOPs under the branch */ zend_op *target = op_array->opcodes + ZEND_OP1(opline).opline_num - 1; while (target->opcode == ZEND_NOP) { target--; } if (target == opline) { /* only NOPs */ opline->opcode = ZEND_NOP; } } shiftlist[i++] = shift; if (opline->opcode == ZEND_NOP) { shift++; } else { if (shift) { op_array->opcodes[new_count] = *opline; } new_count++; } } if (shift) { op_array->last = new_count; end = op_array->opcodes + op_array->last; /* update JMPs */ for (opline = op_array->opcodes; opline<end; opline++) { switch (opline->opcode) { case ZEND_JMP: #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO case ZEND_GOTO: #endif #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO case ZEND_FAST_CALL: #endif ZEND_OP1(opline).opline_num -= shiftlist[ZEND_OP1(opline).opline_num]; break; case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_FE_FETCH: case ZEND_FE_RESET: case ZEND_NEW: #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO case ZEND_JMP_SET: #endif #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO case ZEND_JMP_SET_VAR: #endif ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num]; break; case ZEND_JMPZNZ: ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num]; opline->extended_value -= shiftlist[opline->extended_value]; break; case ZEND_CATCH: opline->extended_value -= shiftlist[opline->extended_value]; break; } } /* update brk/cont array */ for (i=0; i<op_array->last_brk_cont; i++) { op_array->brk_cont_array[i].brk -= shiftlist[op_array->brk_cont_array[i].brk]; op_array->brk_cont_array[i].cont -= shiftlist[op_array->brk_cont_array[i].cont]; op_array->brk_cont_array[i].start -= shiftlist[op_array->brk_cont_array[i].start]; } /* update try/catch array */ for (j=0; j<op_array->last_try_catch; j++) { op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op]; #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO if (op_array->try_catch_array[j].finally_op) { op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op]; op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end]; } #endif } #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO /* update early binding list */ if (op_array->early_binding != -1) { zend_uint *opline_num = &op_array->early_binding; do { *opline_num -= shiftlist[*opline_num]; opline_num = &ZEND_RESULT(&op_array->opcodes[*opline_num]).opline_num; } while (*opline_num != -1); } #endif } FREE_ALLOCA(shiftlist); }
void zend_optimizer_nop_removal(zend_op_array *op_array) { zend_op *end, *opline; uint32_t new_count, i, shift; int j; uint32_t *shiftlist; ALLOCA_FLAG(use_heap); shiftlist = (uint32_t *)DO_ALLOCA(sizeof(uint32_t) * op_array->last); i = new_count = shift = 0; end = op_array->opcodes + op_array->last; for (opline = op_array->opcodes; opline < end; opline++) { /* Kill JMP-over-NOP-s */ if (opline->opcode == ZEND_JMP && ZEND_OP1(opline).opline_num > i) { /* check if there are only NOPs under the branch */ zend_op *target = op_array->opcodes + ZEND_OP1(opline).opline_num - 1; while (target->opcode == ZEND_NOP) { target--; } if (target == opline) { /* only NOPs */ opline->opcode = ZEND_NOP; } } shiftlist[i++] = shift; if (opline->opcode == ZEND_NOP) { shift++; } else { if (shift) { op_array->opcodes[new_count] = *opline; } new_count++; } } if (shift) { op_array->last = new_count; end = op_array->opcodes + op_array->last; /* update JMPs */ for (opline = op_array->opcodes; opline<end; opline++) { switch (opline->opcode) { case ZEND_JMP: case ZEND_FAST_CALL: case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: ZEND_OP1(opline).opline_num -= shiftlist[ZEND_OP1(opline).opline_num]; break; case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_ASSERT_CHECK: ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num]; break; case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: opline->extended_value -= shiftlist[opline->extended_value]; break; case ZEND_JMPZNZ: ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num]; opline->extended_value -= shiftlist[opline->extended_value]; break; case ZEND_CATCH: opline->extended_value -= shiftlist[opline->extended_value]; break; } } /* update try/catch array */ for (j = 0; j < op_array->last_try_catch; j++) { op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op]; if (op_array->try_catch_array[j].finally_op) { op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op]; op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end]; } } /* update early binding list */ if (op_array->early_binding != (uint32_t)-1) { uint32_t *opline_num = &op_array->early_binding; do { *opline_num -= shiftlist[*opline_num]; opline_num = &ZEND_RESULT(&op_array->opcodes[*opline_num]).opline_num; } while (*opline_num != (uint32_t)-1); } } FREE_ALLOCA(shiftlist); }
void zend_optimizer_pass3(zend_op_array *op_array) { zend_op *opline; zend_op *end = op_array->opcodes + op_array->last; zend_op **jmp_hitlist; int jmp_hitlist_count; int i; uint32_t opline_num = 0; ALLOCA_FLAG(use_heap); jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap); opline = op_array->opcodes; while (opline < end) { jmp_hitlist_count = 0; switch (opline->opcode) { case ZEND_ADD: case ZEND_SUB: case ZEND_MUL: case ZEND_DIV: case ZEND_MOD: case ZEND_POW: case ZEND_CONCAT: case ZEND_SL: case ZEND_SR: case ZEND_BW_OR: case ZEND_BW_AND: case ZEND_BW_XOR: { zend_op *next_opline = opline + 1; while (next_opline < end && next_opline->opcode == ZEND_NOP) { ++next_opline; } if (next_opline >= end || next_opline->opcode != ZEND_ASSIGN) { break; } if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_CV)) && ZEND_OP2(opline).var == ZEND_OP1(next_opline).var && (opline->opcode == ZEND_ADD || opline->opcode == ZEND_MUL || opline->opcode == ZEND_BW_OR || opline->opcode == ZEND_BW_AND || opline->opcode == ZEND_BW_XOR)) { /* change $i=expr+$i to $i=$i+expr so that the next * optimization works on it */ zend_uchar tmp_type = opline->op1_type; znode_op tmp = opline->op1; if (opline->opcode != ZEND_ADD || (ZEND_OP1_TYPE(opline) == IS_CONST && Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_ARRAY)) { /* protection from array add: $a = array + $a is not commutative! */ COPY_NODE(opline->op1, opline->op2); COPY_NODE(opline->op2, tmp); } } if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_CV)) && ZEND_OP1(opline).var == ZEND_OP1(next_opline).var && ZEND_OP1_TYPE(opline) == ZEND_OP1_TYPE(next_opline)) { switch (opline->opcode) { case ZEND_ADD: opline->opcode = ZEND_ASSIGN_ADD; break; case ZEND_SUB: opline->opcode = ZEND_ASSIGN_SUB; break; case ZEND_MUL: opline->opcode = ZEND_ASSIGN_MUL; break; case ZEND_DIV: opline->opcode = ZEND_ASSIGN_DIV; break; case ZEND_MOD: opline->opcode = ZEND_ASSIGN_MOD; break; case ZEND_POW: opline->opcode = ZEND_ASSIGN_POW; break; case ZEND_CONCAT: opline->opcode = ZEND_ASSIGN_CONCAT; break; case ZEND_SL: opline->opcode = ZEND_ASSIGN_SL; break; case ZEND_SR: opline->opcode = ZEND_ASSIGN_SR; break; case ZEND_BW_OR: opline->opcode = ZEND_ASSIGN_BW_OR; break; case ZEND_BW_AND: opline->opcode = ZEND_ASSIGN_BW_AND; break; case ZEND_BW_XOR: opline->opcode = ZEND_ASSIGN_BW_XOR; break; } COPY_NODE(opline->result, next_opline->result); MAKE_NOP(next_opline); opline++; opline_num++; } } break; case ZEND_JMP: if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { break; } /* convert L: JMP L+1 to NOP */ if (ZEND_OP1_JMP_ADDR(opline) == opline + 1) { MAKE_NOP(opline); goto done_jmp_optimization; } /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */ while (ZEND_OP1_JMP_ADDR(opline) < end && ZEND_OP1_JMP_ADDR(opline)->opcode == ZEND_JMP) { zend_op *target = ZEND_OP1_JMP_ADDR(opline); CHECK_JMP(target, done_jmp_optimization); ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(target)); } break; case ZEND_JMP_SET: case ZEND_COALESCE: if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { break; } while (ZEND_OP2_JMP_ADDR(opline) < end) { zend_op *target = ZEND_OP2_JMP_ADDR(opline); if (target->opcode == ZEND_JMP) { ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target)); } else { break; } } break; case ZEND_JMPZ: case ZEND_JMPNZ: if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { break; } while (ZEND_OP2_JMP_ADDR(opline) < end) { zend_op *target = ZEND_OP2_JMP_ADDR(opline); if (target->opcode == ZEND_JMP) { /* plain JMP */ /* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */ CHECK_JMP(target, done_jmp_optimization); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target)); } else if (target->opcode == opline->opcode && SAME_VAR(opline->op1, target->op1)) { /* same opcode and same var as this opcode */ /* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */ CHECK_JMP2(target, done_jmp_optimization); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); } else if (target->opcode == opline->opcode + 3 && SAME_VAR(opline->op1, target->op1)) { /* convert JMPZ(X,L1), L1: T JMPZ_EX(X,L2) to T = JMPZ_EX(X, L2) */ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); opline->opcode += 3; COPY_NODE(opline->result, target->result); break; } else if (target->opcode == INV_COND(opline->opcode) && SAME_VAR(opline->op1, target->op1)) { /* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to JMPZ(X,L1+1) */ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); break; } else if (target->opcode == INV_COND_EX(opline->opcode) && SAME_VAR(opline->op1, target->op1)) { /* convert JMPZ(X,L1), L1: T = JMPNZ_EX(X,L2) to T = JMPZ_EX(X,L1+1) */ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); opline->opcode += 3; COPY_NODE(opline->result, target->result); break; } else { break; } } break; case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: { zend_uchar T_type = opline->result_type; znode_op T = opline->result; if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { break; } /* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */ /* convert L: T = JMPZ_EX T,L+1 to NOP */ if (ZEND_OP2_JMP_ADDR(opline) == opline + 1) { if (ZEND_OP1(opline).var == ZEND_RESULT(opline).var) { MAKE_NOP(opline); } else { opline->opcode = ZEND_BOOL; SET_UNUSED(opline->op2); } goto done_jmp_optimization; } while (ZEND_OP2_JMP_ADDR(opline) < end) { zend_op *target = ZEND_OP2_JMP_ADDR(opline); if (SAME_OPCODE_EX(opline->opcode, target->opcode) && SAME_VAR(target->op1, T)) { /* Check for JMPZ_EX to JMPZ[_EX] with the same condition, either with _EX or not */ if (target->opcode == opline->opcode) { /* change T only if we have _EX opcode there */ COPY_NODE(T, target->result); } CHECK_JMP2(target, continue_jmp_ex_optimization); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); } else if (target->opcode == ZEND_JMPZNZ && SAME_VAR(target->op1, T)) { /* Check for JMPZNZ with same cond variable */ zend_op *new_target; CHECK_JMP2(target, continue_jmp_ex_optimization); if (opline->opcode == ZEND_JMPZ_EX) { new_target = ZEND_OP2_JMP_ADDR(target); } else { /* JMPNZ_EX */ new_target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value); } ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_target); } else if ((target->opcode == INV_EX_COND_EX(opline->opcode) || target->opcode == INV_EX_COND(opline->opcode)) && SAME_VAR(opline->op1, target->op1)) { /* convert JMPZ_EX(X,L1), L1: JMPNZ_EX(X,L2) to JMPZ_EX(X,L1+1) */ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); break; } else { break; } } /* while */ continue_jmp_ex_optimization: break; #if 0 /* If Ti = JMPZ_EX(X, L) and Ti is not used, convert to JMPZ(X, L) */ { zend_op *op; for(op = opline+1; op<end; op++) { if(ZEND_RESULT_TYPE(op) == IS_TMP_VAR && ZEND_RESULT(op).var == ZEND_RESULT(opline).var) { break; /* can pass to part 2 */ } if(op->opcode == ZEND_JMP || op->opcode == ZEND_JMPZ || op->opcode == ZEND_JMPZ_EX || op->opcode == ZEND_JMPNZ || op->opcode == ZEND_JMPNZ_EX || op->opcode == ZEND_JMPZNZ || op->opcode == ZEND_CASE || op->opcode == ZEND_RETURN || op->opcode == ZEND_RETURN_BY_REF || op->opcode == ZEND_FAST_RET || op->opcode == ZEND_FE_FETCH_R || op->opcode == ZEND_FE_FETCH_RW || op->opcode == ZEND_EXIT) { break; } if(ZEND_OP1_TYPE(op) == IS_TMP_VAR && ZEND_OP1(op).var == ZEND_RESULT(opline).var) { goto done_jmp_optimization; } if(ZEND_OP2_TYPE(op) == IS_TMP_VAR && ZEND_OP2(op).var == ZEND_RESULT(opline).var) { goto done_jmp_optimization; } } /* for */ for(op = &op_array->opcodes[ZEND_OP2(opline).opline_num]; op<end; op++) { if(ZEND_RESULT_TYPE(op) == IS_TMP_VAR && ZEND_RESULT(op).var == ZEND_RESULT(opline).var) { break; /* can pass to optimization */ } if(op->opcode == ZEND_JMP || op->opcode == ZEND_JMPZ || op->opcode == ZEND_JMPZ_EX || op->opcode == ZEND_JMPNZ || op->opcode == ZEND_JMPNZ_EX || op->opcode == ZEND_JMPZNZ || op->opcode == ZEND_CASE || op->opcode == ZEND_RETURN || op->opcode == ZEND_RETURN_BY_REF || op->opcode == ZEND_FAST_RET || op->opcode == ZEND_FE_FETCH_R || op->opcode == ZEND_FE_FETCH_RW || op->opcode == ZEND_EXIT) { break; } if(ZEND_OP1_TYPE(op) == IS_TMP_VAR && ZEND_OP1(op).var == ZEND_RESULT(opline).var) { goto done_jmp_optimization; } if(ZEND_OP2_TYPE(op) == IS_TMP_VAR && ZEND_OP2(op).var == ZEND_RESULT(opline).var) { goto done_jmp_optimization; } } opline->opcode = opline->opcode-3; /* JMP_EX -> JMP */ SET_UNUSED(opline->result); break; } #endif } break; case ZEND_JMPZNZ: if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { break; } /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */ while (ZEND_OP2_JMP_ADDR(opline) < end && ZEND_OP2_JMP_ADDR(opline)->opcode == ZEND_JMP) { zend_op *target = ZEND_OP2_JMP_ADDR(opline); CHECK_JMP(target, continue_jmpznz_optimization); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target)); } continue_jmpznz_optimization: /* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */ while (ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) < end && ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value)->opcode == ZEND_JMP) { zend_op *target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); CHECK_JMP(target, done_jmp_optimization); opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(target)); } break; case ZEND_POST_INC: case ZEND_POST_DEC: { /* POST_INC, FREE => PRE_INC */ zend_op *next_op = opline + 1; if (next_op >= end) { break; } if (next_op->opcode == ZEND_FREE && ZEND_OP1(next_op).var == ZEND_RESULT(opline).var) { MAKE_NOP(next_op); opline->opcode -= 2; ZEND_RESULT_TYPE(opline) = IS_VAR | EXT_TYPE_UNUSED; } } break; } done_jmp_optimization: opline++; opline_num++; } free_alloca(jmp_hitlist, use_heap); }
void zend_optimizer_pass2(zend_op_array *op_array) { zend_op *opline; zend_op *end = op_array->opcodes + op_array->last; opline = op_array->opcodes; while (opline < end) { switch (opline->opcode) { case ZEND_ADD: case ZEND_SUB: case ZEND_MUL: case ZEND_DIV: if (ZEND_OP1_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { convert_scalar_to_number(&ZEND_OP1_LITERAL(opline)); } } /* break missing *intentionally* - the assign_op's may only optimize op2 */ case ZEND_ASSIGN_ADD: case ZEND_ASSIGN_SUB: case ZEND_ASSIGN_MUL: case ZEND_ASSIGN_DIV: if (opline->extended_value != 0) { /* object tristate op - don't attempt to optimize it! */ break; } if (ZEND_OP2_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { convert_scalar_to_number(&ZEND_OP2_LITERAL(opline)); } } break; case ZEND_MOD: case ZEND_SL: case ZEND_SR: if (ZEND_OP1_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) { convert_to_long(&ZEND_OP1_LITERAL(opline)); } } /* break missing *intentionally - the assign_op's may only optimize op2 */ case ZEND_ASSIGN_MOD: case ZEND_ASSIGN_SL: case ZEND_ASSIGN_SR: if (opline->extended_value != 0) { /* object tristate op - don't attempt to optimize it! */ break; } if (ZEND_OP2_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { convert_to_long(&ZEND_OP2_LITERAL(opline)); } } break; case ZEND_CONCAT: case ZEND_FAST_CONCAT: if (ZEND_OP1_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) { convert_to_string(&ZEND_OP1_LITERAL(opline)); } } /* break missing *intentionally - the assign_op's may only optimize op2 */ case ZEND_ASSIGN_CONCAT: if (opline->extended_value != 0) { /* object tristate op - don't attempt to optimize it! */ break; } if (ZEND_OP2_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { convert_to_string(&ZEND_OP2_LITERAL(opline)); } } break; case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: /* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */ if (0 && /* FIXME: temporary disable unsafe pattern */ ZEND_OP1_TYPE(opline) == IS_TMP_VAR && ZEND_RESULT_TYPE(opline) == IS_TMP_VAR && ZEND_OP1(opline).var == ZEND_RESULT(opline).var) { opline->opcode -= 3; /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C) in case we know it wouldn't jump */ } else if (ZEND_OP1_TYPE(opline) == IS_CONST) { int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); if (opline->opcode == ZEND_JMPZ_EX) { should_jmp = !should_jmp; } if (!should_jmp) { opline->opcode = ZEND_QM_ASSIGN; SET_UNUSED(opline->op2); } } break; case ZEND_JMPZ: case ZEND_JMPNZ: if (ZEND_OP1_TYPE(opline) == IS_CONST) { int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); if (opline->opcode == ZEND_JMPZ) { should_jmp = !should_jmp; } literal_dtor(&ZEND_OP1_LITERAL(opline)); ZEND_OP1_TYPE(opline) = IS_UNUSED; if (should_jmp) { opline->opcode = ZEND_JMP; COPY_NODE(opline->op1, opline->op2); } else { MAKE_NOP(opline); } break; } if ((opline + 1)->opcode == ZEND_JMP) { /* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */ /* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */ if (ZEND_OP2(opline).opline_num == ZEND_OP1(opline + 1).opline_num) { /* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */ MAKE_NOP(opline); } else { if (opline->opcode == ZEND_JMPZ) { opline->extended_value = ZEND_OP1(opline + 1).opline_num; } else { opline->extended_value = ZEND_OP2(opline).opline_num; COPY_NODE(opline->op2, (opline + 1)->op1); } opline->opcode = ZEND_JMPZNZ; } } break; case ZEND_JMPZNZ: if (ZEND_OP1_TYPE(opline) == IS_CONST) { int opline_num; if (zend_is_true(&ZEND_OP1_LITERAL(opline))) { opline_num = opline->extended_value; /* JMPNZ */ } else { opline_num = ZEND_OP2(opline).opline_num; /* JMPZ */ } literal_dtor(&ZEND_OP1_LITERAL(opline)); ZEND_OP1(opline).opline_num = opline_num; ZEND_OP1_TYPE(opline) = IS_UNUSED; opline->opcode = ZEND_JMP; } break; case ZEND_BRK: case ZEND_CONT: { zend_brk_cont_element *jmp_to; int array_offset; int nest_levels; int dont_optimize = 0; ZEND_ASSERT(ZEND_OP2_TYPE(opline) == IS_CONST); ZEND_ASSERT(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_LONG); nest_levels = Z_LVAL(ZEND_OP2_LITERAL(opline)); array_offset = ZEND_OP1(opline).opline_num; while (1) { if (array_offset == -1) { dont_optimize = 1; /* don't optimize this bogus break/continue, let the executor shout */ break; } jmp_to = &op_array->brk_cont_array[array_offset]; array_offset = jmp_to->parent; if (--nest_levels > 0) { if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE || op_array->opcodes[jmp_to->brk].opcode == ZEND_FE_FREE || op_array->opcodes[jmp_to->brk].opcode == ZEND_END_SILENCE) { dont_optimize = 1; break; } } else { break; } } if (dont_optimize) { break; } /* optimize - convert to a JMP */ switch (opline->opcode) { case ZEND_BRK: MAKE_NOP(opline); ZEND_OP1(opline).opline_num = jmp_to->brk; break; case ZEND_CONT: MAKE_NOP(opline); ZEND_OP1(opline).opline_num = jmp_to->cont; break; } opline->opcode = ZEND_JMP; /* MAKE_NOP() already set op1 and op2 to IS_UNUSED */ } break; } opline++; } }
void zend_optimizer_pass2(zend_op_array *op_array) { zend_op *opline; zend_op *end = op_array->opcodes + op_array->last; opline = op_array->opcodes; while (opline < end) { switch (opline->opcode) { case ZEND_ADD: case ZEND_SUB: case ZEND_MUL: case ZEND_DIV: case ZEND_POW: if (ZEND_OP1_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { convert_scalar_to_number(&ZEND_OP1_LITERAL(opline)); } } /* break missing *intentionally* - the assign_op's may only optimize op2 */ case ZEND_ASSIGN_ADD: case ZEND_ASSIGN_SUB: case ZEND_ASSIGN_MUL: case ZEND_ASSIGN_DIV: case ZEND_ASSIGN_POW: if (opline->extended_value != 0) { /* object tristate op - don't attempt to optimize it! */ break; } if (ZEND_OP2_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { convert_scalar_to_number(&ZEND_OP2_LITERAL(opline)); } } break; case ZEND_MOD: case ZEND_SL: case ZEND_SR: if (ZEND_OP1_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) { convert_to_long(&ZEND_OP1_LITERAL(opline)); } } /* break missing *intentionally - the assign_op's may only optimize op2 */ case ZEND_ASSIGN_MOD: case ZEND_ASSIGN_SL: case ZEND_ASSIGN_SR: if (opline->extended_value != 0) { /* object tristate op - don't attempt to optimize it! */ break; } if (ZEND_OP2_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { convert_to_long(&ZEND_OP2_LITERAL(opline)); } } break; case ZEND_CONCAT: case ZEND_FAST_CONCAT: if (ZEND_OP1_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) { convert_to_string(&ZEND_OP1_LITERAL(opline)); } } /* break missing *intentionally - the assign_op's may only optimize op2 */ case ZEND_ASSIGN_CONCAT: if (opline->extended_value != 0) { /* object tristate op - don't attempt to optimize it! */ break; } if (ZEND_OP2_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { convert_to_string(&ZEND_OP2_LITERAL(opline)); } } break; case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: /* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */ if (0 && /* FIXME: temporary disable unsafe pattern */ ZEND_OP1_TYPE(opline) == IS_TMP_VAR && ZEND_RESULT_TYPE(opline) == IS_TMP_VAR && ZEND_OP1(opline).var == ZEND_RESULT(opline).var) { opline->opcode -= 3; /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C) in case we know it wouldn't jump */ } else if (ZEND_OP1_TYPE(opline) == IS_CONST) { int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); if (opline->opcode == ZEND_JMPZ_EX) { should_jmp = !should_jmp; } if (!should_jmp) { opline->opcode = ZEND_QM_ASSIGN; SET_UNUSED(opline->op2); } } break; case ZEND_JMPZ: case ZEND_JMPNZ: if (ZEND_OP1_TYPE(opline) == IS_CONST) { int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); if (opline->opcode == ZEND_JMPZ) { should_jmp = !should_jmp; } literal_dtor(&ZEND_OP1_LITERAL(opline)); ZEND_OP1_TYPE(opline) = IS_UNUSED; if (should_jmp) { opline->opcode = ZEND_JMP; COPY_NODE(opline->op1, opline->op2); } else { MAKE_NOP(opline); } break; } if ((opline + 1)->opcode == ZEND_JMP) { /* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */ /* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */ if (ZEND_OP2_JMP_ADDR(opline) == ZEND_OP1_JMP_ADDR(opline + 1)) { /* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */ MAKE_NOP(opline); } else { if (opline->opcode == ZEND_JMPZ) { opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(opline + 1)); } else { opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP2_JMP_ADDR(opline)); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(opline + 1)); } opline->opcode = ZEND_JMPZNZ; } } break; case ZEND_JMPZNZ: if (ZEND_OP1_TYPE(opline) == IS_CONST) { zend_op *target_opline; if (zend_is_true(&ZEND_OP1_LITERAL(opline))) { target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */ } else { target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */ } literal_dtor(&ZEND_OP1_LITERAL(opline)); ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline); ZEND_OP1_TYPE(opline) = IS_UNUSED; opline->opcode = ZEND_JMP; } break; } opline++; } }
int zend_optimizer_replace_by_const(zend_op_array *op_array, zend_op *opline, zend_uchar type, uint32_t var, zval *val) { zend_op *end = op_array->opcodes + op_array->last; while (opline < end) { if (ZEND_OP1_TYPE(opline) == type && ZEND_OP1(opline).var == var) { switch (opline->opcode) { case ZEND_FETCH_DIM_W: case ZEND_FETCH_DIM_RW: case ZEND_FETCH_DIM_FUNC_ARG: case ZEND_FETCH_DIM_UNSET: case ZEND_ASSIGN_DIM: case ZEND_SEPARATE: return 0; case ZEND_SEND_VAR: opline->extended_value = 0; opline->opcode = ZEND_SEND_VAL; break; case ZEND_SEND_VAR_EX: opline->extended_value = 0; opline->opcode = ZEND_SEND_VAL_EX; break; case ZEND_SEND_VAR_NO_REF: if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) { if (opline->extended_value & ZEND_ARG_SEND_BY_REF) { return 0; } opline->extended_value = 0; opline->opcode = ZEND_SEND_VAL_EX; } else { opline->extended_value = 0; opline->opcode = ZEND_SEND_VAL; } break; /* In most cases IS_TMP_VAR operand may be used only once. * The operands are usually destroyed by the opcode handler. * ZEND_CASE is an exception, that keeps operand unchanged, * and allows its reuse. The number of ZEND_CASE instructions * usually terminated by ZEND_FREE that finally kills the value. */ case ZEND_CASE: { zval old_val; ZVAL_COPY_VALUE(&old_val, val); zval_copy_ctor(val); zend_optimizer_update_op1_const(op_array, opline, val); ZVAL_COPY_VALUE(val, &old_val); opline++; continue; } case ZEND_FREE: MAKE_NOP(opline); zval_dtor(val); return 1; default: break; } zend_optimizer_update_op1_const(op_array, opline, val); break; } if (ZEND_OP2_TYPE(opline) == type && ZEND_OP2(opline).var == var) { switch (opline->opcode) { case ZEND_ASSIGN_REF: return 0; default: break; } zend_optimizer_update_op2_const(op_array, opline, val); break; } opline++; } return 1; }
void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) { int i = 0; zend_op *opline = op_array->opcodes; zend_op *end = opline + op_array->last; zend_bool collect_constants = (op_array == &ctx->script->main_op_array); while (opline < end) { switch (opline->opcode) { case ZEND_ADD: case ZEND_SUB: case ZEND_MUL: case ZEND_DIV: case ZEND_MOD: case ZEND_POW: case ZEND_SL: case ZEND_SR: case ZEND_CONCAT: case ZEND_FAST_CONCAT: case ZEND_IS_EQUAL: case ZEND_IS_NOT_EQUAL: case ZEND_IS_SMALLER: case ZEND_IS_SMALLER_OR_EQUAL: case ZEND_IS_IDENTICAL: case ZEND_IS_NOT_IDENTICAL: case ZEND_BW_OR: case ZEND_BW_AND: case ZEND_BW_XOR: case ZEND_BOOL_XOR: if (ZEND_OP1_TYPE(opline) == IS_CONST && ZEND_OP2_TYPE(opline) == IS_CONST) { /* binary operation with constant operands */ binary_op_type binary_op = get_binary_op(opline->opcode); uint32_t tv = ZEND_RESULT(opline).var; /* temporary variable */ zval result; int er; if (opline->opcode == ZEND_DIV && Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_LONG && Z_LVAL(ZEND_OP2_LITERAL(opline)) == 0) { /* div by 0 */ break; } er = EG(error_reporting); EG(error_reporting) = 0; /* evaluate constant expression */ if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) != SUCCESS) { EG(error_reporting) = er; break; } EG(error_reporting) = er; if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, tv, &result)) { literal_dtor(&ZEND_OP1_LITERAL(opline)); literal_dtor(&ZEND_OP2_LITERAL(opline)); MAKE_NOP(opline); } } break; case ZEND_CAST: if (ZEND_OP1_TYPE(opline) == IS_CONST && opline->extended_value != IS_ARRAY && opline->extended_value != IS_OBJECT && opline->extended_value != IS_RESOURCE) { /* cast of constant operand */ zend_uchar type = opline->result_type; uint32_t tv = ZEND_RESULT(opline).var; /* temporary variable */ zval res; res = ZEND_OP1_LITERAL(opline); zval_copy_ctor(&res); switch (opline->extended_value) { case IS_NULL: convert_to_null(&res); break; case _IS_BOOL: convert_to_boolean(&res); break; case IS_LONG: convert_to_long(&res); break; case IS_DOUBLE: convert_to_double(&res); break; case IS_STRING: convert_to_string(&res); break; } if (zend_optimizer_replace_by_const(op_array, opline + 1, type, tv, &res)) { literal_dtor(&ZEND_OP1_LITERAL(opline)); MAKE_NOP(opline); } } else if (opline->extended_value == _IS_BOOL) { /* T = CAST(X, IS_BOOL) => T = BOOL(X) */ opline->opcode = ZEND_BOOL; opline->extended_value = 0; } break; case ZEND_BW_NOT: case ZEND_BOOL_NOT: if (ZEND_OP1_TYPE(opline) == IS_CONST) { /* unary operation on constant operand */ unary_op_type unary_op = get_unary_op(opline->opcode); zval result; uint32_t tv = ZEND_RESULT(opline).var; /* temporary variable */ int er; er = EG(error_reporting); EG(error_reporting) = 0; if (unary_op(&result, &ZEND_OP1_LITERAL(opline)) != SUCCESS) { EG(error_reporting) = er; break; } EG(error_reporting) = er; if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, tv, &result)) { literal_dtor(&ZEND_OP1_LITERAL(opline)); MAKE_NOP(opline); } } break; #if 0 case ZEND_ADD_STRING: case ZEND_ADD_CHAR: { zend_op *next_op = opline + 1; int requires_conversion = (opline->opcode == ZEND_ADD_CHAR? 1 : 0); size_t final_length = 0; zend_string *str; char *ptr; zend_op *last_op; /* There is always a ZEND_RETURN at the end if (next_op>=end) { break; } */ while (next_op->opcode == ZEND_ADD_STRING || next_op->opcode == ZEND_ADD_CHAR) { if (ZEND_RESULT(opline).var != ZEND_RESULT(next_op).var) { break; } if (next_op->opcode == ZEND_ADD_CHAR) { final_length += 1; } else { /* ZEND_ADD_STRING */ final_length += Z_STRLEN(ZEND_OP2_LITERAL(next_op)); } next_op++; } if (final_length == 0) { break; } last_op = next_op; final_length += (requires_conversion? 1 : Z_STRLEN(ZEND_OP2_LITERAL(opline))); str = zend_string_alloc(final_length, 0); str->len = final_length; ptr = str->val; ptr[final_length] = '\0'; if (requires_conversion) { /* ZEND_ADD_CHAR */ char chval = (char)Z_LVAL(ZEND_OP2_LITERAL(opline)); ZVAL_NEW_STR(&ZEND_OP2_LITERAL(opline), str); ptr[0] = chval; opline->opcode = ZEND_ADD_STRING; ptr++; } else { /* ZEND_ADD_STRING */ memcpy(ptr, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline))); ptr += Z_STRLEN(ZEND_OP2_LITERAL(opline)); zend_string_release(Z_STR(ZEND_OP2_LITERAL(opline))); ZVAL_NEW_STR(&ZEND_OP2_LITERAL(opline), str); } next_op = opline + 1; while (next_op < last_op) { if (next_op->opcode == ZEND_ADD_STRING) { memcpy(ptr, Z_STRVAL(ZEND_OP2_LITERAL(next_op)), Z_STRLEN(ZEND_OP2_LITERAL(next_op))); ptr += Z_STRLEN(ZEND_OP2_LITERAL(next_op)); literal_dtor(&ZEND_OP2_LITERAL(next_op)); } else { /* ZEND_ADD_CHAR */ *ptr = (char)Z_LVAL(ZEND_OP2_LITERAL(next_op)); ptr++; } MAKE_NOP(next_op); next_op++; } if (!((ZEND_OPTIMIZER_PASS_5|ZEND_OPTIMIZER_PASS_10) & OPTIMIZATION_LEVEL)) { /* NOP removal is disabled => insert JMP over NOPs */ if (last_op-opline >= 3) { /* If we have more than 2 NOPS then JMP over them */ (opline + 1)->opcode = ZEND_JMP; ZEND_OP1(opline + 1).opline_num = last_op - op_array->opcodes; /* that's OK even for ZE2, since opline_num's are resolved in pass 2 later */ } } } break; #endif case ZEND_FETCH_CONSTANT: if (ZEND_OP1_TYPE(opline) == IS_UNUSED && ZEND_OP2_TYPE(opline) == IS_CONST && Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING && Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("__COMPILER_HALT_OFFSET__") - 1 && memcmp(Z_STRVAL(ZEND_OP2_LITERAL(opline)), "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1) == 0) { /* substitute __COMPILER_HALT_OFFSET__ constant */ zend_execute_data *orig_execute_data = EG(current_execute_data); zend_execute_data fake_execute_data; zval *offset; memset(&fake_execute_data, 0, sizeof(zend_execute_data)); fake_execute_data.func = (zend_function*)op_array; EG(current_execute_data) = &fake_execute_data; if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) { uint32_t tv = ZEND_RESULT(opline).var; if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, offset)) { literal_dtor(&ZEND_OP2_LITERAL(opline)); MAKE_NOP(opline); } } EG(current_execute_data) = orig_execute_data; break; } if (ZEND_OP1_TYPE(opline) == IS_UNUSED && ZEND_OP2_TYPE(opline) == IS_CONST && Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { /* substitute persistent constants */ uint32_t tv = ZEND_RESULT(opline).var; zval c; if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP2_LITERAL(opline)), &c, 1)) { if (!ctx->constants || !zend_optimizer_get_collected_constant(ctx->constants, &ZEND_OP2_LITERAL(opline), &c)) { break; } } if (Z_TYPE(c) == IS_CONSTANT_AST) { break; } if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, &c)) { literal_dtor(&ZEND_OP2_LITERAL(opline)); MAKE_NOP(opline); } } /* class constant */ if (ZEND_OP1_TYPE(opline) != IS_UNUSED && ZEND_OP2_TYPE(opline) == IS_CONST && Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { zend_class_entry *ce = NULL; if (ZEND_OP1_TYPE(opline) == IS_CONST && Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { /* for A::B */ if (op_array->scope && !strncasecmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)), op_array->scope->name->val, Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1)) { ce = op_array->scope; } else { if ((ce = zend_hash_find_ptr(EG(class_table), Z_STR(op_array->literals[opline->op1.constant + 1]))) == NULL || (ce->type == ZEND_INTERNAL_CLASS && ce->info.internal.module->type != MODULE_PERSISTENT) || (ce->type == ZEND_USER_CLASS && ZEND_CE_FILENAME(ce) != op_array->filename)) { break; } } } else if (op_array->scope && ZEND_OP1_TYPE(opline) == IS_VAR && (opline - 1)->opcode == ZEND_FETCH_CLASS && (ZEND_OP1_TYPE(opline - 1) == IS_UNUSED && ((opline - 1)->extended_value & ~ZEND_FETCH_CLASS_NO_AUTOLOAD) == ZEND_FETCH_CLASS_SELF) && ZEND_RESULT((opline - 1)).var == ZEND_OP1(opline).var) { /* for self::B */ ce = op_array->scope; } if (ce) { uint32_t tv = ZEND_RESULT(opline).var; zval *c, t; if ((c = zend_hash_find(&ce->constants_table, Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL) { ZVAL_DEREF(c); if (Z_TYPE_P(c) == IS_CONSTANT_AST) { break; } if (ZEND_IS_CONSTANT_TYPE(Z_TYPE_P(c))) { if (!zend_optimizer_get_persistent_constant(Z_STR_P(c), &t, 1) || ZEND_IS_CONSTANT_TYPE(Z_TYPE(t))) { break; } } else { ZVAL_COPY_VALUE(&t, c); zval_copy_ctor(&t); } if (ZEND_OP1_TYPE(opline) == IS_CONST) { literal_dtor(&ZEND_OP1_LITERAL(opline)); } else { MAKE_NOP((opline - 1)); } if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, &t)) { literal_dtor(&ZEND_OP2_LITERAL(opline)); MAKE_NOP(opline); } } } } break; case ZEND_DO_ICALL: { zend_op *send1_opline = opline - 1; zend_op *send2_opline = NULL; zend_op *init_opline = NULL; while (send1_opline->opcode == ZEND_NOP) { send1_opline--; } if (send1_opline->opcode != ZEND_SEND_VAL || ZEND_OP1_TYPE(send1_opline) != IS_CONST) { /* don't colllect constants after unknown function call */ collect_constants = 0; break; } if (send1_opline->op2.num == 2) { send2_opline = send1_opline; send1_opline--; while (send1_opline->opcode == ZEND_NOP) { send1_opline--; } if (send1_opline->opcode != ZEND_SEND_VAL || ZEND_OP1_TYPE(send1_opline) != IS_CONST) { /* don't colllect constants after unknown function call */ collect_constants = 0; break; } } init_opline = send1_opline - 1; while (init_opline->opcode == ZEND_NOP) { init_opline--; } if (init_opline->opcode != ZEND_INIT_FCALL || ZEND_OP2_TYPE(init_opline) != IS_CONST || Z_TYPE(ZEND_OP2_LITERAL(init_opline)) != IS_STRING) { /* don't colllect constants after unknown function call */ collect_constants = 0; break; } /* define("name", scalar); */ if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("define")-1 && zend_binary_strcasecmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), Z_STRLEN(ZEND_OP2_LITERAL(init_opline)), "define", sizeof("define")-1) == 0) { if (Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING && send2_opline && Z_TYPE(ZEND_OP1_LITERAL(send2_opline)) <= IS_STRING) { if (collect_constants) { zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(send1_opline), &ZEND_OP1_LITERAL(send2_opline)); } if (RESULT_UNUSED(opline) && !zend_memnstr(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), "::", sizeof("::") - 1, Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)) + Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) { opline->opcode = ZEND_DECLARE_CONST; opline->op1_type = IS_CONST; opline->op2_type = IS_CONST; opline->result_type = IS_UNUSED; opline->op1.constant = send1_opline->op1.constant; opline->op2.constant = send2_opline->op1.constant; opline->result.num = 0; literal_dtor(&ZEND_OP2_LITERAL(init_opline)); MAKE_NOP(init_opline); MAKE_NOP(send1_opline); MAKE_NOP(send2_opline); } break; } } /* pre-evaluate constant functions: defined(x) constant(x) function_exists(x) is_callable(x) extension_loaded(x) */ if (!send2_opline && Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING) { if ((Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("function_exists")-1 && !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), "function_exists", sizeof("function_exists")-1)) || (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("is_callable")-1 && !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), "is_callable", sizeof("is_callable")))) { zend_internal_function *func; zend_string *lc_name = zend_string_tolower( Z_STR(ZEND_OP1_LITERAL(send1_opline))); if ((func = zend_hash_find_ptr(EG(function_table), lc_name)) != NULL && func->type == ZEND_INTERNAL_FUNCTION && func->module->type == MODULE_PERSISTENT) { zval t; if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("is_callable") - 1 || func->handler != ZEND_FN(display_disabled_function)) { ZVAL_TRUE(&t); } else { ZVAL_FALSE(&t); } if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, ZEND_RESULT(opline).var, &t)) { literal_dtor(&ZEND_OP2_LITERAL(init_opline)); MAKE_NOP(init_opline); literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); MAKE_NOP(send1_opline); MAKE_NOP(opline); zend_string_release(lc_name); break; } } zend_string_release(lc_name); } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("extension_loaded")-1 && !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), "extension_loaded", sizeof("extension_loaded")-1)) { zval t; zend_string *lc_name = zend_string_tolower( Z_STR(ZEND_OP1_LITERAL(send1_opline))); zend_module_entry *m = zend_hash_find_ptr(&module_registry, lc_name); zend_string_release(lc_name); if (!m) { if (!PG(enable_dl)) { break; } else { ZVAL_FALSE(&t); } } else { if (m->type == MODULE_PERSISTENT) { ZVAL_TRUE(&t); } else { break; } } if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, ZEND_RESULT(opline).var, &t)) { literal_dtor(&ZEND_OP2_LITERAL(init_opline)); MAKE_NOP(init_opline); literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); MAKE_NOP(send1_opline); MAKE_NOP(opline); break; } } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("defined")-1 && !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), "defined", sizeof("defined")-1)) { zval t; if (zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(send1_opline)), &t, 0)) { ZVAL_TRUE(&t); if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, ZEND_RESULT(opline).var, &t)) { literal_dtor(&ZEND_OP2_LITERAL(init_opline)); MAKE_NOP(init_opline); literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); MAKE_NOP(send1_opline); MAKE_NOP(opline); break; } } } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("constant")-1 && !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), "constant", sizeof("constant")-1)) { zval t; if (zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(send1_opline)), &t, 1)) { if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, ZEND_RESULT(opline).var, &t)) { literal_dtor(&ZEND_OP2_LITERAL(init_opline)); MAKE_NOP(init_opline); literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); MAKE_NOP(send1_opline); MAKE_NOP(opline); break; } } } else if ((CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN) == 0 && Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("strlen") - 1 && !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), "strlen", sizeof("strlen") - 1)) { zval t; ZVAL_LONG(&t, Z_STRLEN(ZEND_OP1_LITERAL(send1_opline))); if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, ZEND_RESULT(opline).var, &t)) { literal_dtor(&ZEND_OP2_LITERAL(init_opline)); MAKE_NOP(init_opline); literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); MAKE_NOP(send1_opline); MAKE_NOP(opline); break; } /* dirname(IS_CONST/IS_STRING) -> IS_CONST/IS_STRING */ } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("dirname")-1 && !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), "dirname", sizeof("dirname")-1) && IS_ABSOLUTE_PATH(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) { zend_string *dirname = zend_string_init(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)), 0); dirname->len = zend_dirname(dirname->val, dirname->len); if (IS_ABSOLUTE_PATH(dirname->val, dirname->len)) { zval t; ZVAL_STR(&t, dirname); if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, ZEND_RESULT(opline).var, &t)) { literal_dtor(&ZEND_OP2_LITERAL(init_opline)); MAKE_NOP(init_opline); literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); MAKE_NOP(send1_opline); MAKE_NOP(opline); break; } } else { zend_string_release(dirname); } } } /* don't colllect constants after any other function call */ collect_constants = 0; break; } case ZEND_STRLEN: if (ZEND_OP1_TYPE(opline) == IS_CONST && Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { zval t; ZVAL_LONG(&t, Z_STRLEN(ZEND_OP1_LITERAL(opline))); if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, ZEND_RESULT(opline).var, &t)) { literal_dtor(&ZEND_OP1_LITERAL(opline)); MAKE_NOP(opline); } } break; case ZEND_DEFINED: { zval c; uint32_t tv = ZEND_RESULT(opline).var; if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(opline)), &c, 0)) { break; } ZVAL_TRUE(&c); if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, &c)) { literal_dtor(&ZEND_OP1_LITERAL(opline)); MAKE_NOP(opline); } } break; case ZEND_DECLARE_CONST: if (collect_constants && Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING && Z_TYPE(ZEND_OP2_LITERAL(opline)) <= IS_STRING) { zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)); } break; case ZEND_RETURN: case ZEND_RETURN_BY_REF: case ZEND_GENERATOR_RETURN: case ZEND_EXIT: case ZEND_THROW: case ZEND_CATCH: case ZEND_BRK: case ZEND_CONT: case ZEND_GOTO: case ZEND_FAST_CALL: case ZEND_FAST_RET: case ZEND_JMP: case ZEND_JMPZNZ: case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_ASSERT_CHECK: collect_constants = 0; break; case ZEND_FETCH_R: case ZEND_FETCH_W: case ZEND_FETCH_RW: case ZEND_FETCH_FUNC_ARG: case ZEND_FETCH_IS: case ZEND_FETCH_UNSET: if (opline != op_array->opcodes && (opline-1)->opcode == ZEND_BEGIN_SILENCE && (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL && opline->op1_type == IS_CONST && opline->op2_type == IS_UNUSED && Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING && (Z_STRLEN(ZEND_OP1_LITERAL(opline)) != sizeof("this")-1 || memcmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)), "this", sizeof("this") - 1) != 0)) { int var = opline->result.var; int level = 0; zend_op *op = opline + 1; zend_op *use = NULL; while (op < end) { if (op->opcode == ZEND_BEGIN_SILENCE) { level++; } else if (op->opcode == ZEND_END_SILENCE) { if (level == 0) { break; } else { level--; } } if (op->op1_type == IS_VAR && op->op1.var == var) { if (use) { /* used more than once */ use = NULL; break; } use = op; } else if (op->op2_type == IS_VAR && op->op2.var == var) { if (use) { /* used more than once */ use = NULL; break; } use = op; } op++; } if (use) { if (use->op1_type == IS_VAR && use->op1.var == var) { use->op1_type = IS_CV; use->op1.var = zend_optimizer_lookup_cv(op_array, Z_STR(ZEND_OP1_LITERAL(opline))); MAKE_NOP(opline); } else if (use->op2_type == IS_VAR && use->op2.var == var) { use->op2_type = IS_CV; use->op2.var = zend_optimizer_lookup_cv(op_array, Z_STR(ZEND_OP1_LITERAL(opline))); MAKE_NOP(opline); } } } break; } opline++; i++; } }