Ejemplo n.º 1
0
static inline char *phpdbg_decode_op(
		zend_op_array *ops, const znode_op *op, uint32_t type) /* {{{ */
{
	char *decode = NULL;

	switch (type) {
		case IS_CV: {
			zend_string *var = ops->vars[EX_VAR_TO_NUM(op->var)];
			spprintf(&decode, 0, "$%.*s%c",
				ZSTR_LEN(var) <= 19 ? (int) ZSTR_LEN(var) : 18,
				ZSTR_VAL(var), ZSTR_LEN(var) <= 19 ? 0 : '+');
		} break;

		case IS_VAR:
			spprintf(&decode, 0, "@%u", EX_VAR_TO_NUM(op->var) - ops->last_var);
		break;
		case IS_TMP_VAR:
			spprintf(&decode, 0, "~%u", EX_VAR_TO_NUM(op->var) - ops->last_var);
		break;
		case IS_CONST: {
			zval *literal = RT_CONSTANT(ops, *op);
			decode = phpdbg_short_zval_print(literal, 20);
		} break;
	}
	return decode;
} /* }}} */
Ejemplo n.º 2
0
static PHP_METHOD(Operand, getNumber) {
	php_inspector_operand_t *operand = 
		php_inspector_operand_this();
	php_inspector_opline_t *opline = 
		php_inspector_opline_fetch_from(Z_OBJ(operand->opline));
	php_inspector_scope_t *scope = 
		php_inspector_scope_fetch_from(Z_OBJ(opline->scope));

	switch(opline->opline->opcode) {
		case ZEND_JMP:
		case ZEND_FAST_CALL:
		case ZEND_DECLARE_ANON_CLASS:
		case ZEND_DECLARE_ANON_INHERITED_CLASS:
			if (operand->which == PHP_INSPECTOR_OPLINE_OP1) {
				ZEND_PASS_TWO_UNDO_JMP_TARGET(scope->ops, opline->opline, *operand->op);
				ZVAL_LONG(return_value, operand->op->num);
				ZEND_PASS_TWO_UPDATE_JMP_TARGET(scope->ops, opline->opline, *operand->op);
				break;
			}
		
		case ZEND_JMPZNZ:
		case ZEND_JMPZ:
		case ZEND_JMPNZ:
		case ZEND_JMPZ_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:
			if (operand->which == PHP_INSPECTOR_OPLINE_OP2) {
				ZEND_PASS_TWO_UNDO_JMP_TARGET(scope->ops, opline->opline, *operand->op);
				ZVAL_LONG(return_value, operand->op->num);
				ZEND_PASS_TWO_UPDATE_JMP_TARGET(scope->ops, opline->opline, *operand->op);
				break;
			}

		default: {
			if (operand->type & IS_CONST) {
				ZEND_PASS_TWO_UNDO_CONSTANT(scope->ops, *operand->op);
				ZVAL_LONG(return_value, operand->op->num);
				ZEND_PASS_TWO_UPDATE_CONSTANT(scope->ops, *operand->op);
			} else if (operand->type & IS_TMP_VAR|IS_VAR) {
				ZVAL_LONG(return_value, EX_VAR_TO_NUM(operand->op->num - scope->ops->last_var));
			} else if (operand->type & IS_CV) {
				ZVAL_LONG(return_value, EX_VAR_TO_NUM(operand->op->num));
			}
		}	
	}
} 
Ejemplo n.º 3
0
static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, uint32_t type, HashTable *vars) /* {{{ */
{
	char *decode = NULL;

	switch (type &~ EXT_TYPE_UNUSED) {
		case IS_CV: {
			zend_string *var = ops->vars[EX_VAR_TO_NUM(op->var)];
			asprintf(&decode, "$%.*s%c", ZSTR_LEN(var) <= 19 ? (int) ZSTR_LEN(var) : 18, ZSTR_VAL(var), ZSTR_LEN(var) <= 19 ? 0 : '+');
		} break;

		case IS_VAR:
		case IS_TMP_VAR: {
			zend_ulong id = 0, *pid = NULL;
			if (vars != NULL) {
				if ((pid = zend_hash_index_find_ptr(vars, (zend_ulong) ops->vars - op->var))) {
					id = *pid;
				} else {
					id = zend_hash_num_elements(vars);
					zend_hash_index_update_mem(vars, (zend_ulong) ops->vars - op->var, &id, sizeof(zend_ulong));
				}
			}
			asprintf(&decode, "@" ZEND_ULONG_FMT, id);
		} break;

		case IS_CONST: {
			zval *literal = RT_CONSTANT(ops, *op);
			decode = phpdbg_short_zval_print(literal, 20);
		} break;
	}
	return decode;
} /* }}} */
Ejemplo n.º 4
0
/* We can interpret $a + 5 == 0 as $a = 0 - 5, i.e. shift the adjustment to the other operand.
 * This negated adjustment is what is written into the "adjustment" parameter. */
