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;
}
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;
}
Exemple #3
0
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;
	}
}
Exemple #4
0
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;
}
Exemple #5
0
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++;
	}
}
Exemple #6
0
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);
}
Exemple #7
0
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;
}
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);
}
Exemple #9
0
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);
}
Exemple #10
0
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++;
	}
}
Exemple #11
0
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(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;
		}
		opline++;
	}
}
Exemple #12
0
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;
}