コード例 #1
0
ファイル: mman.c プロジェクト: ccotter/libdeterm
/* TODO combine contiguous regions of freed memory. */
void free(void *ptr)
{
    if (NULL == ptr)
        return;
    if (!IS_USED(ptr))
        return;
    SET_UNUSED(ptr);
}
コード例 #2
0
static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func)
{
	if (func->type == ZEND_USER_FUNCTION
	 && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS))
	 && fcall->extended_value >= func->op_array.required_num_args
	 && func->op_array.opcodes[func->op_array.num_args].opcode == ZEND_RETURN) {

		zend_op *ret_opline = func->op_array.opcodes + func->op_array.num_args;

		if (ret_opline->op1_type == IS_CONST) {
			uint32_t i, num_args = func->op_array.num_args;
			num_args += (func->op_array.fn_flags & ZEND_ACC_VARIADIC) != 0;

			if (fcall->opcode == ZEND_INIT_METHOD_CALL && fcall->op1_type == IS_UNUSED) {
				/* TODO: we can't inlne methods, because $this may be used
				 *       not in object context ???
				 */
				return;
			}

			for (i = 0; i < num_args; i++) {
				/* Don't inline functions with by-reference arguments. This would require
				 * correct handling of INDIRECT arguments. */
				if (func->op_array.arg_info[i].pass_by_reference) {
					return;
				}
			}

			if (fcall->extended_value < func->op_array.num_args) {
				/* don't inline funcions with named constants in default arguments */
				i = fcall->extended_value;

				do {
					if (Z_CONSTANT_P(RT_CONSTANT(&func->op_array, func->op_array.opcodes[i].op2))) {
						return;
					}
					i++;
				} while (i < func->op_array.num_args);
			}

			if (RETURN_VALUE_USED(opline)) {
				zval zv;

				ZVAL_DUP(&zv, RT_CONSTANT(&func->op_array, ret_opline->op1));
				opline->opcode = ZEND_QM_ASSIGN;
				opline->op1_type = IS_CONST;
				opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
				SET_UNUSED(opline->op2);
			} else {
				MAKE_NOP(opline);
			}

			zend_delete_call_instructions(opline-1);
		}
	}
}
コード例 #3
0
ファイル: mman.c プロジェクト: ccotter/libdeterm
void *malloc(ssize_t size_)
{
    void *at = mem_ptr;
    uint32_t size = (uint32_t)ROUNDUP(size_, BYTES_PER_LONG) + BYTES_PER_LONG;
    int looped = 0;
    uint32_t expand_size;
    void *expanded;

    /* Scan the current heap for an opening. */
    while (!looped || at < mem_ptr)
    {
        uint32_t at_size = BLOCK_SIZE(at);
        if (!IS_USED(at) && at_size >= size && at_size - size != BYTES_PER_LONG)
        { /* We can use this block. */
            void *leftover = at + size;
            SET_SIZE(at, size);
            SET_USED(at);
            SET_SIZE(leftover, at_size - size);
            SET_UNUSED(leftover);
            mem_ptr = leftover;
            return PTR(at);
        }
        at += at_size;
        if (at >= curr_brk)
        {
            at = orig_brk;
            looped = 1;
        }
    }

    /* Expand the heap. */
    expand_size = size + BUFFER_SIZE;
    expanded = mbrk(curr_brk + expand_size);
    if (expanded != at + expand_size)
        return NULL;
    at = curr_brk;
    curr_brk = expanded;
    SET_SIZE(at, size);
    SET_USED(at);
    SET_SIZE(at + size, expand_size - size);
    SET_UNUSED(at + size);
    return PTR(at);
}
コード例 #4
0
ファイル: mman.c プロジェクト: ccotter/libdeterm
int __init_malloc(void)
{
    curr_brk = mem_ptr = orig_brk = (void*)ROUNDUP(UL(mbrk(0)), BYTES_PER_LONG);
    if (orig_brk + INITIAL_SIZE != mbrk(orig_brk + INITIAL_SIZE))
        return -1;
    SET_SIZE(mem_ptr, INITIAL_SIZE);
    SET_UNUSED(mem_ptr);
    curr_brk = orig_brk + INITIAL_SIZE;
    return 0;
}
コード例 #5
0
ファイル: dfa_pass.c プロジェクト: Apfelfrisch/php-src
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa)
{
	if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
		zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa);
	}

	if (ssa->var_info) {
		int op_1;
		int v;
		int remove_nops = 0;
		zend_op *opline;
		zval tmp;

		for (v = op_array->last_var; v < ssa->vars_count; v++) {

			op_1 = ssa->vars[v].definition;

			if (op_1 < 0) {
				continue;
			}

			opline = op_array->opcodes + op_1;

			/* Convert LONG constants to DOUBLE */
			if (ssa->var_info[v].use_as_double) {
				if (opline->opcode == ZEND_ASSIGN
				 && opline->op2_type == IS_CONST
				 && ssa->ops[op_1].op1_def == v
				 && !RETURN_VALUE_USED(opline)
				) {

// op_1: ASSIGN ? -> #v [use_as_double], long(?) => ASSIGN ? -> #v, double(?)

					zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
					ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
					ZVAL_DOUBLE(&tmp, zval_get_double(zv));
					opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);

				} else if (opline->opcode == ZEND_QM_ASSIGN
				 && opline->op1_type == IS_CONST
				) {

// op_1: QM_ASSIGN #v [use_as_double], long(?) => QM_ASSIGN #v, double(?)

					zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
					ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
					ZVAL_DOUBLE(&tmp, zval_get_double(zv));
					opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
				}

			} else {
				if (opline->opcode == ZEND_ADD
				 || opline->opcode == ZEND_SUB
				 || opline->opcode == ZEND_MUL
				 || opline->opcode == ZEND_IS_EQUAL
				 || opline->opcode == ZEND_IS_NOT_EQUAL
				 || opline->opcode == ZEND_IS_SMALLER
				 || opline->opcode == ZEND_IS_SMALLER_OR_EQUAL
				) {

					if (opline->op1_type == IS_CONST
					 && opline->op2_type != IS_CONST
					 && (OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
					 && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_LONG
					) {

// op_1: #v.? = ADD long(?), #?.? [double] => #v.? = ADD double(?), #?.? [double]

						zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
						ZVAL_DOUBLE(&tmp, zval_get_double(zv));
						opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);

					} else if (opline->op1_type != IS_CONST
					 && opline->op2_type == IS_CONST
					 && (OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
					 && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
					) {

// op_1: #v.? = ADD #?.? [double], long(?) => #v.? = ADD #?.? [double], double(?)

						zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
						ZVAL_DOUBLE(&tmp, zval_get_double(zv));
						opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
					}
				}
			}

			if (ssa->vars[v].var >= op_array->last_var) {
				/* skip TMP and VAR */
				continue;
			}

			if (opline->opcode == ZEND_ASSIGN
			 && ssa->ops[op_1].op1_def == v
			 && !RETURN_VALUE_USED(opline)
			) {
				int orig_var = ssa->ops[op_1].op1_use;

				if (orig_var >= 0
				 && !(ssa->var_info[orig_var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
				) {

					int src_var = ssa->ops[op_1].op2_use;

					if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
					 && src_var >= 0
					 && !(ssa->var_info[src_var].type & MAY_BE_REF)
					 && ssa->vars[src_var].definition >= 0
					 && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
					 && ssa->ops[ssa->vars[src_var].definition].result_use < 0
					 && ssa->vars[src_var].use_chain == op_1
					 && ssa->ops[op_1].op2_use_chain < 0
					 && !ssa->vars[src_var].phi_use_chain
					 && !ssa->vars[src_var].sym_use_chain
					 /* see Zend/tests/generators/aborted_yield_during_new.phpt */
					 && op_array->opcodes[ssa->vars[src_var].definition].opcode != ZEND_NEW
					) {

						int op_2 = ssa->vars[src_var].definition;

// op_2: #src_var.T = OP ...                                     => #v.CV = OP ...
// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, #src_var.T    NOP

						if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
							/* Reconstruct SSA */
							ssa->vars[v].definition = op_2;
							ssa->ops[op_2].result_def = v;

							ssa->vars[src_var].definition = -1;
							ssa->vars[src_var].use_chain = -1;

							ssa->ops[op_1].op1_use = -1;
							ssa->ops[op_1].op2_use = -1;
							ssa->ops[op_1].op1_def = -1;
							ssa->ops[op_1].op1_use_chain = -1;

							/* Update opcodes */
							op_array->opcodes[op_2].result_type = opline->op1_type;
							op_array->opcodes[op_2].result.var = opline->op1.var;
							MAKE_NOP(opline);
							remove_nops = 1;
						}
					} else if (opline->op2_type == IS_CONST
					 || ((opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV))
					     && ssa->ops[op_1].op2_use >= 0
					     && ssa->ops[op_1].op2_def < 0)
					) {

// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, CONST|TMPVAR => QM_ASSIGN v.CV, CONST|TMPVAR

						if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
							/* Reconstruct SSA */
							ssa->ops[op_1].result_def = v;
							ssa->ops[op_1].op1_def = -1;
							ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
							ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
							ssa->ops[op_1].op2_use = -1;
							ssa->ops[op_1].op2_use_chain = -1;

							/* Update opcode */
							opline->result_type = opline->op1_type;
							opline->result.var = opline->op1.var;
							opline->op1_type = opline->op2_type;
							opline->op1.var = opline->op2.var;
							opline->op2_type = IS_UNUSED;
							opline->op2.var = 0;
							opline->opcode = ZEND_QM_ASSIGN;
						}
					}
				}

			} else if (opline->opcode == ZEND_ASSIGN_ADD
			 && opline->extended_value == 0
			 && ssa->ops[op_1].op1_def == v
			 && opline->op2_type == IS_CONST
			 && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
			 && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
			 && ssa->ops[op_1].op1_use >= 0
			 && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {

// op_1: ASSIGN_ADD #?.CV [undef,null,int,foat] ->#v.CV, int(1) => PRE_INC #?.CV ->#v.CV

				opline->opcode = ZEND_PRE_INC;
				SET_UNUSED(opline->op2);

			} else if (opline->opcode == ZEND_ASSIGN_SUB
			 && opline->extended_value == 0
			 && ssa->ops[op_1].op1_def == v
			 && opline->op2_type == IS_CONST
			 && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
			 && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
			 && ssa->ops[op_1].op1_use >= 0
			 && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {

// op_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #v.CV, int(1) => PRE_DEC #?.CV ->#v.CV

				opline->opcode = ZEND_PRE_DEC;
				SET_UNUSED(opline->op2);

			} else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE
			 && ssa->ops[op_1].op1_def == v
			 && ssa->ops[op_1].op1_use >= 0
			 && ssa->ops[op_1].op1_use_chain == -1
			 && ssa->vars[v].use_chain >= 0
			 && (ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_ANY|MAY_BE_UNDEF)) == (ssa->var_info[ssa->ops[op_1].op1_def].type & MAY_BE_ANY)) {

// op_1: VERIFY_RETURN_TYPE #orig_var.CV [T] -> #v.CV [T] => NOP

				int orig_var = ssa->ops[op_1].op1_use;
				int ret = ssa->vars[v].use_chain;

				ssa->vars[orig_var].use_chain = ret;
				ssa->ops[ret].op1_use = orig_var;

				ssa->vars[v].definition = -1;
				ssa->vars[v].use_chain = -1;

				ssa->ops[op_1].op1_def = -1;
				ssa->ops[op_1].op1_use = -1;

				MAKE_NOP(opline);
				remove_nops = 1;

			} else if (ssa->ops[op_1].op1_def == v
			 && !RETURN_VALUE_USED(opline)
			 && ssa->ops[op_1].op1_use >= 0
			 && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
			 && (opline->opcode == ZEND_ASSIGN_ADD
			  || opline->opcode == ZEND_ASSIGN_SUB
			  || opline->opcode == ZEND_ASSIGN_MUL
			  || opline->opcode == ZEND_ASSIGN_DIV
			  || opline->opcode == ZEND_ASSIGN_MOD
			  || opline->opcode == ZEND_ASSIGN_SL
			  || opline->opcode == ZEND_ASSIGN_SR
			  || opline->opcode == ZEND_ASSIGN_BW_OR
			  || opline->opcode == ZEND_ASSIGN_BW_AND
			  || opline->opcode == ZEND_ASSIGN_BW_XOR)
			 && opline->extended_value == 0) {

// op_1: ASSIGN_ADD #orig_var.CV [undef,null,bool,int,double] -> #v.CV, ? => #v.CV = ADD #orig_var.CV, ?

				/* Reconstruct SSA */
				ssa->ops[op_1].result_def = ssa->ops[op_1].op1_def;
				ssa->ops[op_1].op1_def = -1;

				/* Update opcode */
				opline->opcode -= (ZEND_ASSIGN_ADD - ZEND_ADD);
				opline->result_type = opline->op1_type;
				opline->result.var = opline->op1.var;

			}
		}

		if (remove_nops) {
			zend_ssa_remove_nops(op_array, ssa);
		}
	}

	if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) {
		zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa);
	}
}
コード例 #6
0
ファイル: pass3.c プロジェクト: AxiosCros/php-src
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 ((opline->op2_type & (IS_VAR | IS_CV))
						&& opline->op2.var == next_opline->op1.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
								|| (opline->op1_type == 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 ((opline->op1_type & (IS_VAR | IS_CV))
						&& opline->op1.var == next_opline->op1.var
						&& opline->op1_type == next_opline->op1_type) {
						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 (opline->op1.var == opline->result.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 (target->opcode == opline->opcode-3 &&
							SAME_VAR(target->op1, T)) {
						   /* convert T=JMPZ_EX(X,L1), L1: JMPZ(T,L2) to
							  JMPZ_EX(X,L2) */
							CHECK_JMP2(target, continue_jmp_ex_optimization);
							ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target));
						} else if (target->opcode == opline->opcode &&
							SAME_VAR(target->op1, T) &&
							SAME_VAR(target->result, T)) {
						   /* convert T=JMPZ_EX(X,L1), L1: T=JMPZ_EX(T,L2) to
							  JMPZ_EX(X,L2) */
							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 if (target->opcode == INV_EX_COND(opline->opcode) &&
									SAME_VAR(target->op1, T)) {
						   /* convert T=JMPZ_EX(X,L1), L1: JMPNZ(T,L2) to
							  JMPZ_EX(X,L1+1) */
							ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
							break;
						} else if (target->opcode == INV_EX_COND_EX(opline->opcode) &&
									SAME_VAR(target->op1, T) &&
									SAME_VAR(target->result, T)) {
						   /* convert T=JMPZ_EX(X,L1), L1: T=JMPNZ_EX(T,L2) to
							  JMPZ_EX(X,L1+1) */
							ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
							break;
						} else if (target->opcode == ZEND_BOOL &&
						           SAME_VAR(opline->result, target->op1)) {
							/* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to
							   Z = JMPZ_EX(X,L1+1) */
							opline->result.var = target->result.var;
							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(op->result_type == IS_TMP_VAR &&
							   op->result.var == opline->result.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(op->op1_type == IS_TMP_VAR &&
							   op->op1.var == opline->result.var) {
								goto done_jmp_optimization;
							}

							if(op->op2_type == IS_TMP_VAR &&
							   op->op2.var == opline->result.var) {
								goto done_jmp_optimization;
							}
						} /* for */

						for(op = &op_array->opcodes[opline->op2.opline_num]; op<end; op++) {

							if(op->result_type == IS_TMP_VAR &&
							   op->result.var == opline->result.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(op->op1_type == IS_TMP_VAR &&
							   op->op1.var == opline->result.var) {
								goto done_jmp_optimization;
							}

							if(op->op2_type == IS_TMP_VAR &&
							   op->op2.var == opline->result.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 &&
						next_op->op1.var == opline->result.var) {
						MAKE_NOP(next_op);
						opline->opcode -= 2;
						opline->result_type = IS_UNUSED;
					}
				}
				break;
		}
done_jmp_optimization:
		opline++;
		opline_num++;
	}
	free_alloca(jmp_hitlist, use_heap);
}
コード例 #7
0
ファイル: pass2.c プロジェクト: AxiosCros/php-src
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 (opline->op1_type == IS_CONST) {
					if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
						/* don't optimise if it should produce a runtime numeric string error */
						if (is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0)) {
							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 (opline->op2_type == IS_CONST) {
					if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
						/* don't optimise if it should produce a runtime numeric string error */
						if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) {
							convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
						}
					}
				}
				break;

			case ZEND_MOD:
			case ZEND_SL:
			case ZEND_SR:
				if (opline->op1_type == IS_CONST) {
					if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) {
						/* don't optimise if it should produce a runtime numeric string error */
						if (!(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING
							&& !is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0))) {
							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 (opline->op2_type == IS_CONST) {
					if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
						/* don't optimise if it should produce a runtime numeric string error */
						if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING
							&& !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) {
							convert_to_long(&ZEND_OP2_LITERAL(opline));
						}
					}
				}
				break;

			case ZEND_CONCAT:
			case ZEND_FAST_CONCAT:
				if (opline->op1_type == 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 (opline->op2_type == 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
				/* Disabled unsafe pattern: in conjunction with
				 * ZEND_VM_SMART_BRANCH() this may improperly eliminate
				 * assignment to Ti.
				 */
				if (opline->op1_type == IS_TMP_VAR &&
				    opline->result_type == IS_TMP_VAR &&
				    opline->op1.var == opline->result.var) {
					opline->opcode -= 3;
					SET_UNUSED(opline->result);
				} else
#endif
				/* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
				   in case we know it wouldn't jump */
				if (opline->op1_type == 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 (opline->op1_type == 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));
					opline->op1_type = 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) */
						if (opline->op1_type == IS_CV) {
							opline->opcode = ZEND_CHECK_VAR;
							opline->op2.num = 0;
						} else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
							opline->opcode = ZEND_FREE;
							opline->op2.num = 0;
						} else {
							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 (opline->op1_type == 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);
					opline->op1_type = IS_UNUSED;
					opline->opcode = ZEND_JMP;
				}
				break;
		}
		opline++;
	}
}
コード例 #8
0
ファイル: pass2.c プロジェクト: Crell/php-src
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++;
	}
}
コード例 #9
0
ファイル: pass1_5.c プロジェクト: DaveRandom/php-src
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 = (ZEND_OPTIMIZER_PASS_15 & ctx->optimization_level)?
		(op_array == &ctx->script->main_op_array) : 0;

	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:
		case ZEND_SPACESHIP:
		case ZEND_CASE:
			if (opline->op1_type == IS_CONST &&
				opline->op2_type == IS_CONST) {
				/* binary operation with constant operands */
				zval result;

				if (zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
					literal_dtor(&ZEND_OP1_LITERAL(opline));
					literal_dtor(&ZEND_OP2_LITERAL(opline));
					if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &result)) {
						MAKE_NOP(opline);
					} else {
						opline->opcode = ZEND_QM_ASSIGN;
						SET_UNUSED(opline->op2);
						zend_optimizer_update_op1_const(op_array, opline, &result);
					}
				}
			}
			break;

		case ZEND_CAST:
			if (opline->op1_type == IS_CONST) {
				/* cast of constant operand */
				zval result;

				if (zend_optimizer_eval_cast(&result, opline->extended_value, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
					literal_dtor(&ZEND_OP1_LITERAL(opline));
					if (zend_optimizer_replace_by_const(op_array, opline + 1, opline->result_type, opline->result.var, &result)) {
						MAKE_NOP(opline);
					} else {
						opline->opcode = ZEND_QM_ASSIGN;
						opline->extended_value = 0;
						zend_optimizer_update_op1_const(op_array, opline, &result);
					}
					break;
				}
			}

			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 (opline->op1_type == IS_CONST) {
				/* unary operation on constant operand */
				zval result;

				if (zend_optimizer_eval_unary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
					literal_dtor(&ZEND_OP1_LITERAL(opline));
					if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &result)) {
						MAKE_NOP(opline);
					} else {
						opline->opcode = ZEND_QM_ASSIGN;
						zend_optimizer_update_op1_const(op_array, opline, &result);
					}
				}
			}
			break;

		case ZEND_FETCH_CONSTANT:
			if (opline->op2_type == 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) {

					literal_dtor(&ZEND_OP2_LITERAL(opline));
					if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, offset)) {
						MAKE_NOP(opline);
					} else {
						opline->opcode = ZEND_QM_ASSIGN;
						opline->extended_value = 0;
						SET_UNUSED(opline->op2);
						zend_optimizer_update_op1_const(op_array, opline, offset);
					}
				}
				EG(current_execute_data) = orig_execute_data;
				break;
			}

			if (opline->op2_type == IS_CONST &&
				Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
				/* substitute persistent constants */
				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;
				}
				literal_dtor(&ZEND_OP2_LITERAL(opline));
				if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &c)) {
					MAKE_NOP(opline);
				} else {
					opline->opcode = ZEND_QM_ASSIGN;
					opline->extended_value = 0;
					SET_UNUSED(opline->op2);
					zend_optimizer_update_op1_const(op_array, opline, &c);
				}
			}
			break;

		case ZEND_FETCH_CLASS_CONSTANT:
			if (opline->op2_type == IS_CONST &&
				Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {

				zend_class_entry *ce = NULL;

				if (opline->op1_type == IS_CONST &&
			        Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
					/* for A::B */
					if (op_array->scope &&
						!strncasecmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)),
						ZSTR_VAL(op_array->scope->name), 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 &&
								 ce->info.user.filename != op_array->filename)) {
							break;
						}
					}
				} else if (op_array->scope &&
					opline->op1_type == IS_UNUSED &&
					(opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
					/* for self::B */
					ce = op_array->scope;
				} else if (op_array->scope &&
					opline->op1_type == IS_VAR &&
					(opline - 1)->opcode == ZEND_FETCH_CLASS &&
					((opline - 1)->op2_type == IS_UNUSED &&
					((opline - 1)->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) &&
					(opline - 1)->result.var == opline->op1.var) {
					/* for self::B */
					ce = op_array->scope;
				}

				if (ce) {
					zend_class_constant *cc;
					zval *c, t;

					if ((cc = zend_hash_find_ptr(&ce->constants_table,
							Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL &&
						(Z_ACCESS_FLAGS(cc->value) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
						c = &cc->value;
						if (Z_TYPE_P(c) == IS_CONSTANT_AST) {
							zend_ast *ast = Z_ASTVAL_P(c);
							if (ast->kind != ZEND_AST_CONSTANT
							 || !zend_optimizer_get_persistent_constant(zend_ast_get_constant_name(ast), &t, 1)
							 || Z_TYPE(t) == IS_CONSTANT_AST) {
								break;
							}
						} else {
							ZVAL_COPY_OR_DUP(&t, c);
						}

						if (opline->op1_type == IS_CONST) {
							literal_dtor(&ZEND_OP1_LITERAL(opline));
						} else if (opline->op1_type == IS_VAR) {
							MAKE_NOP((opline - 1));
						}
						literal_dtor(&ZEND_OP2_LITERAL(opline));

						if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &t)) {
							MAKE_NOP(opline);
						} else {
							opline->opcode = ZEND_QM_ASSIGN;
							opline->extended_value = 0;
							SET_UNUSED(opline->op2);
							zend_optimizer_update_op1_const(op_array, opline, &t);
						}
					}
				}
			}
			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 ||
			    send1_opline->op1_type != 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 ||
				    send1_opline->op1_type != 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 ||
			    init_opline->op2_type != 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:
			   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) &&
					!zend_optimizer_is_disabled_func("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_optimizer_is_disabled_func("is_callable", sizeof("is_callable") - 1))) {
					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
#ifdef ZEND_WIN32
						 && func->module->handle == NULL
#endif
						) {
						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);
						}
						literal_dtor(&ZEND_OP2_LITERAL(init_opline));
						MAKE_NOP(init_opline);
						literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
						MAKE_NOP(send1_opline);
						if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
							MAKE_NOP(opline);
						} else {
							opline->opcode = ZEND_QM_ASSIGN;
							opline->extended_value = 0;
							SET_UNUSED(opline->op2);
							zend_optimizer_update_op1_const(op_array, opline, &t);
						}
					}
					zend_string_release_ex(lc_name, 0);
					break;
				} 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) &&
					!zend_optimizer_is_disabled_func("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_ex(lc_name, 0);
					if (!m) {
						if (PG(enable_dl)) {
							break;
						} else {
							ZVAL_FALSE(&t);
						}
					} else {
						if (m->type == MODULE_PERSISTENT
#ifdef ZEND_WIN32
						 && m->handle == NULL
#endif
						) {
							ZVAL_TRUE(&t);
						} else {
							break;
						}
					}

					literal_dtor(&ZEND_OP2_LITERAL(init_opline));
					MAKE_NOP(init_opline);
					literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
					MAKE_NOP(send1_opline);
					if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
						MAKE_NOP(opline);
					} else {
						opline->opcode = ZEND_QM_ASSIGN;
						opline->extended_value = 0;
						SET_UNUSED(opline->op2);
						zend_optimizer_update_op1_const(op_array, opline, &t);
					}
					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) &&
					!zend_optimizer_is_disabled_func("constant", sizeof("constant") - 1)) {
					zval t;

					if (zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(send1_opline)), &t, 1)) {
						literal_dtor(&ZEND_OP2_LITERAL(init_opline));
						MAKE_NOP(init_opline);
						literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
						MAKE_NOP(send1_opline);
						if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
							MAKE_NOP(opline);
						} else {
							opline->opcode = ZEND_QM_ASSIGN;
							opline->extended_value = 0;
							SET_UNUSED(opline->op2);
							zend_optimizer_update_op1_const(op_array, opline, &t);
						}
					}
					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) &&
					!zend_optimizer_is_disabled_func("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);
					ZSTR_LEN(dirname) = zend_dirname(ZSTR_VAL(dirname), ZSTR_LEN(dirname));
					if (IS_ABSOLUTE_PATH(ZSTR_VAL(dirname), ZSTR_LEN(dirname))) {
						zval t;

						ZVAL_STR(&t, dirname);
						literal_dtor(&ZEND_OP2_LITERAL(init_opline));
						MAKE_NOP(init_opline);
						literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
						MAKE_NOP(send1_opline);
						if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
							MAKE_NOP(opline);
						} else {
							opline->opcode = ZEND_QM_ASSIGN;
							opline->extended_value = 0;
							SET_UNUSED(opline->op2);
							zend_optimizer_update_op1_const(op_array, opline, &t);
						}
					} else {
						zend_string_release_ex(dirname, 0);
					}
					break;
				}
			}
			/* don't colllect constants after any other function call */
			collect_constants = 0;
			break;
		}
		case ZEND_STRLEN:
			if (opline->op1_type == IS_CONST) {
				zval t;

				if (zend_optimizer_eval_strlen(&t, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
					literal_dtor(&ZEND_OP1_LITERAL(opline));
					if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &t)) {
						MAKE_NOP(opline);
					} else {
						opline->opcode = ZEND_QM_ASSIGN;
						zend_optimizer_update_op1_const(op_array, opline, &t);
					}
				}
			}
			break;
		case ZEND_DEFINED:
			{
				zval c;
				if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(opline)), &c, 0)) {
					break;
				}
				ZVAL_TRUE(&c);
				literal_dtor(&ZEND_OP1_LITERAL(opline));
				if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &c)) {
					MAKE_NOP(opline);
				} else {
					opline->opcode = ZEND_QM_ASSIGN;
					zend_optimizer_update_op1_const(op_array, opline, &c);
				}
			}
			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_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_JMP_SET:
		case ZEND_COALESCE:
		case ZEND_ASSERT_CHECK:
			collect_constants = 0;
			break;
		}
		opline++;
		i++;
	}
}
コード例 #10
0
ファイル: pass2.c プロジェクト: Nevett/php-src
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++;
	}
}