static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_flags, zend_op *opline, uint32_t var_num, zend_long *adjustment)
{
	zend_op *op = opline;
	while (op != op_array->opcodes) {
		op--;
		if (op->result_type != IS_TMP_VAR || op->result.var != var_num) {
			continue;
		}

		if (op->opcode == ZEND_POST_DEC) {
			if (op->op1_type == IS_CV) {
				*adjustment = -1;
				return EX_VAR_TO_NUM(op->op1.var);
			}
		} else if (op->opcode == ZEND_POST_INC) {
			if (op->op1_type == IS_CV) {
				*adjustment = 1;
				return EX_VAR_TO_NUM(op->op1.var);
			}
		} else if (op->opcode == ZEND_ADD) {
			if (op->op1_type == IS_CV &&
				op->op2_type == IS_CONST &&
				Z_TYPE_P(CRT_CONSTANT(op->op2)) == IS_LONG &&
				Z_LVAL_P(CRT_CONSTANT(op->op2)) != ZEND_LONG_MIN) {
				*adjustment = -Z_LVAL_P(CRT_CONSTANT(op->op2));
				return EX_VAR_TO_NUM(op->op1.var);
			} else if (op->op2_type == IS_CV &&
					   op->op1_type == IS_CONST &&
					   Z_TYPE_P(CRT_CONSTANT(op->op1)) == IS_LONG &&
					   Z_LVAL_P(CRT_CONSTANT(op->op1)) != ZEND_LONG_MIN) {
				*adjustment = -Z_LVAL_P(CRT_CONSTANT(op->op1));
				return EX_VAR_TO_NUM(op->op2.var);
			}
		} else if (op->opcode == ZEND_SUB) {
			if (op->op1_type == IS_CV &&
				op->op2_type == IS_CONST &&
				Z_TYPE_P(CRT_CONSTANT(op->op2)) == IS_LONG) {
				*adjustment = Z_LVAL_P(CRT_CONSTANT(op->op2));
				return EX_VAR_TO_NUM(op->op1.var);
			}
		}
		break;
	}
	return -1;
}
Ejemplo n.º 5
0
static PHP_METHOD(Operand, getName) {
	php_inspector_operand_t *operand = php_inspector_operand_this();

	if (operand->type & IS_CV) {
		php_inspector_opline_t *opline = 
			php_inspector_opline_fetch_from(Z_OBJ(operand->opline));
		php_inspector_scope_t *scope = 
			php_inspector_scope_fetch_from(Z_OBJ(opline->scope));
		
		RETURN_STR(zend_string_copy(scope->ops->vars[EX_VAR_TO_NUM(operand->op->var)]));
	}
}
Ejemplo n.º 6
0
int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
{
	zend_ssa_var *ssa_vars;
	int i;

	if (!ssa->vars) {
		ssa->vars = zend_arena_calloc(arena, ssa->vars_count, sizeof(zend_ssa_var));
	}
	ssa_vars = ssa->vars;

	for (i = 0; i < op_array->last_var; i++) {
		ssa_vars[i].var = i;
		ssa_vars[i].scc = -1;
		ssa_vars[i].definition = -1;
		ssa_vars[i].use_chain = -1;
	}
	for (i = op_array->last_var; i < ssa->vars_count; i++) {
		ssa_vars[i].var = -1;
		ssa_vars[i].scc = -1;
		ssa_vars[i].definition = -1;
		ssa_vars[i].use_chain = -1;
	}

	for (i = op_array->last - 1; i >= 0; i--) {
		zend_ssa_op *op = ssa->ops + i;

		if (op->op1_use >= 0) {
			op->op1_use_chain = ssa_vars[op->op1_use].use_chain;
			ssa_vars[op->op1_use].use_chain = i;
		}
		if (op->op2_use >= 0 && op->op2_use != op->op1_use) {
			op->op2_use_chain = ssa_vars[op->op2_use].use_chain;
			ssa_vars[op->op2_use].use_chain = i;
		}
		if (op->result_use >= 0) {
			op->res_use_chain = ssa_vars[op->result_use].use_chain;
			ssa_vars[op->result_use].use_chain = i;
		}
		if (op->op1_def >= 0) {
			ssa_vars[op->op1_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op1.var);
			ssa_vars[op->op1_def].definition = i;
		}
		if (op->op2_def >= 0) {
			ssa_vars[op->op2_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op2.var);
			ssa_vars[op->op2_def].definition = i;
		}
		if (op->result_def >= 0) {
			ssa_vars[op->result_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].result.var);
			ssa_vars[op->result_def].definition = i;
		}
	}

	for (i = 0; i < ssa->cfg.blocks_count; i++) {
		zend_ssa_phi *phi = ssa->blocks[i].phis;
		while (phi) {
			phi->block = i;
			ssa_vars[phi->ssa_var].var = phi->var;
			ssa_vars[phi->ssa_var].definition_phi = phi;
			if (phi->pi >= 0) {
				if (phi->sources[0] >= 0) {
					zend_ssa_phi *p = ssa_vars[phi->sources[0]].phi_use_chain;
					while (p && p != phi) {
						p = zend_ssa_next_use_phi(ssa, phi->sources[0], p);
					}
					if (!p) {
						phi->use_chains[0] = ssa_vars[phi->sources[0]].phi_use_chain;
						ssa_vars[phi->sources[0]].phi_use_chain = phi;
					}
				}
				/* min and max variables can't be used together */
				if (phi->constraint.min_ssa_var >= 0) {
					phi->sym_use_chain = ssa_vars[phi->constraint.min_ssa_var].sym_use_chain;
					ssa_vars[phi->constraint.min_ssa_var].sym_use_chain = phi;
				} else if (phi->constraint.max_ssa_var >= 0) {
					phi->sym_use_chain = ssa_vars[phi->constraint.max_ssa_var].sym_use_chain;
					ssa_vars[phi->constraint.max_ssa_var].sym_use_chain = phi;
				}
			} else {
				int j;

				for (j = 0; j < ssa->cfg.blocks[i].predecessors_count; j++) {
					if (phi->sources[j] >= 0) {
						zend_ssa_phi *p = ssa_vars[phi->sources[j]].phi_use_chain;
						while (p && p != phi) {
							p = zend_ssa_next_use_phi(ssa, phi->sources[j], p);
						}
						if (!p) {
							phi->use_chains[j] = ssa_vars[phi->sources[j]].phi_use_chain;
							ssa_vars[phi->sources[j]].phi_use_chain = phi;
						}
					}
				}
			}
			phi = phi->next;
		}
	}

	return SUCCESS;
}
Ejemplo n.º 7
0
static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */
{
	zend_basic_block *blocks = ssa->cfg.blocks;
	zend_ssa_block *ssa_blocks = ssa->blocks;
	zend_ssa_op *ssa_ops = ssa->ops;
	int ssa_vars_count = ssa->vars_count;
	int i, j;
	uint32_t k;
	zend_op *opline;
	int *tmp = NULL;
	ALLOCA_FLAG(use_heap);

	// FIXME: Can we optimize this copying out in some cases?
	if (blocks[n].next_child >= 0) {
		tmp = do_alloca(sizeof(int) * (op_array->last_var + op_array->T), use_heap);
		memcpy(tmp, var, sizeof(int) * (op_array->last_var + op_array->T));
		var = tmp;
	}

	if (ssa_blocks[n].phis) {
		zend_ssa_phi *phi = ssa_blocks[n].phis;
		do {
			if (phi->ssa_var < 0) {
				phi->ssa_var = ssa_vars_count;
				var[phi->var] = ssa_vars_count;
				ssa_vars_count++;
			} else {
				var[phi->var] = phi->ssa_var;
			}
			phi = phi->next;
		} while (phi);
	}

	for (k = blocks[n].start; k <= blocks[n].end; k++) {
		opline = op_array->opcodes + k;
		if (opline->opcode != ZEND_OP_DATA) {
			zend_op *next = opline + 1;
			if (k < blocks[n].end &&
			    next->opcode == ZEND_OP_DATA) {
				if (next->op1_type == IS_CV) {
					ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
					//USE_SSA_VAR(next->op1.var);
				} else if (next->op1_type & (IS_VAR|IS_TMP_VAR)) {
					ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
					//USE_SSA_VAR(op_array->last_var + next->op1.var);
				}
				if (next->op2_type == IS_CV) {
					ssa_ops[k + 1].op2_use = var[EX_VAR_TO_NUM(next->op2.var)];
					//USE_SSA_VAR(next->op2.var);
				} else if (next->op2_type & (IS_VAR|IS_TMP_VAR)) {
					ssa_ops[k + 1].op2_use = var[EX_VAR_TO_NUM(next->op2.var)];
					//USE_SSA_VAR(op_array->last_var + next->op2.var);
				}
			}
			if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
				ssa_ops[k].op1_use = var[EX_VAR_TO_NUM(opline->op1.var)];
				//USE_SSA_VAR(op_array->last_var + opline->op1.var)
			}
			if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
				if (opline->op2_type == IS_CV) {
					ssa_ops[k].op2_use = var[EX_VAR_TO_NUM(opline->op2.var)];
				}
				ssa_ops[k].op2_def = ssa_vars_count;
				var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
				ssa_vars_count++;
				//NEW_SSA_VAR(opline->op2.var)
			} else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
				ssa_ops[k].op2_use = var[EX_VAR_TO_NUM(opline->op2.var)];
				//USE_SSA_VAR(op_array->last_var + opline->op2.var)
			}
			switch (opline->opcode) {
				case ZEND_ASSIGN:
					if (opline->op1_type == IS_CV) {
						ssa_ops[k].op1_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
						ssa_vars_count++;
						//NEW_SSA_VAR(opline->op1.var)
					}
					if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
						ssa_ops[k].op2_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
						ssa_vars_count++;
						//NEW_SSA_VAR(opline->op2.var)
					}
					break;
				case ZEND_ASSIGN_REF:
