mirror of
https://github.com/php/php-src.git
synced 2024-09-30 06:16:08 +00:00
a1c48d5e3a
* Add FETCH_LIST to inference * Restrict JMP_SET/COALESCE result type * Fix typos in range inference * Add const annotations in zend_ssa.h * For pi statements dump the associated predecessor block * If type can be both true and false, dump bool instead
1195 lines
35 KiB
C
1195 lines
35 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| Zend Engine, Bytecode Visualisation |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 1998-2016 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Authors: Dmitry Stogov <dmitry@zend.com> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "php.h"
|
|
#include "zend_compile.h"
|
|
#include "zend_cfg.h"
|
|
#include "zend_ssa.h"
|
|
#include "zend_inference.h"
|
|
#include "zend_func_info.h"
|
|
#include "zend_call_graph.h"
|
|
#include "zend_dump.h"
|
|
|
|
static void zend_dump_const(const zval *zv)
|
|
{
|
|
switch (Z_TYPE_P(zv)) {
|
|
case IS_NULL:
|
|
fprintf(stderr, " null");
|
|
break;
|
|
case IS_FALSE:
|
|
fprintf(stderr, " bool(false)");
|
|
break;
|
|
case IS_TRUE:
|
|
fprintf(stderr, " bool(true)");
|
|
break;
|
|
case IS_LONG:
|
|
fprintf(stderr, " int(" ZEND_LONG_FMT ")", Z_LVAL_P(zv));
|
|
break;
|
|
case IS_DOUBLE:
|
|
fprintf(stderr, " float(%g)", Z_DVAL_P(zv));
|
|
break;
|
|
case IS_STRING:
|
|
fprintf(stderr, " string(\"%s\")", Z_STRVAL_P(zv));
|
|
break;
|
|
case IS_ARRAY:
|
|
fprintf(stderr, " array(...)");
|
|
break;
|
|
default:
|
|
fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void zend_dump_class_fetch_type(uint32_t fetch_type)
|
|
{
|
|
switch (fetch_type & ZEND_FETCH_CLASS_MASK) {
|
|
case ZEND_FETCH_CLASS_SELF:
|
|
fprintf(stderr, " (self)");
|
|
break;
|
|
case ZEND_FETCH_CLASS_PARENT:
|
|
fprintf(stderr, " (parent)");
|
|
break;
|
|
case ZEND_FETCH_CLASS_STATIC:
|
|
fprintf(stderr, " (static)");
|
|
break;
|
|
case ZEND_FETCH_CLASS_AUTO:
|
|
fprintf(stderr, " (auto)");
|
|
break;
|
|
case ZEND_FETCH_CLASS_INTERFACE:
|
|
fprintf(stderr, " (interface)");
|
|
break;
|
|
case ZEND_FETCH_CLASS_TRAIT:
|
|
fprintf(stderr, " (trait)");
|
|
break;
|
|
}
|
|
if (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) {
|
|
fprintf(stderr, " (no-autolod)");
|
|
}
|
|
if (fetch_type & ZEND_FETCH_CLASS_SILENT) {
|
|
fprintf(stderr, " (silent)");
|
|
}
|
|
if (fetch_type & ZEND_FETCH_CLASS_EXCEPTION) {
|
|
fprintf(stderr, " (exception)");
|
|
}
|
|
}
|
|
|
|
static void zend_dump_unused_op(const zend_op *opline, znode_op op, uint32_t flags) {
|
|
if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) {
|
|
fprintf(stderr, " %u", op.num);
|
|
} else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) {
|
|
if (opline->opcode != ZEND_FAST_RET || opline->extended_value) {
|
|
fprintf(stderr, " try-catch(%u)", op.num);
|
|
}
|
|
} else if (ZEND_VM_OP_LIVE_RANGE == (flags & ZEND_VM_OP_MASK)) {
|
|
if (opline->extended_value & ZEND_FREE_ON_RETURN) {
|
|
fprintf(stderr, " live-range(%u)", op.num);
|
|
}
|
|
} else if (ZEND_VM_OP_THIS == (flags & ZEND_VM_OP_MASK)) {
|
|
fprintf(stderr, " THIS");
|
|
} else if (ZEND_VM_OP_NEXT == (flags & ZEND_VM_OP_MASK)) {
|
|
fprintf(stderr, " NEXT");
|
|
} else if (ZEND_VM_OP_CLASS_FETCH == (flags & ZEND_VM_OP_MASK)) {
|
|
zend_dump_class_fetch_type(op.num);
|
|
} else if (ZEND_VM_OP_CONSTRUCTOR == (flags & ZEND_VM_OP_MASK)) {
|
|
fprintf(stderr, " CONSTRUCTOR");
|
|
}
|
|
}
|
|
|
|
void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num)
|
|
{
|
|
if (var_type == IS_CV && var_num < op_array->last_var) {
|
|
fprintf(stderr, "CV%d($%s)", var_num, op_array->vars[var_num]->val);
|
|
} else if (var_type == IS_VAR) {
|
|
fprintf(stderr, "V%d", var_num);
|
|
} else if (var_type == IS_TMP_VAR) {
|
|
fprintf(stderr, "T%d", var_num);
|
|
} else {
|
|
fprintf(stderr, "X%d", var_num);
|
|
}
|
|
}
|
|
|
|
static void zend_dump_range(const zend_ssa_range *r)
|
|
{
|
|
if (r->underflow && r->overflow) {
|
|
return;
|
|
}
|
|
fprintf(stderr, " RANGE[");
|
|
if (r->underflow) {
|
|
fprintf(stderr, "--..");
|
|
} else {
|
|
fprintf(stderr, ZEND_LONG_FMT "..", r->min);
|
|
}
|
|
if (r->overflow) {
|
|
fprintf(stderr, "++]");
|
|
} else {
|
|
fprintf(stderr, ZEND_LONG_FMT "]", r->max);
|
|
}
|
|
}
|
|
|
|
static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_instanceof, uint32_t dump_flags)
|
|
{
|
|
int first = 1;
|
|
|
|
fprintf(stderr, " [");
|
|
if (info & MAY_BE_UNDEF) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "undef");
|
|
}
|
|
if (info & MAY_BE_REF) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "ref");
|
|
}
|
|
if (dump_flags & ZEND_DUMP_RC_INFERENCE) {
|
|
if (info & MAY_BE_RC1) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "rc1");
|
|
}
|
|
if (info & MAY_BE_RCN) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "rcn");
|
|
}
|
|
}
|
|
if (info & MAY_BE_CLASS) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "class");
|
|
if (ce) {
|
|
if (is_instanceof) {
|
|
fprintf(stderr, " (instanceof %s)", ce->name->val);
|
|
} else {
|
|
fprintf(stderr, " (%s)", ce->name->val);
|
|
}
|
|
}
|
|
} else if ((info & MAY_BE_ANY) == MAY_BE_ANY) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "any");
|
|
} else {
|
|
if (info & MAY_BE_NULL) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "null");
|
|
}
|
|
if ((info & MAY_BE_FALSE) && (info & MAY_BE_TRUE)) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "bool");
|
|
} else if (info & MAY_BE_FALSE) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "false");
|
|
} else if (info & MAY_BE_TRUE) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "true");
|
|
}
|
|
if (info & MAY_BE_LONG) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "long");
|
|
}
|
|
if (info & MAY_BE_DOUBLE) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "double");
|
|
}
|
|
if (info & MAY_BE_STRING) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "string");
|
|
}
|
|
if (info & MAY_BE_ARRAY) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "array");
|
|
if ((info & MAY_BE_ARRAY_KEY_ANY) != 0 &&
|
|
(info & MAY_BE_ARRAY_KEY_ANY) != MAY_BE_ARRAY_KEY_ANY) {
|
|
int afirst = 1;
|
|
fprintf(stderr, " [");
|
|
if (info & MAY_BE_ARRAY_KEY_LONG) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "long");
|
|
}
|
|
if (info & MAY_BE_ARRAY_KEY_STRING) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "string");
|
|
}
|
|
fprintf(stderr, "]");
|
|
}
|
|
if (info & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF)) {
|
|
int afirst = 1;
|
|
fprintf(stderr, " of [");
|
|
if ((info & MAY_BE_ARRAY_OF_ANY) == MAY_BE_ARRAY_OF_ANY) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "any");
|
|
} else {
|
|
if (info & MAY_BE_ARRAY_OF_NULL) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "null");
|
|
}
|
|
if (info & MAY_BE_ARRAY_OF_FALSE) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "false");
|
|
}
|
|
if (info & MAY_BE_ARRAY_OF_TRUE) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "true");
|
|
}
|
|
if (info & MAY_BE_ARRAY_OF_LONG) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "long");
|
|
}
|
|
if (info & MAY_BE_ARRAY_OF_DOUBLE) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "double");
|
|
}
|
|
if (info & MAY_BE_ARRAY_OF_STRING) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "string");
|
|
}
|
|
if (info & MAY_BE_ARRAY_OF_ARRAY) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "array");
|
|
}
|
|
if (info & MAY_BE_ARRAY_OF_OBJECT) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "object");
|
|
}
|
|
if (info & MAY_BE_ARRAY_OF_RESOURCE) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "resource");
|
|
}
|
|
}
|
|
if (info & MAY_BE_ARRAY_OF_REF) {
|
|
if (afirst) afirst = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "ref");
|
|
}
|
|
fprintf(stderr, "]");
|
|
}
|
|
}
|
|
if (info & MAY_BE_OBJECT) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "object");
|
|
if (ce) {
|
|
if (is_instanceof) {
|
|
fprintf(stderr, " (instanceof %s)", ce->name->val);
|
|
} else {
|
|
fprintf(stderr, " (%s)", ce->name->val);
|
|
}
|
|
}
|
|
}
|
|
if (info & MAY_BE_RESOURCE) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "resource");
|
|
}
|
|
}
|
|
if (info & MAY_BE_ERROR) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "error");
|
|
}
|
|
//TODO: this is useful only for JIT???
|
|
if (info & MAY_BE_IN_REG) {
|
|
if (first) first = 0; else fprintf(stderr, ", ");
|
|
fprintf(stderr, "reg");
|
|
}
|
|
fprintf(stderr, "]");
|
|
}
|
|
|
|
static void zend_dump_ssa_var_info(const zend_ssa *ssa, int ssa_var_num, uint32_t dump_flags)
|
|
{
|
|
zend_dump_type_info(
|
|
ssa->var_info[ssa_var_num].type,
|
|
ssa->var_info[ssa_var_num].ce,
|
|
ssa->var_info[ssa_var_num].ce ?
|
|
ssa->var_info[ssa_var_num].is_instanceof : 0,
|
|
dump_flags);
|
|
}
|
|
|
|
static void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int ssa_var_num, zend_uchar var_type, int var_num, uint32_t dump_flags)
|
|
{
|
|
if (ssa_var_num >= 0) {
|
|
fprintf(stderr, "#%d.", ssa_var_num);
|
|
} else {
|
|
fprintf(stderr, "#?.");
|
|
}
|
|
zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : var_type), var_num);
|
|
|
|
if (ssa_var_num >= 0 && ssa->vars) {
|
|
if (ssa_var_num >= 0 && ssa->vars[ssa_var_num].no_val) {
|
|
fprintf(stderr, " NOVAL");
|
|
}
|
|
if (ssa->var_info) {
|
|
zend_dump_ssa_var_info(ssa, ssa_var_num, dump_flags);
|
|
if (ssa->var_info[ssa_var_num].has_range) {
|
|
zend_dump_range(&ssa->var_info[ssa_var_num].range);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void zend_dump_pi_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_pi_constraint *r, uint32_t dump_flags)
|
|
{
|
|
if (r->type_mask != (uint32_t) -1) {
|
|
fprintf(stderr, " TYPE");
|
|
zend_dump_type_info(r->type_mask, NULL, 0, dump_flags);
|
|
return;
|
|
}
|
|
|
|
if (r->range.underflow && r->range.overflow) {
|
|
return;
|
|
}
|
|
fprintf(stderr, " RANGE");
|
|
if (r->negative) {
|
|
fprintf(stderr, "~");
|
|
}
|
|
fprintf(stderr, "[");
|
|
if (r->range.underflow) {
|
|
fprintf(stderr, "-- .. ");
|
|
} else {
|
|
if (r->min_ssa_var >= 0) {
|
|
zend_dump_ssa_var(op_array, ssa, r->min_ssa_var, (r->min_var < op_array->last_var ? IS_CV : 0), r->min_var, dump_flags);
|
|
if (r->range.min > 0) {
|
|
fprintf(stderr, " + " ZEND_LONG_FMT, r->range.min);
|
|
} else if (r->range.min < 0) {
|
|
fprintf(stderr, " - " ZEND_LONG_FMT, -r->range.min);
|
|
}
|
|
fprintf(stderr, " .. ");
|
|
} else {
|
|
fprintf(stderr, ZEND_LONG_FMT " .. ", r->range.min);
|
|
}
|
|
}
|
|
if (r->range.overflow) {
|
|
fprintf(stderr, "++]");
|
|
} else {
|
|
if (r->max_ssa_var >= 0) {
|
|
zend_dump_ssa_var(op_array, ssa, r->max_ssa_var, (r->max_var < op_array->last_var ? IS_CV : 0), r->max_var, dump_flags);
|
|
if (r->range.max > 0) {
|
|
fprintf(stderr, " + " ZEND_LONG_FMT, r->range.max);
|
|
} else if (r->range.max < 0) {
|
|
fprintf(stderr, " - " ZEND_LONG_FMT, -r->range.max);
|
|
}
|
|
fprintf(stderr, "]");
|
|
} else {
|
|
fprintf(stderr, ZEND_LONG_FMT "]", r->range.max);
|
|
}
|
|
}
|
|
}
|
|
|
|
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)");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (opline->op1_type == IS_CONST) {
|
|
zend_dump_const(CRT_CONSTANT_EX(op_array, opline->op1, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
|
|
} else if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
|
if (ssa && ssa->ops) {
|
|
int ssa_var_num = ssa->ops[opline - op_array->opcodes].op1_use;
|
|
if (ssa_var_num >= 0) {
|
|
fprintf(stderr, " ");
|
|
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var), dump_flags);
|
|
} else if (ssa->ops[opline - op_array->opcodes].op1_def < 0) {
|
|
fprintf(stderr, " ");
|
|
zend_dump_var(op_array, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var));
|
|
}
|
|
} else {
|
|
fprintf(stderr, " ");
|
|
zend_dump_var(op_array, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var));
|
|
}
|
|
if (ssa && ssa->ops) {
|
|
int ssa_var_num = ssa->ops[opline - op_array->opcodes].op1_def;
|
|
if (ssa_var_num >= 0) {
|
|
fprintf(stderr, " -> ");
|
|
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var), dump_flags);
|
|
}
|
|
}
|
|
} else {
|
|
uint32_t op1_flags = ZEND_VM_OP1_FLAGS(flags);
|
|
if (ZEND_VM_OP_JMP_ADDR == (op1_flags & ZEND_VM_OP_MASK)) {
|
|
if (b) {
|
|
fprintf(stderr, " BB%d", b->successors[n++]);
|
|
} else {
|
|
fprintf(stderr, " L%u", (uint32_t)(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes));
|
|
}
|
|
} else {
|
|
zend_dump_unused_op(opline, opline->op1, op1_flags);
|
|
}
|
|
}
|
|
|
|
if (opline->op2_type == IS_CONST) {
|
|
zend_dump_const(CRT_CONSTANT_EX(op_array, opline->op2, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
|
|
} else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
|
if (ssa && ssa->ops) {
|
|
int ssa_var_num = ssa->ops[opline - op_array->opcodes].op2_use;
|
|
if (ssa_var_num >= 0) {
|
|
fprintf(stderr, " ");
|
|
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var), dump_flags);
|
|
} else if (ssa->ops[opline - op_array->opcodes].op2_def < 0) {
|
|
fprintf(stderr, " ");
|
|
zend_dump_var(op_array, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var));
|
|
}
|
|
} else {
|
|
fprintf(stderr, " ");
|
|
zend_dump_var(op_array, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var));
|
|
}
|
|
if (ssa && ssa->ops) {
|
|
int ssa_var_num = ssa->ops[opline - op_array->opcodes].op2_def;
|
|
if (ssa_var_num >= 0) {
|
|
fprintf(stderr, " -> ");
|
|
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var), dump_flags);
|
|
}
|
|
}
|
|
} else {
|
|
uint32_t op2_flags = ZEND_VM_OP2_FLAGS(flags);
|
|
if (ZEND_VM_OP_JMP_ADDR == (op2_flags & ZEND_VM_OP_MASK)) {
|
|
if (b) {
|
|
fprintf(stderr, " BB%d", b->successors[n++]);
|
|
} else {
|
|
fprintf(stderr, " L%u", (uint32_t)(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes));
|
|
}
|
|
} else {
|
|
zend_dump_unused_op(opline, opline->op2, op2_flags);
|
|
}
|
|
}
|
|
|
|
if (ZEND_VM_EXT_JMP_ADDR == (flags & ZEND_VM_EXT_MASK)) {
|
|
if (opline->opcode != ZEND_CATCH || !opline->result.num) {
|
|
if (b) {
|
|
fprintf(stderr, " BB%d", b->successors[n++]);
|
|
} else {
|
|
fprintf(stderr, " L%u", (uint32_t)ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
|
|
}
|
|
}
|
|
}
|
|
if (opline->result_type == IS_CONST) {
|
|
zend_dump_const(CRT_CONSTANT_EX(op_array, opline->result, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
|
|
} else 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_use;
|
|
if (ssa_var_num >= 0) {
|
|
fprintf(stderr, " ");
|
|
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
|
|
}
|
|
} else {
|
|
fprintf(stderr, " ");
|
|
zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
|
|
}
|
|
if (ssa && ssa->ops) {
|
|
int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_def;
|
|
if (ssa_var_num >= 0) {
|
|
fprintf(stderr, " -> ");
|
|
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
|
|
{
|
|
zend_basic_block *b = cfg->blocks + n;
|
|
int printed = 0;
|
|
|
|
fprintf(stderr, "BB%d:", n);
|
|
if (b->flags & ZEND_BB_START) {
|
|
fprintf(stderr, " start");
|
|
}
|
|
if (b->flags & ZEND_BB_FOLLOW) {
|
|
fprintf(stderr, " follow");
|
|
}
|
|
if (b->flags & ZEND_BB_TARGET) {
|
|
fprintf(stderr, " target");
|
|
}
|
|
if (b->flags & ZEND_BB_EXIT) {
|
|
fprintf(stderr, " exit");
|
|
}
|
|
if (b->flags & ZEND_BB_ENTRY) {
|
|
fprintf(stderr, " entry");
|
|
}
|
|
if (b->flags & ZEND_BB_TRY) {
|
|
fprintf(stderr, " try");
|
|
}
|
|
if (b->flags & ZEND_BB_CATCH) {
|
|
fprintf(stderr, " catch");
|
|
}
|
|
if (b->flags & ZEND_BB_FINALLY) {
|
|
fprintf(stderr, " finally");
|
|
}
|
|
if (b->flags & ZEND_BB_FINALLY_END) {
|
|
fprintf(stderr, " finally_end");
|
|
}
|
|
if (b->flags & ZEND_BB_GEN_VAR) {
|
|
fprintf(stderr, " gen_var");
|
|
}
|
|
if (b->flags & ZEND_BB_KILL_VAR) {
|
|
fprintf(stderr, " kill_var");
|
|
}
|
|
if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) & !(b->flags & ZEND_BB_REACHABLE)) {
|
|
fprintf(stderr, " unreachable");
|
|
}
|
|
if (b->flags & ZEND_BB_LOOP_HEADER) {
|
|
fprintf(stderr, " loop_header");
|
|
}
|
|
if (b->flags & ZEND_BB_IRREDUCIBLE_LOOP) {
|
|
fprintf(stderr, " irreducible");
|
|
}
|
|
fprintf(stderr, " lines=[%d-%d]", b->start, b->end);
|
|
fprintf(stderr, "\n");
|
|
|
|
if (b->predecessors_count) {
|
|
int *p = cfg->predecessors + b->predecessor_offset;
|
|
int *end = p + b->predecessors_count;
|
|
|
|
fprintf(stderr, " ; from=(BB%d", *p);
|
|
for (p++; p < end; p++) {
|
|
fprintf(stderr, ", BB%d", *p);
|
|
}
|
|
fprintf(stderr, ")\n");
|
|
}
|
|
|
|
if (b->successors[0] != -1) {
|
|
fprintf(stderr, " ; to=(BB%d", b->successors[0]);
|
|
printed = 1;
|
|
if (b->successors[1] != -1) {
|
|
fprintf(stderr, ", BB%d", b->successors[1]);
|
|
}
|
|
}
|
|
if (printed) {
|
|
fprintf(stderr, ")\n");
|
|
}
|
|
|
|
if (b->idom >= 0) {
|
|
fprintf(stderr, " ; idom=BB%d\n", b->idom);
|
|
}
|
|
if (b->level >= 0) {
|
|
fprintf(stderr, " ; level=%d\n", b->level);
|
|
}
|
|
if (b->loop_header >= 0) {
|
|
fprintf(stderr, " ; loop_header=%d\n", b->level);
|
|
}
|
|
if (b->children >= 0) {
|
|
int j = b->children;
|
|
fprintf(stderr, " ; children=(BB%d", j);
|
|
j = cfg->blocks[j].next_child;
|
|
while (j >= 0) {
|
|
fprintf(stderr, ", BB%d", j);
|
|
j = cfg->blocks[j].next_child;
|
|
}
|
|
fprintf(stderr, ")\n");
|
|
}
|
|
}
|
|
|
|
static void zend_dump_block_header(const zend_cfg *cfg, const zend_op_array *op_array, const zend_ssa *ssa, int n, uint32_t dump_flags)
|
|
{
|
|
zend_dump_block_info(cfg, n, dump_flags);
|
|
if (ssa && ssa->blocks && ssa->blocks[n].phis) {
|
|
zend_ssa_phi *p = ssa->blocks[n].phis;
|
|
|
|
do {
|
|
int j;
|
|
|
|
fprintf(stderr, " ");
|
|
zend_dump_ssa_var(op_array, ssa, p->ssa_var, 0, p->var, dump_flags);
|
|
if (p->pi < 0) {
|
|
fprintf(stderr, " = Phi(");
|
|
for (j = 0; j < cfg->blocks[n].predecessors_count; j++) {
|
|
if (j > 0) {
|
|
fprintf(stderr, ", ");
|
|
}
|
|
zend_dump_ssa_var(op_array, ssa, p->sources[j], 0, p->var, dump_flags);
|
|
}
|
|
fprintf(stderr, ")\n");
|
|
} else {
|
|
fprintf(stderr, " = Pi<BB%d>(", p->pi);
|
|
zend_dump_ssa_var(op_array, ssa, p->sources[0], 0, p->var, dump_flags);
|
|
fprintf(stderr, " &");
|
|
zend_dump_pi_constraint(op_array, ssa, &p->constraint, dump_flags);
|
|
fprintf(stderr, ")\n");
|
|
}
|
|
p = p->next;
|
|
} while (p);
|
|
}
|
|
}
|
|
|
|
static void zend_dump_op_array_name(const zend_op_array *op_array)
|
|
{
|
|
zend_func_info *func_info = NULL;
|
|
|
|
func_info = ZEND_FUNC_INFO(op_array);
|
|
if (op_array->function_name) {
|
|
if (op_array->scope && op_array->scope->name) {
|
|
fprintf(stderr, "%s::%s", op_array->scope->name->val, op_array->function_name->val);
|
|
} else {
|
|
fprintf(stderr, "%s", op_array->function_name->val);
|
|
}
|
|
} else {
|
|
fprintf(stderr, "%s", "$_main");
|
|
}
|
|
if (func_info && func_info->clone_num > 0) {
|
|
fprintf(stderr, "_@_clone_%d", func_info->clone_num);
|
|
}
|
|
}
|
|
|
|
void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data)
|
|
{
|
|
int i;
|
|
const zend_cfg *cfg = NULL;
|
|
const zend_ssa *ssa = NULL;
|
|
zend_func_info *func_info = NULL;
|
|
uint32_t func_flags = 0;
|
|
|
|
if (dump_flags & (ZEND_DUMP_CFG|ZEND_DUMP_SSA)) {
|
|
cfg = (const zend_cfg*)data;
|
|
if (!cfg->blocks) {
|
|
cfg = data = NULL;
|
|
}
|
|
}
|
|
if (dump_flags & ZEND_DUMP_SSA) {
|
|
ssa = (const zend_ssa*)data;
|
|
}
|
|
|
|
func_info = ZEND_FUNC_INFO(op_array);
|
|
if (func_info) {
|
|
func_flags = func_info->flags;
|
|
}
|
|
|
|
fprintf(stderr, "\n");
|
|
zend_dump_op_array_name(op_array);
|
|
fprintf(stderr, ": ; (lines=%d, args=%d",
|
|
op_array->last,
|
|
op_array->num_args);
|
|
if (func_info && func_info->num_args >= 0) {
|
|
fprintf(stderr, "/%d", func_info->num_args);
|
|
}
|
|
fprintf(stderr, ", vars=%d, tmps=%d", op_array->last_var, op_array->T);
|
|
if (ssa) {
|
|
fprintf(stderr, ", ssa_vars=%d", ssa->vars_count);
|
|
}
|
|
if (func_flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) {
|
|
fprintf(stderr, ", dynamic");
|
|
}
|
|
if (func_flags & ZEND_FUNC_RECURSIVE) {
|
|
fprintf(stderr, ", recursive");
|
|
if (func_flags & ZEND_FUNC_RECURSIVE_DIRECTLY) {
|
|
fprintf(stderr, " directly");
|
|
}
|
|
if (func_flags & ZEND_FUNC_RECURSIVE_INDIRECTLY) {
|
|
fprintf(stderr, " indirectly");
|
|
}
|
|
}
|
|
if (func_flags & ZEND_FUNC_IRREDUCIBLE) {
|
|
fprintf(stderr, ", irreducable");
|
|
}
|
|
if (func_flags & ZEND_FUNC_NO_LOOPS) {
|
|
fprintf(stderr, ", no_loops");
|
|
}
|
|
//TODO: this is useful only for JIT???
|
|
#if 0
|
|
if (info->flags & ZEND_JIT_FUNC_NO_IN_MEM_CVS) {
|
|
fprintf(stderr, ", no_in_mem_cvs");
|
|
}
|
|
if (info->flags & ZEND_JIT_FUNC_NO_USED_ARGS) {
|
|
fprintf(stderr, ", no_used_args");
|
|
}
|
|
if (info->flags & ZEND_JIT_FUNC_NO_SYMTAB) {
|
|
fprintf(stderr, ", no_symtab");
|
|
}
|
|
if (info->flags & ZEND_JIT_FUNC_NO_FRAME) {
|
|
fprintf(stderr, ", no_frame");
|
|
}
|
|
if (info->flags & ZEND_JIT_FUNC_INLINE) {
|
|
fprintf(stderr, ", inline");
|
|
}
|
|
#endif
|
|
if (func_info && func_info->return_value_used == 0) {
|
|
fprintf(stderr, ", no_return_value");
|
|
} else if (func_info && func_info->return_value_used == 1) {
|
|
fprintf(stderr, ", return_value");
|
|
}
|
|
fprintf(stderr, ")\n");
|
|
if (msg) {
|
|
fprintf(stderr, " ; (%s)\n", msg);
|
|
}
|
|
fprintf(stderr, " ; %s:%u-%u\n", op_array->filename->val, op_array->line_start, op_array->line_end);
|
|
|
|
if (func_info && func_info->num_args > 0) {
|
|
for (i = 0; i < MIN(op_array->num_args, func_info->num_args ); i++) {
|
|
fprintf(stderr, " ; arg %d ", i);
|
|
zend_dump_type_info(func_info->arg_info[i].info.type, func_info->arg_info[i].info.ce, func_info->arg_info[i].info.is_instanceof, dump_flags);
|
|
zend_dump_range(&func_info->arg_info[i].info.range);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
|
|
if (func_info) {
|
|
fprintf(stderr, " ; return ");
|
|
zend_dump_type_info(func_info->return_info.type, func_info->return_info.ce, func_info->return_info.is_instanceof, dump_flags);
|
|
zend_dump_range(&func_info->return_info.range);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
if (ssa && ssa->var_info) {
|
|
for (i = 0; i < op_array->last_var; i++) {
|
|
fprintf(stderr, " ; ");
|
|
zend_dump_ssa_var(op_array, ssa, i, IS_CV, i, dump_flags);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
|
|
if (cfg) {
|
|
int n;
|
|
zend_basic_block *b;
|
|
|
|
for (n = 0; n < cfg->blocks_count; n++) {
|
|
b = cfg->blocks + n;
|
|
if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) || (b->flags & ZEND_BB_REACHABLE)) {
|
|
const zend_op *opline;
|
|
const zend_op *end;
|
|
|
|
zend_dump_block_header(cfg, op_array, ssa, n, dump_flags);
|
|
if (!(b->flags & ZEND_BB_EMPTY)) {
|
|
opline = op_array->opcodes + b->start;
|
|
end = op_array->opcodes + b->end + 1;
|
|
while (opline < end) {
|
|
zend_dump_op(op_array, b, opline, dump_flags, data);
|
|
opline++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (op_array->last_live_range) {
|
|
fprintf(stderr, "LIVE RANGES:\n");
|
|
for (i = 0; i < op_array->last_live_range; i++) {
|
|
if (cfg->split_at_live_ranges) {
|
|
fprintf(stderr, " %u: BB%u - BB%u ",
|
|
EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
|
|
cfg->map[op_array->live_range[i].start],
|
|
cfg->map[op_array->live_range[i].end]);
|
|
} else {
|
|
fprintf(stderr, " %u: L%u - L%u ",
|
|
EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
|
|
op_array->live_range[i].start,
|
|
op_array->live_range[i].end);
|
|
}
|
|
switch (op_array->live_range[i].var & ZEND_LIVE_MASK) {
|
|
case ZEND_LIVE_TMPVAR:
|
|
fprintf(stderr, "(tmp/var)\n");
|
|
break;
|
|
case ZEND_LIVE_LOOP:
|
|
fprintf(stderr, "(loop)\n");
|
|
break;
|
|
case ZEND_LIVE_SILENCE:
|
|
fprintf(stderr, "(silence)\n");
|
|
break;
|
|
case ZEND_LIVE_ROPE:
|
|
fprintf(stderr, "(rope)\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (op_array->last_try_catch) {
|
|
fprintf(stderr, "EXCEPTION TABLE:\n");
|
|
for (i = 0; i < op_array->last_try_catch; i++) {
|
|
fprintf(stderr, " BB%u",
|
|
cfg->map[op_array->try_catch_array[i].try_op]);
|
|
if (op_array->try_catch_array[i].catch_op) {
|
|
fprintf(stderr, ", BB%u",
|
|
cfg->map[op_array->try_catch_array[i].catch_op]);
|
|
} else {
|
|
fprintf(stderr, ", -");
|
|
}
|
|
if (op_array->try_catch_array[i].finally_op) {
|
|
fprintf(stderr, ", BB%u",
|
|
cfg->map[op_array->try_catch_array[i].finally_op]);
|
|
} else {
|
|
fprintf(stderr, ", -");
|
|
}
|
|
if (op_array->try_catch_array[i].finally_end) {
|
|
fprintf(stderr, ", BB%u\n",
|
|
cfg->map[op_array->try_catch_array[i].finally_end]);
|
|
} else {
|
|
fprintf(stderr, ", -\n");
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
const zend_op *opline = op_array->opcodes;
|
|
const zend_op *end = opline + op_array->last;
|
|
|
|
while (opline < end) {
|
|
zend_dump_op(op_array, NULL, opline, dump_flags, data);
|
|
opline++;
|
|
}
|
|
if (op_array->last_live_range) {
|
|
fprintf(stderr, "LIVE RANGES:\n");
|
|
for (i = 0; i < op_array->last_live_range; i++) {
|
|
fprintf(stderr, " %u: L%u - L%u ",
|
|
EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
|
|
op_array->live_range[i].start,
|
|
op_array->live_range[i].end);
|
|
switch (op_array->live_range[i].var & ZEND_LIVE_MASK) {
|
|
case ZEND_LIVE_TMPVAR:
|
|
fprintf(stderr, "(tmp/var)\n");
|
|
break;
|
|
case ZEND_LIVE_LOOP:
|
|
fprintf(stderr, "(loop)\n");
|
|
break;
|
|
case ZEND_LIVE_SILENCE:
|
|
fprintf(stderr, "(silence)\n");
|
|
break;
|
|
case ZEND_LIVE_ROPE:
|
|
fprintf(stderr, "(rope)\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (op_array->last_try_catch) {
|
|
fprintf(stderr, "EXCEPTION TABLE:\n");
|
|
for (i = 0; i < op_array->last_try_catch; i++) {
|
|
fprintf(stderr, " L%u",
|
|
op_array->try_catch_array[i].try_op);
|
|
if (op_array->try_catch_array[i].catch_op) {
|
|
fprintf(stderr, ", L%u",
|
|
op_array->try_catch_array[i].catch_op);
|
|
} else {
|
|
fprintf(stderr, ", -");
|
|
}
|
|
if (op_array->try_catch_array[i].finally_op) {
|
|
fprintf(stderr, ", L%u",
|
|
op_array->try_catch_array[i].finally_op);
|
|
} else {
|
|
fprintf(stderr, ", -");
|
|
}
|
|
if (op_array->try_catch_array[i].finally_end) {
|
|
fprintf(stderr, ", L%u\n",
|
|
op_array->try_catch_array[i].finally_end);
|
|
} else {
|
|
fprintf(stderr, ", -\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg)
|
|
{
|
|
int j;
|
|
|
|
fprintf(stderr, "\nDOMINATORS-TREE for \"");
|
|
zend_dump_op_array_name(op_array);
|
|
fprintf(stderr, "\"\n");
|
|
for (j = 0; j < cfg->blocks_count; j++) {
|
|
zend_basic_block *b = cfg->blocks + j;
|
|
if (b->flags & ZEND_BB_REACHABLE) {
|
|
zend_dump_block_info(cfg, j, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void zend_dump_variables(const zend_op_array *op_array)
|
|
{
|
|
int j;
|
|
|
|
fprintf(stderr, "\nCV Variables for \"");
|
|
zend_dump_op_array_name(op_array);
|
|
fprintf(stderr, "\"\n");
|
|
for (j = 0; j < op_array->last_var; j++) {
|
|
fprintf(stderr, " ");
|
|
zend_dump_var(op_array, IS_CV, j);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
|
|
void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags)
|
|
{
|
|
int j;
|
|
|
|
if (ssa->vars) {
|
|
fprintf(stderr, "\nSSA Variable for \"");
|
|
zend_dump_op_array_name(op_array);
|
|
fprintf(stderr, "\"\n");
|
|
|
|
for (j = 0; j < ssa->vars_count; j++) {
|
|
fprintf(stderr, " ");
|
|
zend_dump_ssa_var(op_array, ssa, j, IS_CV, ssa->vars[j].var, dump_flags);
|
|
if (ssa->vars[j].scc >= 0) {
|
|
if (ssa->vars[j].scc_entry) {
|
|
fprintf(stderr, " *");
|
|
} else {
|
|
fprintf(stderr, " ");
|
|
}
|
|
fprintf(stderr, "SCC=%d", ssa->vars[j].scc);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void zend_dump_var_set(const zend_op_array *op_array, const char *name, zend_bitset set)
|
|
{
|
|
int first = 1;
|
|
uint32_t i;
|
|
|
|
fprintf(stderr, " ; %s = {", name);
|
|
for (i = 0; i < op_array->last_var + op_array->T; i++) {
|
|
if (zend_bitset_in(set, i)) {
|
|
if (first) {
|
|
first = 0;
|
|
} else {
|
|
fprintf(stderr, ", ");
|
|
}
|
|
zend_dump_var(op_array, IS_CV, i);
|
|
}
|
|
}
|
|
fprintf(stderr, "}\n");
|
|
}
|
|
|
|
void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg)
|
|
{
|
|
int j;
|
|
fprintf(stderr, "\nVariable Liveness for \"");
|
|
zend_dump_op_array_name(op_array);
|
|
fprintf(stderr, "\"\n");
|
|
|
|
for (j = 0; j < cfg->blocks_count; j++) {
|
|
fprintf(stderr, " BB%d:\n", j);
|
|
zend_dump_var_set(op_array, "gen", DFG_BITSET(dfg->gen, dfg->size, j));
|
|
zend_dump_var_set(op_array, "def", DFG_BITSET(dfg->def, dfg->size, j));
|
|
zend_dump_var_set(op_array, "use", DFG_BITSET(dfg->use, dfg->size, j));
|
|
zend_dump_var_set(op_array, "in ", DFG_BITSET(dfg->in, dfg->size, j));
|
|
zend_dump_var_set(op_array, "out", DFG_BITSET(dfg->out, dfg->size, j));
|
|
}
|
|
}
|
|
|
|
void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa)
|
|
{
|
|
int j;
|
|
zend_ssa_block *ssa_blocks = ssa->blocks;
|
|
int blocks_count = ssa->cfg.blocks_count;
|
|
|
|
fprintf(stderr, "\nSSA Phi() Placement for \"");
|
|
zend_dump_op_array_name(op_array);
|
|
fprintf(stderr, "\"\n");
|
|
for (j = 0; j < blocks_count; j++) {
|
|
if (ssa_blocks && ssa_blocks[j].phis) {
|
|
zend_ssa_phi *p = ssa_blocks[j].phis;
|
|
int first = 1;
|
|
|
|
fprintf(stderr, " BB%d:\n", j);
|
|
if (p->pi >= 0) {
|
|
fprintf(stderr, " ; pi={");
|
|
} else {
|
|
fprintf(stderr, " ; phi={");
|
|
}
|
|
do {
|
|
if (first) {
|
|
first = 0;
|
|
} else {
|
|
fprintf(stderr, ", ");
|
|
}
|
|
zend_dump_var(op_array, IS_CV, p->var);
|
|
p = p->next;
|
|
} while (p);
|
|
fprintf(stderr, "}\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* tab-width: 4
|
|
* c-basic-offset: 4
|
|
* indent-tabs-mode: t
|
|
* End:
|
|
*/
|