diff options
Diffstat (limited to 'ext/opcache/Optimizer/zend_dump.c')
| -rw-r--r-- | ext/opcache/Optimizer/zend_dump.c | 1185 |
1 files changed, 1185 insertions, 0 deletions
diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c new file mode 100644 index 0000000000..c8fb945067 --- /dev/null +++ b/ext/opcache/Optimizer/zend_dump.c @@ -0,0 +1,1185 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine, Bytecode Visualisation | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2015 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)"); + } +} + +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) +{ + 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 (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) { + if (first) first = 0; else fprintf(stderr, ", "); + fprintf(stderr, "false"); + } + 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) +{ + 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); +} + +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) +{ + 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); + if (ssa->var_info[ssa_var_num].has_range) { + zend_dump_range(&ssa->var_info[ssa_var_num].range); + } + } + } +} + +static void zend_dump_pi_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_pi_range *r) +{ + 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); + 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); + 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 || + opline->result_type == IS_VAR || + opline->result_type == 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)); + } else { + zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var)); + } + fprintf(stderr, " = "); + } else if (!(dump_flags & ZEND_DUMP_HIDE_UNUSED_VARS) && + (opline->result_type & IS_VAR) && + (opline->result_type & EXT_TYPE_UNUSED)) { + fprintf(stderr, "U%u = ", EX_VAR_TO_NUM(opline->result.var)); + } + } + + 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_STATIC: + fprintf(stderr, " (static)"); + 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 (ZEND_VM_OP1_JMP_ADDR == (flags & ZEND_VM_OP1_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 if (ZEND_VM_OP1_NUM == (flags & ZEND_VM_OP1_MASK)) { + fprintf(stderr, " %u", opline->op1.num); + } else if (ZEND_VM_OP1_TRY_CATCH == (flags & ZEND_VM_OP1_MASK)) { + fprintf(stderr, " try-catch(%u)", opline->op1.num); + } else if (ZEND_VM_OP1_LIVE_RANGE == (flags & ZEND_VM_OP1_MASK)) { + if (opline->extended_value & ZEND_FREE_ON_RETURN) { + fprintf(stderr, " live-range(%u)", opline->op1.num); + } + } else 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 || + opline->op1_type == IS_VAR || + opline->op1_type == 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)); + } 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)); + } + } + } else if (ZEND_VM_OP1_THIS == (flags & ZEND_VM_OP1_MASK)) { + fprintf(stderr, " THIS"); + } else if (ZEND_VM_OP1_NEXT == (flags & ZEND_VM_OP1_MASK)) { + fprintf(stderr, " NEXT"); + } else if (ZEND_VM_OP1_CLASS_FETCH == (flags & ZEND_VM_OP1_MASK)) { + zend_dump_class_fetch_type(opline->op1.num); + } else if (ZEND_VM_OP1_CONSTRUCTOR == (flags & ZEND_VM_OP1_MASK)) { + fprintf(stderr, " CONSTRUCTOR"); + } + if (ZEND_VM_OP2_JMP_ADDR == (flags & ZEND_VM_OP2_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 if (ZEND_VM_OP2_NUM == (flags & ZEND_VM_OP2_MASK)) { + fprintf(stderr, " %u", opline->op2.num); + } else if (ZEND_VM_OP2_TRY_CATCH == (flags & ZEND_VM_OP2_MASK)) { + fprintf(stderr, " try-catch(%u)", opline->op2.num); + } else if (ZEND_VM_OP2_LIVE_RANGE == (flags & ZEND_VM_OP2_MASK)) { + if (opline->extended_value & ZEND_FREE_ON_RETURN) { + fprintf(stderr, " live-range(%u)", opline->op2.num); + } + } else 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 || + opline->op2_type == IS_VAR || + opline->op2_type == 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)); + } 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)); + } + } + } else if (ZEND_VM_OP2_THIS == (flags & ZEND_VM_OP2_MASK)) { + fprintf(stderr, " THIS"); + } else if (ZEND_VM_OP2_NEXT == (flags & ZEND_VM_OP2_MASK)) { + fprintf(stderr, " NEXT"); + } else if (ZEND_VM_OP2_CLASS_FETCH == (flags & ZEND_VM_OP2_MASK)) { + zend_dump_class_fetch_type(opline->op2.num); + } else if (ZEND_VM_OP2_CONSTRUCTOR == (flags & ZEND_VM_OP2_MASK)) { + fprintf(stderr, " CONSTRUCTOR"); + } + 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 || + opline->result_type == IS_VAR || + opline->result_type == 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)); + } + } 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)); + } + } + } + } + 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); + 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); + } + fprintf(stderr, ")\n"); + } else { + fprintf(stderr, " = Pi("); + zend_dump_ssa_var(op_array, ssa, p->sources[0], 0, p->var); + fprintf(stderr, " &"); + zend_dump_pi_range(op_array, ssa, &p->constraint); + 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_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); + 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); + 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); + 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++) { + 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]); + 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) +{ + 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); + 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: + */ |