//TODO: ???
					if (opline->op1_type == IS_CV) {
						ssa_ops[k].op1_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
						ssa_vars_count++;
						//NEW_SSA_VAR(opline->op1.var)
					}
					if (opline->op2_type == IS_CV) {
						ssa_ops[k].op2_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
						ssa_vars_count++;
						//NEW_SSA_VAR(opline->op2.var)
					}
					break;
				case ZEND_BIND_GLOBAL:
				case ZEND_BIND_STATIC:
					if (opline->op1_type == IS_CV) {
						ssa_ops[k].op1_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
						ssa_vars_count++;
						//NEW_SSA_VAR(opline->op1.var)
					}
					break;
				case ZEND_ASSIGN_DIM:
				case ZEND_ASSIGN_OBJ:
					if (opline->op1_type == IS_CV) {
						ssa_ops[k].op1_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
						ssa_vars_count++;
						//NEW_SSA_VAR(opline->op1.var)
					}
					if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) {
						ssa_ops[k + 1].op1_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
						ssa_vars_count++;
						//NEW_SSA_VAR(next->op1.var)
					}
					break;
				case ZEND_ADD_ARRAY_ELEMENT:
					ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
				case ZEND_INIT_ARRAY:
					if (((build_flags & ZEND_SSA_RC_INFERENCE)
								|| (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
							&& opline->op1_type == IS_CV) {
						ssa_ops[k].op1_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
						ssa_vars_count++;
						//NEW_SSA_VAR(opline+->op1.var)
					}
					break;
				case ZEND_SEND_VAR_NO_REF:
				case ZEND_SEND_VAR_EX:
				case ZEND_SEND_REF:
				case ZEND_FE_RESET_RW:
//TODO: ???
					if (opline->op1_type == IS_CV) {
						ssa_ops[k].op1_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
						ssa_vars_count++;
						//NEW_SSA_VAR(opline->op1.var)
					}
					break;
				case ZEND_FE_RESET_R:
					if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
						ssa_ops[k].op1_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
						ssa_vars_count++;
						//NEW_SSA_VAR(opline->op1.var)
					}
					break;
				case ZEND_ASSIGN_ADD:
				case ZEND_ASSIGN_SUB:
				case ZEND_ASSIGN_MUL:
				case ZEND_ASSIGN_DIV:
				case ZEND_ASSIGN_MOD:
				case ZEND_ASSIGN_SL:
				case ZEND_ASSIGN_SR:
				case ZEND_ASSIGN_CONCAT:
				case ZEND_ASSIGN_BW_OR:
				case ZEND_ASSIGN_BW_AND:
				case ZEND_ASSIGN_BW_XOR:
				case ZEND_ASSIGN_POW:
				case ZEND_PRE_INC:
				case ZEND_PRE_DEC:
				case ZEND_POST_INC:
				case ZEND_POST_DEC:
					if (opline->op1_type == IS_CV) {
						ssa_ops[k].op1_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
						ssa_vars_count++;
						//NEW_SSA_VAR(opline->op1.var)
					}
					break;
				case ZEND_UNSET_VAR:
					if (opline->extended_value & ZEND_QUICK_SET) {
						ssa_ops[k].op1_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
						ssa_vars_count++;
					}
					break;
				case ZEND_UNSET_DIM:
				case ZEND_UNSET_OBJ:
				case ZEND_FETCH_DIM_W:
				case ZEND_FETCH_DIM_RW:
				case ZEND_FETCH_DIM_FUNC_ARG:
				case ZEND_FETCH_DIM_UNSET:
				case ZEND_FETCH_OBJ_W:
				case ZEND_FETCH_OBJ_RW:
				case ZEND_FETCH_OBJ_FUNC_ARG:
				case ZEND_FETCH_OBJ_UNSET:
					if (opline->op1_type == IS_CV) {
						ssa_ops[k].op1_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
						ssa_vars_count++;
						//NEW_SSA_VAR(opline->op1.var)
					}
					break;
				case ZEND_BIND_LEXICAL:
					if (opline->extended_value || (build_flags & ZEND_SSA_RC_INFERENCE)) {
						ssa_ops[k].op2_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
						ssa_vars_count++;
					}
					break;
				case ZEND_YIELD:
					if (opline->op1_type == IS_CV
							&& ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
								|| (build_flags & ZEND_SSA_RC_INFERENCE))) {
						ssa_ops[k].op1_def = ssa_vars_count;
						var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
						ssa_vars_count++;
					}
				default:
					break;
			}
			if (opline->result_type == IS_CV) {
				ssa_ops[k].result_def = ssa_vars_count;
				var[EX_VAR_TO_NUM(opline->result.var)] = ssa_vars_count;
				ssa_vars_count++;
				//NEW_SSA_VAR(opline->result.var)
			} else if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
				ssa_ops[k].result_def = ssa_vars_count;
				var[EX_VAR_TO_NUM(opline->result.var)] = ssa_vars_count;
				ssa_vars_count++;
				//NEW_SSA_VAR(op_array->last_var + opline->result.var)
			}
		}
	}

	for (i = 0; i < 2; i++) {
		int succ = blocks[n].successors[i];
		if (succ >= 0) {
			zend_ssa_phi *p;
			for (p = ssa_blocks[succ].phis; p; p = p->next) {
				if (p->pi == n) {
					/* e-SSA Pi */
					if (p->constraint.min_var >= 0) {
						p->constraint.min_ssa_var = var[p->constraint.min_var];
					}
					if (p->constraint.max_var >= 0) {
						p->constraint.max_ssa_var = var[p->constraint.max_var];
					}
					for (j = 0; j < blocks[succ].predecessors_count; j++) {
						p->sources[j] = var[p->var];
					}
					if (p->ssa_var < 0) {
						p->ssa_var = ssa_vars_count;
						ssa_vars_count++;
					}
				} else if (p->pi < 0) {
					/* Normal Phi */
					for (j = 0; j < blocks[succ].predecessors_count; j++)
						if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
							break;
						}
					ZEND_ASSERT(j < blocks[succ].predecessors_count);
					p->sources[j] = var[p->var];
				}
			}
			for (p = ssa_blocks[succ].phis; p && (p->pi >= 0); p = p->next) {
				if (p->pi == n) {
					zend_ssa_phi *q = p->next;
					while (q) {
						if (q->pi < 0 && q->var == p->var) {
							for (j = 0; j < blocks[succ].predecessors_count; j++) {
								if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
									break;
								}
							}
							ZEND_ASSERT(j < blocks[succ].predecessors_count);
							q->sources[j] = p->ssa_var;
						}
						q = q->next;
					}
				}
			}
		}
	}

	ssa->vars_count = ssa_vars_count;

	j = blocks[n].children;
	while (j >= 0) {
		// FIXME: Tail call optimization?
		if (zend_ssa_rename(op_array, build_flags, ssa, var, j) != SUCCESS)
			return FAILURE;
		j = blocks[j].next_child;
	}

	if (tmp) {
		free_alloca(tmp, use_heap);
	}

	return SUCCESS;
}
Ejemplo n.º 8
0
/* e-SSA construction: Pi placement (Pi is actually a Phi with single
 * source and constraint).
 * Order of Phis is importent, Pis must be placed before Phis
 */
