php-src/ext/opcache/Optimizer/zend_dump.c
Nikita Popov a5944f8dd5 Merge def and gen sets
For live-variable analysis it does not matter if def includes
variables that are previously use in the same block, the data flow
equations still have the same result. As such there is no need to
compute separate gen & def sets.

I'm keeping the name "def", because use of "gen" in this context is
pretty confusing (gen is usually the use set, not the def set).
2016-04-21 23:32:01 +02:00

1194 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, "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:
*/