static void place_essa_pis(
		zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa,
		zend_dfg *dfg) {
	zend_basic_block *blocks = ssa->cfg.blocks;
	int j, blocks_count = ssa->cfg.blocks_count;
	for (j = 0; j < blocks_count; j++) {
		zend_ssa_phi *pi;
		zend_op *opline = op_array->opcodes + ssa->cfg.blocks[j].end;
		int bt; /* successor block number if a condition is true */
		int bf; /* successor block number if a condition is false */

		if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
			continue;
		}
		/* the last instruction of basic block is conditional branch,
		 * based on comparison of CV(s)
		 */
		switch (opline->opcode) {
			case ZEND_JMPZ:
			case ZEND_JMPZNZ:
				bf = ssa->cfg.blocks[j].successors[0];
				bt = ssa->cfg.blocks[j].successors[1];
				break;
			case ZEND_JMPNZ:
				bt = ssa->cfg.blocks[j].successors[0];
				bf = ssa->cfg.blocks[j].successors[1];
				break;
			default:
				continue;
		}
		if (opline->op1_type == IS_TMP_VAR &&
		    ((opline-1)->opcode == ZEND_IS_EQUAL ||
		     (opline-1)->opcode == ZEND_IS_NOT_EQUAL ||
		     (opline-1)->opcode == ZEND_IS_SMALLER ||
		     (opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) &&
		    opline->op1.var == (opline-1)->result.var) {
			int  var1 = -1;
			int  var2 = -1;
			zend_long val1 = 0;
			zend_long val2 = 0;
//			long val = 0;

			if ((opline-1)->op1_type == IS_CV) {
				var1 = EX_VAR_TO_NUM((opline-1)->op1.var);
			} else if ((opline-1)->op1_type == IS_TMP_VAR) {
				var1 = find_adjusted_tmp_var(
					op_array, build_flags, opline, (opline-1)->op1.var, &val2);
			}

			if ((opline-1)->op2_type == IS_CV) {
				var2 = EX_VAR_TO_NUM((opline-1)->op2.var);
			} else if ((opline-1)->op2_type == IS_TMP_VAR) {
				var2 = find_adjusted_tmp_var(
					op_array, build_flags, opline, (opline-1)->op2.var, &val1);
			}

			if (var1 >= 0 && var2 >= 0) {
				if (!sub_will_overflow(val1, val2) && !sub_will_overflow(val2, val1)) {
					zend_long tmp = val1;
					val1 -= val2;
					val2 -= tmp;
				} else {
					var1 = -1;
					var2 = -1;
				}
			} else if (var1 >= 0 && var2 < 0) {
				zend_long add_val2 = 0;
				if ((opline-1)->op2_type == IS_CONST &&
				    Z_TYPE_P(CRT_CONSTANT((opline-1)->op2)) == IS_LONG) {
					add_val2 = Z_LVAL_P(CRT_CONSTANT((opline-1)->op2));
				} else if ((opline-1)->op2_type == IS_CONST &&
				    Z_TYPE_P(CRT_CONSTANT((opline-1)->op2)) == IS_FALSE) {
					add_val2 = 0;
				} else if ((opline-1)->op2_type == IS_CONST &&
				    Z_TYPE_P(CRT_CONSTANT((opline-1)->op2)) == IS_TRUE) {
					add_val2 = 1;
				} else {
					var1 = -1;
				}
				if (!add_will_overflow(val2, add_val2)) {
					val2 += add_val2;
				} else {
					var1 = -1;
				}
			} else if (var1 < 0 && var2 >= 0) {
				zend_long add_val1 = 0;
				if ((opline-1)->op1_type == IS_CONST &&
				    Z_TYPE_P(CRT_CONSTANT((opline-1)->op1)) == IS_LONG) {
					add_val1 = Z_LVAL_P(CRT_CONSTANT((opline-1)->op1));
				} else if ((opline-1)->op1_type == IS_CONST &&
				    Z_TYPE_P(CRT_CONSTANT((opline-1)->op1)) == IS_FALSE) {
					add_val1 = 0;
				} else if ((opline-1)->op1_type == IS_CONST &&
				    Z_TYPE_P(CRT_CONSTANT((opline-1)->op1)) == IS_TRUE) {
					add_val1 = 1;
				} else {
					var2 = -1;
				}
				if (!add_will_overflow(val1, add_val1)) {
					val1 += add_val1;
				} else {
					var2 = -1;
				}
			}

			if (var1 >= 0) {
				if ((opline-1)->opcode == ZEND_IS_EQUAL) {
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
						pi_range_equals(pi, var2, val2);
					}
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
						pi_range_not_equals(pi, var2, val2);
					}
				} else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
						pi_range_equals(pi, var2, val2);
					}
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
						pi_range_not_equals(pi, var2, val2);
					}
				} else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
					if (val2 > ZEND_LONG_MIN) {
						if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
							pi_range_max(pi, var2, val2-1);
						}
					}
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
						pi_range_min(pi, var2, val2);
					}
				} else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
						pi_range_max(pi, var2, val2);
					}
					if (val2 < ZEND_LONG_MAX) {
						if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
							pi_range_min(pi, var2, val2+1);
						}
					}
				}
			}
			if (var2 >= 0) {
				if((opline-1)->opcode == ZEND_IS_EQUAL) {
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
						pi_range_equals(pi, var1, val1);
					}
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
						pi_range_not_equals(pi, var1, val1);
					}
				} else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
						pi_range_equals(pi, var1, val1);
					}
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
						pi_range_not_equals(pi, var1, val1);
					}
				} else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
					if (val1 < ZEND_LONG_MAX) {
						if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
							pi_range_min(pi, var1, val1+1);
						}
					}
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
						pi_range_max(pi, var1, val1);
					}
				} else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
					if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
						pi_range_min(pi, var1, val1);
					}
					if (val1 > ZEND_LONG_MIN) {
						if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
							pi_range_max(pi, var1, val1-1);
						}
					}
				}
			}
		} else if (opline->op1_type == IS_TMP_VAR &&
		           ((opline-1)->opcode == ZEND_POST_INC ||
		            (opline-1)->opcode == ZEND_POST_DEC) &&
		           opline->op1.var == (opline-1)->result.var &&
		           (opline-1)->op1_type == IS_CV) {
			int var = EX_VAR_TO_NUM((opline-1)->op1.var);

			if ((opline-1)->opcode == ZEND_POST_DEC) {
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
					pi_range_equals(pi, -1, -1);
				}
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
					pi_range_not_equals(pi, -1, -1);
				}
			} else if ((opline-1)->opcode == ZEND_POST_INC) {
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
					pi_range_equals(pi, -1, 1);
				}
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
					pi_range_not_equals(pi, -1, 1);
				}
			}
		} else if (opline->op1_type == IS_VAR &&
		           ((opline-1)->opcode == ZEND_PRE_INC ||
		            (opline-1)->opcode == ZEND_PRE_DEC) &&
		           opline->op1.var == (opline-1)->result.var &&
		           (opline-1)->op1_type == IS_CV) {
			int var = EX_VAR_TO_NUM((opline-1)->op1.var);

			if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
				pi_range_equals(pi, -1, 0);
			}
			/* speculative */
			if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
				pi_range_not_equals(pi, -1, 0);
			}
		} else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_TYPE_CHECK &&
				   opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV) {
			int var = EX_VAR_TO_NUM((opline-1)->op1.var);
			uint32_t type = (opline-1)->extended_value;
			if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
				pi_type_mask(pi, mask_for_type_check(type));
			}
			if (type != IS_OBJECT && type != IS_RESOURCE) {
				/* is_object() and is_resource() may return false, even though the value is
				 * an object/resource. */
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
					pi_not_type_mask(pi, mask_for_type_check(type));
				}
			}
		} else if (opline->op1_type == IS_TMP_VAR &&
				   ((opline-1)->opcode == ZEND_IS_IDENTICAL
					|| (opline-1)->opcode == ZEND_IS_NOT_IDENTICAL) &&
				   opline->op1.var == (opline-1)->result.var) {
			int var;
			zval *val;
			uint32_t type_mask;
			if ((opline-1)->op1_type == IS_CV && (opline-1)->op2_type == IS_CONST) {
				var = EX_VAR_TO_NUM((opline-1)->op1.var);
				val = CRT_CONSTANT((opline-1)->op2);
			} else if ((opline-1)->op1_type == IS_CONST && (opline-1)->op2_type == IS_CV) {
				var = EX_VAR_TO_NUM((opline-1)->op2.var);
				val = CRT_CONSTANT((opline-1)->op1);
			} else {
				continue;
			}

			/* We're interested in === null/true/false comparisons here, because they eliminate
			 * a type in the false-branch. Other === VAL comparisons are unlikely to be useful. */
			if (Z_TYPE_P(val) != IS_NULL && Z_TYPE_P(val) != IS_TRUE && Z_TYPE_P(val) != IS_FALSE) {
				continue;
			}

			type_mask = _const_op_type(val);
			if ((opline-1)->opcode == ZEND_IS_IDENTICAL) {
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
					pi_type_mask(pi, type_mask);
				}
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
					pi_not_type_mask(pi, type_mask);
				}
			} else {
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
					pi_type_mask(pi, type_mask);
				}
				if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
					pi_not_type_mask(pi, type_mask);
				}
			}
		}
	}
}
Ejemplo n.º 9
0
int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg) /* {{{ */
{
	int set_size;
	zend_basic_block *blocks = cfg->blocks;
	int blocks_count = cfg->blocks_count;
	zend_bitset tmp, gen, def, use, in, out;
	zend_op *opline;
	uint32_t k;
	int j;

	/* FIXME: can we use "gen" instead of "def" for flow analyzing? */
	set_size = dfg->size;
	tmp = dfg->tmp;
	gen = dfg->gen;
	def = dfg->def;
	use = dfg->use;
	in  = dfg->in;
	out = dfg->out;

	/* Collect "gen", "def" and "use" sets */
	for (j = 0; j < blocks_count; j++) {
		if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
			continue;
		}
		for (k = blocks[j].start; k <= blocks[j].end; k++) {
			opline = op_array->opcodes + k;
			if (opline->opcode != ZEND_OP_DATA) {
				zend_op *next = opline + 1;
				if (k < blocks[j].end &&
					next->opcode == ZEND_OP_DATA) {
					if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
						if (!DFG_ISSET(def, set_size, j, EX_VAR_TO_NUM(next->op1.var))) {
							DFG_SET(use, set_size, j, EX_VAR_TO_NUM(next->op1.var));
						}
					}
					if (next->op2_type == IS_CV) {
						if (!DFG_ISSET(def, set_size, j,EX_VAR_TO_NUM(next->op2.var))) {
							DFG_SET(use, set_size, j, EX_VAR_TO_NUM(next->op2.var));
						}
					} else if (next->op2_type == IS_VAR ||
							   next->op2_type == IS_TMP_VAR) {
						/* ZEND_ASSIGN_??? use the second operand
						   of the following OP_DATA instruction as
						   a temporary variable */
						switch (opline->opcode) {
							case ZEND_ASSIGN_DIM:
							case ZEND_ASSIGN_OBJ:
							case ZEND_ASSIGN_ADD:
							case ZEND_ASSIGN_SUB:
							case ZEND_ASSIGN_MUL:
							case ZEND_ASSIGN_DIV:
							case ZEND_ASSIGN_MOD:
							case ZEND_ASSIGN_SL:
							case ZEND_ASSIGN_SR:
							case ZEND_ASSIGN_CONCAT:
							case ZEND_ASSIGN_BW_OR:
							case ZEND_ASSIGN_BW_AND:
							case ZEND_ASSIGN_BW_XOR:
							case ZEND_ASSIGN_POW:
								break;
							default:
								if (!DFG_ISSET(def, set_size, j, EX_VAR_TO_NUM(next->op2.var))) {
									DFG_SET(use, set_size, j, EX_VAR_TO_NUM(next->op2.var));
								}
						}
					}
				}
				if (opline->op1_type == IS_CV) {
					switch (opline->opcode) {
					case ZEND_ASSIGN:
					case ZEND_ASSIGN_REF:
					case ZEND_BIND_GLOBAL:
					case ZEND_BIND_STATIC:
					case ZEND_SEND_VAR_EX:
					case ZEND_SEND_REF:
					case ZEND_SEND_VAR_NO_REF:
					case ZEND_FE_RESET_R:
					case ZEND_FE_RESET_RW:
					case ZEND_ADD_ARRAY_ELEMENT:
					case ZEND_INIT_ARRAY:
					case ZEND_BIND_LEXICAL:
						if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(opline->op1.var))) {
							// FIXME: include into "use" to ...?
							DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op1.var));
							DFG_SET(def, set_size, j, EX_VAR_TO_NUM(opline->op1.var));
						}
						DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(opline->op1.var));
						break;
					case ZEND_UNSET_VAR:
						ZEND_ASSERT(opline->extended_value & ZEND_QUICK_SET);
						/* break missing intentionally */
					case ZEND_ASSIGN_ADD:
					case ZEND_ASSIGN_SUB:
					case ZEND_ASSIGN_MUL:
					case ZEND_ASSIGN_DIV:
					case ZEND_ASSIGN_MOD:
					case ZEND_ASSIGN_SL:
					case ZEND_ASSIGN_SR:
					case ZEND_ASSIGN_CONCAT:
					case ZEND_ASSIGN_BW_OR:
					case ZEND_ASSIGN_BW_AND:
					case ZEND_ASSIGN_BW_XOR:
					case ZEND_ASSIGN_POW:
					case ZEND_PRE_INC:
					case ZEND_PRE_DEC:
					case ZEND_POST_INC:
					case ZEND_POST_DEC:
					case ZEND_ASSIGN_DIM:
					case ZEND_ASSIGN_OBJ:
					case ZEND_UNSET_DIM:
					case ZEND_UNSET_OBJ:
					case ZEND_FETCH_DIM_W:
					case ZEND_FETCH_DIM_RW:
					case ZEND_FETCH_DIM_FUNC_ARG:
					case ZEND_FETCH_DIM_UNSET:
					case ZEND_FETCH_OBJ_W:
					case ZEND_FETCH_OBJ_RW:
					case ZEND_FETCH_OBJ_FUNC_ARG:
					case ZEND_FETCH_OBJ_UNSET:
						DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(opline->op1.var));
					default:
						if (!DFG_ISSET(def, set_size, j, EX_VAR_TO_NUM(opline->op1.var))) {
							DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op1.var));
						}
					}
				} else if (opline->op1_type == IS_VAR ||
						   opline->op1_type == IS_TMP_VAR) {
					if (!DFG_ISSET(def, set_size, j, EX_VAR_TO_NUM(opline->op1.var))) {
						DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op1.var));
					}
				}
				if (opline->op2_type == IS_CV) {
					switch (opline->opcode) {
						case ZEND_ASSIGN:
						case ZEND_ASSIGN_REF:
						case ZEND_FE_FETCH_R:
						case ZEND_FE_FETCH_RW:
							if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(opline->op2.var))) {
								// FIXME: include into "use" to ...?
								DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op2.var));
								DFG_SET(def, set_size, j, EX_VAR_TO_NUM(opline->op2.var));
							}
							DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(opline->op2.var));
							break;
						default:
							if (!DFG_ISSET(def, set_size, j, EX_VAR_TO_NUM(opline->op2.var))) {
								DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op2.var));
							}
							break;
					}
				} else if (opline->op2_type == IS_VAR ||
						   opline->op2_type == IS_TMP_VAR) {
					if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
						if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(opline->op2.var))) {
							DFG_SET(def, set_size, j, EX_VAR_TO_NUM(opline->op2.var));
						}
						DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(opline->op2.var));
					} else {
						if (!DFG_ISSET(def, set_size, j, EX_VAR_TO_NUM(opline->op2.var))) {
							DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op2.var));
						}
					}
				}
				if (opline->result_type == IS_CV) {
					if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(opline->result.var))) {
						DFG_SET(def, set_size, j, EX_VAR_TO_NUM(opline->result.var));
					}
					DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(opline->result.var));
				} else if (opline->result_type == IS_VAR ||
						   opline->result_type == IS_TMP_VAR) {
					if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(opline->result.var))) {
						DFG_SET(def, set_size, j, EX_VAR_TO_NUM(opline->result.var));
					}
					DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(opline->result.var));
				}
				if ((opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) && opline->result_type == IS_TMP_VAR) {
					if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(next->result.var))) {
						DFG_SET(def, set_size, j, EX_VAR_TO_NUM(next->result.var));
					}
					DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(next->result.var));
				}
			}
		}
	}

	/* Calculate "in" and "out" sets */
	{
		uint32_t worklist_len = zend_bitset_len(blocks_count);
		ALLOCA_FLAG(use_heap);
		zend_bitset worklist = ZEND_BITSET_ALLOCA(worklist_len, use_heap);
		memset(worklist, 0, worklist_len * ZEND_BITSET_ELM_SIZE);
		for (j = 0; j < blocks_count; j++) {
			zend_bitset_incl(worklist, j);
		}
		while (!zend_bitset_empty(worklist, worklist_len)) {
			/* We use the last block on the worklist, because predecessors tend to be located
			 * before the succeeding block, so this converges faster. */
			j = zend_bitset_last(worklist, worklist_len);
			zend_bitset_excl(worklist, j);

			if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
				continue;
			}
			if (blocks[j].successors[0] >= 0) {
				zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size);
				if (blocks[j].successors[1] >= 0) {
					zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[1]), set_size);
				}
			} else {
				zend_bitset_clear(DFG_BITSET(out, set_size, j), set_size);
			}
			zend_bitset_union_with_difference(tmp, DFG_BITSET(use, set_size, j), DFG_BITSET(out, set_size, j), DFG_BITSET(def, set_size, j), set_size);
			if (!zend_bitset_equal(DFG_BITSET(in, set_size, j), tmp, set_size)) {
				zend_bitset_copy(DFG_BITSET(in, set_size, j), tmp, set_size);

				/* Add predecessors of changed block to worklist */
				{
					int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset];
					for (k = 0; k < blocks[j].predecessors_count; k++) {
						zend_bitset_incl(worklist, predecessors[k]);
					}
				}
			}
		}

		free_alloca(worklist, use_heap);
	}

	return SUCCESS;
}
Ejemplo n.º 10
0
int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags) /* {{{ */
{
	int set_size;
	zend_basic_block *blocks = cfg->blocks;
	int blocks_count = cfg->blocks_count;
	zend_bitset tmp, def, use, in, out;
	int k;
	uint32_t var_num;
	int j;

	set_size = dfg->size;
	tmp = dfg->tmp;
	def = dfg->def;
	use = dfg->use;
	in  = dfg->in;
	out = dfg->out;

	/* Collect "def" and "use" sets */
	for (j = 0; j < blocks_count; j++) {
		zend_op *opline, *end;
		if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
			continue;
		}

		opline = op_array->opcodes + blocks[j].start;
		end = opline + blocks[j].len;
		for (; opline < end; opline++) {
			if (opline->opcode != ZEND_OP_DATA) {
				zend_op *next = opline + 1;
				if (next < end && next->opcode == ZEND_OP_DATA) {
					if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
						var_num = EX_VAR_TO_NUM(next->op1.var);
						if (!DFG_ISSET(def, set_size, j, var_num)) {
							DFG_SET(use, set_size, j, var_num);
						}
					}
					if (next->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
						var_num = EX_VAR_TO_NUM(next->op2.var);
						if (!DFG_ISSET(def, set_size, j, var_num)) {
							DFG_SET(use, set_size, j, var_num);
						}
					}
				}
				if (opline->op1_type == IS_CV) {
					var_num = EX_VAR_TO_NUM(opline->op1.var);
					switch (opline->opcode) {
					case ZEND_ADD_ARRAY_ELEMENT:
					case ZEND_INIT_ARRAY:
						if ((build_flags & ZEND_SSA_RC_INFERENCE)
								|| (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
							goto op1_def;
						}
						goto op1_use;
					case ZEND_FE_RESET_R:
					case ZEND_SEND_VAR:
					case ZEND_CAST:
					case ZEND_QM_ASSIGN:
					case ZEND_JMP_SET:
					case ZEND_COALESCE:
						if (build_flags & ZEND_SSA_RC_INFERENCE) {
							goto op1_def;
						}
						goto op1_use;
					case ZEND_YIELD:
						if ((build_flags & ZEND_SSA_RC_INFERENCE)
								|| (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
							goto op1_def;
						}
						goto op1_use;
					case ZEND_UNSET_VAR:
						ZEND_ASSERT(opline->extended_value & ZEND_QUICK_SET);
						/* break missing intentionally */
					case ZEND_ASSIGN:
					case ZEND_ASSIGN_REF:
					case ZEND_BIND_GLOBAL:
					case ZEND_BIND_STATIC:
					case ZEND_SEND_VAR_EX:
					case ZEND_SEND_REF:
					case ZEND_SEND_VAR_NO_REF:
					case ZEND_SEND_VAR_NO_REF_EX:
					case ZEND_FE_RESET_RW:
					case ZEND_ASSIGN_ADD:
					case ZEND_ASSIGN_SUB:
					case ZEND_ASSIGN_MUL:
					case ZEND_ASSIGN_DIV:
					case ZEND_ASSIGN_MOD:
					case ZEND_ASSIGN_SL:
					case ZEND_ASSIGN_SR:
					case ZEND_ASSIGN_CONCAT:
					case ZEND_ASSIGN_BW_OR:
					case ZEND_ASSIGN_BW_AND:
					case ZEND_ASSIGN_BW_XOR:
					case ZEND_ASSIGN_POW:
					case ZEND_PRE_INC:
					case ZEND_PRE_DEC:
					case ZEND_POST_INC:
					case ZEND_POST_DEC:
					case ZEND_ASSIGN_DIM:
					case ZEND_ASSIGN_OBJ:
					case ZEND_UNSET_DIM:
					case ZEND_UNSET_OBJ:
					case ZEND_FETCH_DIM_W:
					case ZEND_FETCH_DIM_RW:
					case ZEND_FETCH_DIM_FUNC_ARG:
					case ZEND_FETCH_DIM_UNSET:
					case ZEND_FETCH_OBJ_W:
					case ZEND_FETCH_OBJ_RW:
					case ZEND_FETCH_OBJ_FUNC_ARG:
					case ZEND_FETCH_OBJ_UNSET:
					case ZEND_VERIFY_RETURN_TYPE:
op1_def:
						/* `def` always come along with dtor or separation,
						 * thus the origin var info might be also `use`d in the feature(CG) */
						DFG_SET(use, set_size, j, var_num);
						DFG_SET(def, set_size, j, var_num);
						break;
					default:
op1_use:
						if (!DFG_ISSET(def, set_size, j, var_num)) {
							DFG_SET(use, set_size, j, var_num);
						}
					}
				} else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
					var_num = EX_VAR_TO_NUM(opline->op1.var);
					if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
						DFG_SET(use, set_size, j, var_num);
						DFG_SET(def, set_size, j, var_num);
					} else if (!DFG_ISSET(def, set_size, j, var_num)) {
						DFG_SET(use, set_size, j, var_num);
					}
				}
				if (opline->op2_type == IS_CV) {
					var_num = EX_VAR_TO_NUM(opline->op2.var);
					switch (opline->opcode) {
						case ZEND_ASSIGN:
							if (build_flags & ZEND_SSA_RC_INFERENCE) {
								goto op2_def;
							}
							goto op2_use;
						case ZEND_BIND_LEXICAL:
							if ((build_flags & ZEND_SSA_RC_INFERENCE) || opline->extended_value) {
								goto op2_def;
							}
							goto op2_use;
						case ZEND_ASSIGN_REF:
						case ZEND_FE_FETCH_R:
						case ZEND_FE_FETCH_RW:
op2_def:
							// FIXME: include into "use" too ...?
							DFG_SET(use, set_size, j, var_num);
							DFG_SET(def, set_size, j, var_num);
							break;
						default:
op2_use:
							if (!DFG_ISSET(def, set_size, j, var_num)) {
								DFG_SET(use, set_size, j, var_num);
							}
							break;
					}
				} else if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
					var_num = EX_VAR_TO_NUM(opline->op2.var);
					if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
						DFG_SET(def, set_size, j, var_num);
					} else {
						if (!DFG_ISSET(def, set_size, j, var_num)) {
							DFG_SET(use, set_size, j, var_num);
						}
					}
				}
				if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
					var_num = EX_VAR_TO_NUM(opline->result.var);
					if ((build_flags & ZEND_SSA_USE_CV_RESULTS)
					 && opline->result_type == IS_CV) {
						DFG_SET(use, set_size, j, var_num);
					}
					DFG_SET(def, set_size, j, var_num);
				}
			}
		}
	}

	/* Calculate "in" and "out" sets */
	{
		uint32_t worklist_len = zend_bitset_len(blocks_count);
		zend_bitset worklist;
		ALLOCA_FLAG(use_heap);
		worklist = ZEND_BITSET_ALLOCA(worklist_len, use_heap);
		memset(worklist, 0, worklist_len * ZEND_BITSET_ELM_SIZE);
		for (j = 0; j < blocks_count; j++) {
			zend_bitset_incl(worklist, j);
		}
		while (!zend_bitset_empty(worklist, worklist_len)) {
			/* We use the last block on the worklist, because predecessors tend to be located
			 * before the succeeding block, so this converges faster. */
			j = zend_bitset_last(worklist, worklist_len);
			zend_bitset_excl(worklist, j);

			if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
				continue;
			}
			if (blocks[j].successors_count != 0) {
				zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size);
				for (k = 1; k < blocks[j].successors_count; k++) {
					zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[k]), set_size);
				}
			} else {
				zend_bitset_clear(DFG_BITSET(out, set_size, j), set_size);
			}
			zend_bitset_union_with_difference(tmp, DFG_BITSET(use, set_size, j), DFG_BITSET(out, set_size, j), DFG_BITSET(def, set_size, j), set_size);
			if (!zend_bitset_equal(DFG_BITSET(in, set_size, j), tmp, set_size)) {
				zend_bitset_copy(DFG_BITSET(in, set_size, j), tmp, set_size);

				/* Add predecessors of changed block to worklist */
				{
					int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset];
					for (k = 0; k < blocks[j].predecessors_count; k++) {
						zend_bitset_incl(worklist, predecessors[k]);
					}
				}
			}
		}

		free_alloca(worklist, use_heap);
	}

	return SUCCESS;
}
Ejemplo n.º 11
0
static zval *smd_get_zval_ptr_cv(zend_execute_data *execute_data, uint32_t var, int type, int force_ret) /* {{{ */ {
    zval *ret = EX_VAR(var);

    if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) {
        if (force_ret) {
            switch (type) {
                case BP_VAR_R:
                case BP_VAR_UNSET:
                    zend_error(E_NOTICE, "Undefined variable: %s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(var))));
                case BP_VAR_IS:
                    ret = &EG(uninitialized_zval);
                    break;
                case BP_VAR_RW:
                    zend_error(E_NOTICE, "Undefined variable: %s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(var))));
                case BP_VAR_W:
                    ZVAL_NULL(ret);
                    break;
            }
        } else {
            return NULL;
        }
    } else {
        ZVAL_DEREF(ret);
    }
    return ret;
} /* }}} */
Ejemplo n.º 12
0
static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const void *data)
{
	const char *name = zend_get_opcode_name(opline->opcode);
	uint32_t flags = zend_get_opcode_flags(opline->opcode);
	uint32_t n = 0;
	int len = 0;
	const zend_ssa *ssa = NULL;

	if (dump_flags & ZEND_DUMP_SSA) {
		ssa = (const zend_ssa*)data;
	}

	if (!b) {
		len = fprintf(stderr, "L%u (%u):", (uint32_t)(opline - op_array->opcodes), opline->lineno);
	}
	fprintf(stderr, "%*c", 12-len, ' ');

	if (!ssa || !ssa->ops || ssa->ops[opline - op_array->opcodes].result_use < 0) {
		if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
			if (ssa && ssa->ops && ssa->ops[opline - op_array->opcodes].result_def >= 0) {
				int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_def;
				zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
			} else {
				zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
			}
			fprintf(stderr, " = ");
		}
	}

	if (name) {
		fprintf(stderr, "%s", (name + 5));
	} else {
		fprintf(stderr, "OP_%d", (int)opline->opcode);
	}

	if (ZEND_VM_EXT_NUM == (flags & ZEND_VM_EXT_MASK)) {
		fprintf(stderr, " %u", opline->extended_value);
	} else if (ZEND_VM_EXT_DIM_OBJ == (flags & ZEND_VM_EXT_MASK)) {
		if (opline->extended_value == ZEND_ASSIGN_DIM) {
			fprintf(stderr, " (dim)");
		} else if (opline->extended_value == ZEND_ASSIGN_OBJ) {
			fprintf(stderr, " (obj)");
		}
	} else if (ZEND_VM_EXT_TYPE == (flags & ZEND_VM_EXT_MASK)) {
		switch (opline->extended_value) {
			case IS_NULL:
				fprintf(stderr, " (null)");
				break;
			case IS_FALSE:
				fprintf(stderr, " (false)");
				break;
			case IS_TRUE:
				fprintf(stderr, " (true)");
				break;
			case IS_LONG:
				fprintf(stderr, " (long)");
				break;
			case IS_DOUBLE:
				fprintf(stderr, " (double)");
				break;
			case IS_STRING:
				fprintf(stderr, " (string)");
				break;
			case IS_ARRAY:
				fprintf(stderr, " (array)");
				break;
			case IS_OBJECT:
				fprintf(stderr, " (object)");
				break;
			case IS_RESOURCE:
				fprintf(stderr, " (resource)");
				break;
			case _IS_BOOL:
				fprintf(stderr, " (bool)");
				break;
			case IS_CALLABLE:
				fprintf(stderr, " (callable)");
				break;
			case IS_VOID:
				fprintf(stderr, " (void)");
				break;
			default:
				fprintf(stderr, " (\?\?\?)");
				break;
		}
	} else if (ZEND_VM_EXT_TYPE_MASK == (flags & ZEND_VM_EXT_MASK)) {
		switch (opline->extended_value) {
			case (1<<IS_NULL):
				fprintf(stderr, " (null)");
				break;
			case (1<<IS_FALSE):
				fprintf(stderr, " (false)");
				break;
			case (1<<IS_TRUE):
				fprintf(stderr, " (true)");
				break;
			case (1<<IS_LONG):
				fprintf(stderr, " (long)");
				break;
			case (1<<IS_DOUBLE):
				fprintf(stderr, " (double)");
				break;
			case (1<<IS_STRING):
				fprintf(stderr, " (string)");
				break;
			case (1<<IS_ARRAY):
				fprintf(stderr, " (array)");
				break;
			case (1<<IS_OBJECT):
				fprintf(stderr, " (object)");
				break;
			case (1<<IS_RESOURCE):
				fprintf(stderr, " (resource)");
				break;
			case ((1<<IS_FALSE)|(1<<IS_TRUE)):
				fprintf(stderr, " (bool)");
				break;
			default:
				fprintf(stderr, " (\?\?\?)");
				break;
		}
	} else if (ZEND_VM_EXT_EVAL == (flags & ZEND_VM_EXT_MASK)) {
		switch (opline->extended_value) {
			case ZEND_EVAL:
				fprintf(stderr, " (eval)");
				break;
			case ZEND_INCLUDE:
				fprintf(stderr, " (include)");
				break;
			case ZEND_INCLUDE_ONCE:
				fprintf(stderr, " (include_once)");
				break;
			case ZEND_REQUIRE:
				fprintf(stderr, " (require)");
				break;
			case ZEND_REQUIRE_ONCE:
				fprintf(stderr, " (require_once)");
				break;
			default:
				fprintf(stderr, " (\?\?\?)");
				break;
		}
	} else if (ZEND_VM_EXT_SRC == (flags & ZEND_VM_EXT_MASK)) {
		if (opline->extended_value == ZEND_RETURNS_VALUE) {
			fprintf(stderr, " (value)");
		} else if (opline->extended_value == ZEND_RETURNS_FUNCTION) {
			fprintf(stderr, " (function)");
		}
	} else {
		if (ZEND_VM_EXT_VAR_FETCH & flags) {
			if (opline->extended_value & ZEND_FETCH_GLOBAL) {
				fprintf(stderr, " (global)");
			} else if (opline->extended_value & ZEND_FETCH_LOCAL) {
				fprintf(stderr, " (local)");
			} else if (opline->extended_value & ZEND_FETCH_GLOBAL_LOCK) {
				fprintf(stderr, " (global+lock)");
			}
		}
		if (ZEND_VM_EXT_ISSET & flags) {
			if (!(opline->extended_value & ZEND_ISEMPTY)) {
				fprintf(stderr, " (isset)");
			} else {
				fprintf(stderr, " (empty)");
			}
		}
		if (ZEND_VM_EXT_ARRAY_INIT & flags) {
			fprintf(stderr, " %u", opline->extended_value >> ZEND_ARRAY_SIZE_SHIFT);
			if (!(opline->extended_value & ZEND_ARRAY_NOT_PACKED)) {
				fprintf(stderr, " (packed)");
			}
		}
		if (ZEND_VM_EXT_REF & flags) {
			if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
				fprintf(stderr, " (ref)");
			}
		}
	}
Ejemplo n.º 13
0
static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, uint32_t type, HashTable *vars) /* {{{ */
{
	char *decode = NULL;

	switch (type &~ EXT_TYPE_UNUSED) {
		case IS_CV: {
			zend_string *var = ops->vars[EX_VAR_TO_NUM(op->var)];
			asprintf(&decode, "$%.*s%c", var->len <= 19 ? (int) var->len : 18, var->val, var->len <= 19 ? 0 : '+');
		} break;

		case IS_VAR:
		case IS_TMP_VAR: {
			zend_ulong id = 0, *pid = NULL;
			if (vars != NULL) {
				if ((pid = zend_hash_index_find_ptr(vars, (zend_ulong) ops->vars - op->var))) {
					id = *pid;
				} else {
					id = zend_hash_num_elements(vars);
					zend_hash_index_update_mem(vars, (zend_ulong) ops->vars - op->var, &id, sizeof(zend_ulong));
				}
			}
			asprintf(&decode, "@" ZEND_ULONG_FMT, id);
		} break;

		case IS_CONST: {
			zval *literal = RT_CONSTANT(ops, *op);
			switch (Z_TYPE_P(literal)) {
				case IS_UNDEF:
					decode = zend_strndup("", 0);
					break;
				case IS_NULL:
					decode = zend_strndup(ZEND_STRL("null"));
					break;
				case IS_FALSE:
					decode = zend_strndup(ZEND_STRL("false"));
					break;
				case IS_TRUE:
					decode = zend_strndup(ZEND_STRL("true"));
					break;
				case IS_LONG:
					asprintf(&decode, "%lld", Z_LVAL_P(literal));
					break;
				case IS_DOUBLE:
					asprintf(&decode, "%.*G", 14, Z_DVAL_P(literal));
					break;
				case IS_STRING: {
					int i;
					zend_string *str = php_addcslashes(Z_STR_P(literal), 0, "\\\"", 2);
					for (i = 0; i < str->len; i++) {
						if (str->val[i] < 32) {
							str->val[i] = ' ';
						}
					}
					asprintf(&decode, "\"%.*s\"%c", str->len <= 18 ? (int) str->len : 17, str->val, str->len <= 18 ? 0 : '+');
					zend_string_release(str);
					} break;
				case IS_RESOURCE:
					asprintf(&decode, "Rsrc #%d", Z_RES_HANDLE_P(literal));
					break;
				case IS_ARRAY:
					asprintf(&decode, "array(%d)", zend_hash_num_elements(Z_ARR_P(literal)));
					break;
				case IS_OBJECT: {
					zend_string *str = Z_OBJCE_P(literal)->name;
					asprintf(&decode, "%.*s%c", str->len <= 18 ? (int) str->len : 18, str->val, str->len <= 18 ? 0 : '+');
					} break;
				case IS_CONSTANT:
					decode = zend_strndup(ZEND_STRL("<constant>"));
					break;
				case IS_CONSTANT_AST:
					decode = zend_strndup(ZEND_STRL("<ast>"));
					break;
				default:
					asprintf(&decode, "unknown type: %d", Z_TYPE_P(literal));
					break;
			}
		} break;

		case IS_UNUSED:
			asprintf(&decode, "<unused>");
		break;
	}
	return decode;
} /* }}} */
Ejemplo n.º 14
0
char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op) /*{{{ */
{
	char *decode[4] = {NULL, NULL, NULL, NULL};

	/* OP1 */
	switch (op->opcode) {
	case ZEND_JMP:
	case ZEND_FAST_CALL:
		asprintf(&decode[1], "J%ld", OP_JMP_ADDR(op, op->op1) - ops->opcodes);
		break;

	case ZEND_INIT_FCALL:
	case ZEND_RECV:
	case ZEND_RECV_INIT:
	case ZEND_RECV_VARIADIC:
		asprintf(&decode[1], "%" PRIu32, op->op1.num);
		break;

	default:
		decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type);
		break;
	}

	/* OP2 */
	switch (op->opcode) {
	/* TODO: ZEND_FAST_CALL, ZEND_FAST_RET op2 */
	case ZEND_JMPZNZ:
		asprintf(&decode[2], "J%u or J%" PRIu32, OP_JMP_ADDR(op, op->op2) - ops->opcodes, ZEND_OFFSET_TO_OPLINE(op, op->extended_value) - ops->opcodes);
		break;

	case ZEND_JMPZ:
	case ZEND_JMPNZ:
	case ZEND_JMPZ_EX:
	case ZEND_JMPNZ_EX:
	case ZEND_JMP_SET:
	case ZEND_ASSERT_CHECK:
		asprintf(&decode[2], "J%ld", OP_JMP_ADDR(op, op->op2) - ops->opcodes);
		break;

	case ZEND_SEND_VAL:
	case ZEND_SEND_VAL_EX:
	case ZEND_SEND_VAR:
	case ZEND_SEND_VAR_NO_REF:
	case ZEND_SEND_REF:
	case ZEND_SEND_VAR_EX:
	case ZEND_SEND_USER:
		asprintf(&decode[2], "%" PRIu32, op->op2.num);
		break;

	default:
		decode[2] = phpdbg_decode_op(ops, &op->op2, op->op2_type);
		break;
	}

	/* RESULT */
	switch (op->opcode) {
	case ZEND_CATCH:
		asprintf(&decode[2], "%" PRIu32, op->result.num);
		break;
	default:
		decode[3] = phpdbg_decode_op(ops, &op->result, op->result_type);
		break;
	}

#if 0
	if (ops->T_liveliness) {
		uint32_t *var = ops->T_liveliness + (op - ops->opcodes);

		if (*var != (uint32_t)-1) {
			smart_str str = {0};

			var = ops->T_liveliness + (*var);
			smart_str_appends(&str, "; [@");
			smart_str_append_long(&str, EX_VAR_TO_NUM(((*var) & ~0x3)) - ops->last_var);
			while (*(++var) != (uint32_t)-1) {
				smart_str_appends(&str, ", @");
				smart_str_append_long(&str, EX_VAR_TO_NUM(((*var) & ~0x3)) - ops->last_var);
			}
			smart_str_appendc(&str, ']');
			smart_str_0(&str);

			asprintf(&decode[0],
				"%-20s %-20s %-20s%-20s",
				decode[1] ? decode[1] : "",
				decode[2] ? decode[2] : "",
				decode[3] ? decode[3] : "",
				ZSTR_VAL(str.s));

			smart_str_free(&str);

			if (decode[1])
				free(decode[1]);
			if (decode[2])
				free(decode[2]);
			if (decode[3])
				free(decode[3]);

			return decode[0];
		}
	}
#endif

	asprintf(&decode[0],
		"%-20s %-20s %-20s",
		decode[1] ? decode[1] : "",
		decode[2] ? decode[2] : "",
		decode[3] ? decode[3] : "");

	if (decode[1])
		free(decode[1]);
	if (decode[2])
		free(decode[2]);
	if (decode[3])
		free(decode[3]);

	return decode[0];
} /* }}} */
Ejemplo n.º 15
0
static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const void *data)
{
	const char *name = zend_get_opcode_name(opline->opcode);
	uint32_t flags = zend_get_opcode_flags(opline->opcode);
	uint32_t n = 0;
	int len = 0;
	const zend_ssa *ssa = NULL;

	if (dump_flags & ZEND_DUMP_SSA) {
		ssa = (const zend_ssa*)data;
	}

	if (!b) {
		len = fprintf(stderr, "L%u:", (uint32_t)(opline - op_array->opcodes));
	}
	fprintf(stderr, "%*c", 8-len, ' ');

	if (!ssa || !ssa->ops || ssa->ops[opline - op_array->opcodes].result_use < 0) {
		if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
			if (ssa && ssa->ops) {
				int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_def;
				ZEND_ASSERT(ssa_var_num >= 0);
				zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
			} else {
				zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
			}
			fprintf(stderr, " = ");
		}
	}

	if (name) {
		fprintf(stderr, "%s", (name + 5));
	} else {
		fprintf(stderr, "OP_%d", (int)opline->opcode);
	}

	if (ZEND_VM_EXT_NUM == (flags & ZEND_VM_EXT_MASK)) {
		fprintf(stderr, " %u", opline->extended_value);
	} else if (ZEND_VM_EXT_DIM_OBJ == (flags & ZEND_VM_EXT_MASK)) {
		if (opline->extended_value == ZEND_ASSIGN_DIM) {
			fprintf(stderr, " (dim)");
		} else if (opline->extended_value == ZEND_ASSIGN_OBJ) {
			fprintf(stderr, " (obj)");
		}
	} else if (ZEND_VM_EXT_CLASS_FETCH == (flags & ZEND_VM_EXT_MASK)) {
		zend_dump_class_fetch_type(opline->extended_value);
	} else if (ZEND_VM_EXT_CONST_FETCH == (flags & ZEND_VM_EXT_MASK)) {
		if (opline->extended_value & IS_CONSTANT_UNQUALIFIED) {
				fprintf(stderr, " (unqualified)");
		}
		if (opline->extended_value & IS_CONSTANT_CLASS) {
				fprintf(stderr, " (__class__)");
		}
		if (opline->extended_value & IS_CONSTANT_IN_NAMESPACE) {
				fprintf(stderr, " (in-namespace)");
		}
	} else if (ZEND_VM_EXT_TYPE == (flags & ZEND_VM_EXT_MASK)) {
		switch (opline->extended_value) {
			case IS_NULL:
				fprintf(stderr, " (null)");
				break;
			case IS_FALSE:
				fprintf(stderr, " (false)");
				break;
			case IS_TRUE:
				fprintf(stderr, " (true)");
				break;
			case IS_LONG:
				fprintf(stderr, " (long)");
				break;
			case IS_DOUBLE:
				fprintf(stderr, " (double)");
				break;
			case IS_STRING:
				fprintf(stderr, " (string)");
				break;
			case IS_ARRAY:
				fprintf(stderr, " (array)");
				break;
			case IS_OBJECT:
				fprintf(stderr, " (object)");
				break;
			case IS_RESOURCE:
				fprintf(stderr, " (resource)");
				break;
			case _IS_BOOL:
				fprintf(stderr, " (bool)");
				break;
			case IS_CALLABLE:
				fprintf(stderr, " (callable)");
				break;
			case IS_VOID:
				fprintf(stderr, " (void)");
				break;
			default:
				fprintf(stderr, " (\?\?\?)");
				break;
		}
	} else if (ZEND_VM_EXT_EVAL == (flags & ZEND_VM_EXT_MASK)) {
		switch (opline->extended_value) {
			case ZEND_EVAL:
				fprintf(stderr, " (eval)");
				break;
			case ZEND_INCLUDE:
				fprintf(stderr, " (include)");
				break;
			case ZEND_INCLUDE_ONCE:
				fprintf(stderr, " (include_once)");
				break;
			case ZEND_REQUIRE:
				fprintf(stderr, " (require)");
				break;
			case ZEND_REQUIRE_ONCE:
				fprintf(stderr, " (require_once)");
				break;
			default:
				fprintf(stderr, " (\?\?\?)");
				break;
		}
	} else if (ZEND_VM_EXT_FAST_CALL == (flags & ZEND_VM_EXT_MASK)) {
		if (opline->extended_value == ZEND_FAST_CALL_FROM_FINALLY) {
			fprintf(stderr, " (from-finally)");
		}
	} else if (ZEND_VM_EXT_FAST_RET == (flags & ZEND_VM_EXT_MASK)) {
		if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
			fprintf(stderr, " (to-catch)");
		} else if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
			fprintf(stderr, " (to-finally)");
		}
	} else if (ZEND_VM_EXT_SRC == (flags & ZEND_VM_EXT_MASK)) {
		if (opline->extended_value == ZEND_RETURNS_VALUE) {
			fprintf(stderr, " (value)");
		} else if (opline->extended_value == ZEND_RETURNS_FUNCTION) {
			fprintf(stderr, " (function)");
		}
	} else if (ZEND_VM_EXT_SEND == (flags & ZEND_VM_EXT_MASK)) {
		if (opline->extended_value & ZEND_ARG_SEND_BY_REF) {
			fprintf(stderr, " (ref)");
		}
		if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) {
			fprintf(stderr, " (compile-time)");
		}
		if (opline->extended_value & ZEND_ARG_SEND_FUNCTION) {
			fprintf(stderr, " (function)");
		}
		if (opline->extended_value & ZEND_ARG_SEND_SILENT) {
			fprintf(stderr, " (silent)");
		}
	} else {
		if (ZEND_VM_EXT_VAR_FETCH & flags) {
			switch (opline->extended_value & ZEND_FETCH_TYPE_MASK) {
				case ZEND_FETCH_GLOBAL:
					fprintf(stderr, " (global)");
					break;
				case ZEND_FETCH_LOCAL:
					fprintf(stderr, " (local)");
					break;
				case ZEND_FETCH_GLOBAL_LOCK:
					fprintf(stderr, " (global+lock)");
					break;
			}
		}
		if (ZEND_VM_EXT_ISSET & flags) {
		    if (opline->extended_value & ZEND_QUICK_SET) {
				fprintf(stderr, " (quick)");
		    }
			if (opline->extended_value & ZEND_ISSET) {
				fprintf(stderr, " (isset)");
			} else if (opline->extended_value & ZEND_ISEMPTY) {
				fprintf(stderr, " (empty)");
			}
		}
		if (ZEND_VM_EXT_ARG_NUM & flags) {
			fprintf(stderr, " %u", opline->extended_value & ZEND_FETCH_ARG_MASK);
		}
		if (ZEND_VM_EXT_ARRAY_INIT & flags) {
			fprintf(stderr, " %u", opline->extended_value >> ZEND_ARRAY_SIZE_SHIFT);
			if (!(opline->extended_value & ZEND_ARRAY_NOT_PACKED)) {
				fprintf(stderr, " (packed)");
			}
		}
		if (ZEND_VM_EXT_REF & flags) {
			if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
				fprintf(stderr, " (ref)");
			}
		}
	}