Skip to content

Generics experimentation (squashed) #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .gdbinit
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ define ____printzv_contents
set $handle = $zvalue->value.obj.handle
set $handlers = $zvalue->value.obj.handlers
set $zobj = $zvalue->value.obj
set $cname = $zobj->ce->name->val
set $cname = $zobj->cr->ce->name->val
printf "(%s) #%d", $cname, $handle
if ! $arg1
if $handlers->get_properties == &zend_std_get_properties
Expand All @@ -225,7 +225,7 @@ define ____printzv_contents
end
else
printf " {\n"
set $ht = &$zobj->ce->properties_info
set $ht = &$zobj->cr->ce->properties_info
set $k = 0
set $num = $ht->nNumUsed
while $k < $num
Expand Down Expand Up @@ -653,3 +653,8 @@ document lookup_root
lookup a refcounted in root
usage: lookup_root [ptr].
end

define print_opcode
set $opcode = $arg0
print zend_get_opcode_name($opcode)
end
73 changes: 72 additions & 1 deletion Zend/Optimizer/compact_literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@
#include "Optimizer/zend_optimizer.h"
#include "Optimizer/zend_optimizer_internal.h"
#include "zend_API.h"
#include "zend_compile.h"
#include "zend_constants.h"
#include "zend_execute.h"
#include "zend_string.h"
#include "zend_types.h"
#include "zend_vm.h"
#include "zend_extensions.h"

Expand All @@ -35,6 +38,9 @@
#define LITERAL_STATIC_METHOD 2
#define LITERAL_STATIC_PROPERTY 3

// TODO: temp hack
#define LITERAL_PNR_AND_KEY (1<<7)

typedef struct _literal_info {
uint8_t num_related;
} literal_info;
Expand Down Expand Up @@ -245,7 +251,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
break;
case ZEND_NEW:
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, 2);
LITERAL_INFO(opline->op1.constant, 2 | LITERAL_PNR_AND_KEY);
}
break;
case ZEND_DECLARE_CLASS:
Expand Down Expand Up @@ -312,6 +318,10 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
memset(map, 0, op_array->last_literal * sizeof(int));
for (i = 0; i < op_array->last_literal; i++) {
if (!info[i].num_related) {
// TODO: temp hack
if (Z_TYPE(op_array->literals[i]) == IS_PNR) {
zend_pnr_destroy(Z_PNR(op_array->literals[i]));
}
/* unset literal */
zval_ptr_dtor_nogc(&op_array->literals[i]);
continue;
Expand Down Expand Up @@ -443,6 +453,67 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
}
break;
}
case IS_PNR: {
ZEND_ASSERT(info[i].num_related & LITERAL_PNR_AND_KEY);
ZEND_ASSERT(Z_TYPE(op_array->literals[i+1]) == IS_STRING);

uint8_t num_related = info[i].num_related & ~LITERAL_PNR_AND_KEY;
ZEND_ASSERT(num_related == 2);

zend_packed_name_reference pnr = Z_PNR(op_array->literals[i]);
zend_string *base_key = Z_STR(op_array->literals[i+1]);
zend_string *key;
if (ZEND_PNR_IS_COMPLEX(pnr)) {
key = ZEND_PNR_COMPLEX_GET_KEY(pnr);
} else {
key = ZEND_PNR_SIMPLE_GET_NAME(pnr);
}
key = zend_string_concat2(
ZSTR_VAL(key), ZSTR_LEN(key)+1,
ZSTR_VAL(base_key), ZSTR_LEN(base_key));
bias_key(key, 300 + num_related - 1);

if ((pos = zend_hash_find(&hash, key)) != NULL) {
ZEND_ASSERT(Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_PNR &&
Z_TYPE(op_array->literals[Z_LVAL_P(pos)+1]) == IS_STRING &&
info[i].num_related == info[Z_LVAL_P(pos)].num_related);
zend_string_release_ex(key, 0);
map[i] = Z_LVAL_P(pos);
zend_packed_name_reference pnr2 = Z_PNR(op_array->literals[i]);
if (ZEND_PNR_IS_SIMPLE(pnr2)) {
zend_string_release(ZEND_PNR_SIMPLE_GET_NAME(pnr2));
} else {
zend_name_reference *class_ref = ZEND_PNR_COMPLEX_GET_REF(pnr2);
zend_string_release(class_ref->name);
zend_string_release(class_ref->key);
}
n = num_related;
while (n > 1) {
i++;
zval_ptr_dtor_nogc(&op_array->literals[i]);
n--;
}
} else {
map[i] = j;
ZVAL_LONG(&zv, j);
zend_hash_add_new(&hash, key, &zv);
zend_string_release_ex(key, 0);
if (i != j) {
op_array->literals[j] = op_array->literals[i];
info[j] = info[i];
}
j++;
n = num_related;
while (n > 1) {
i++;
if (i != j) op_array->literals[j] = op_array->literals[i];
j++;
n--;
}
}

break;
}
case IS_ARRAY:
ZEND_ASSERT(info[i].num_related == 1);
if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
Expand Down
5 changes: 3 additions & 2 deletions Zend/Optimizer/dfa_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "zend_call_graph.h"
#include "zend_inference.h"
#include "zend_dump.h"
#include "zend_scc.h"

#ifndef ZEND_DEBUG_DFA
# define ZEND_DEBUG_DFA ZEND_DEBUG
Expand Down Expand Up @@ -278,8 +279,8 @@ static inline bool can_elide_list_type(
ZEND_ASSERT(!is_intersection);
return can_elide_list_type(script, op_array, use_info, *single_type);
}
if (ZEND_TYPE_HAS_NAME(*single_type)) {
zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(*single_type));
if (ZEND_TYPE_HAS_PNR(*single_type)) {
zend_string *lcname = zend_string_tolower(ZEND_TYPE_PNR_NAME(*single_type));
zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, lcname);
zend_string_release(lcname);
bool result = ce && safe_instanceof(use_info->ce, ce);
Expand Down
4 changes: 2 additions & 2 deletions Zend/Optimizer/escape_analysis.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i
/* These flags will always cause an exception */
ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS
| ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT;
if (ce && !ce->parent && !ce->create_object && !ce->constructor &&
if (ce && !ce->num_parents && !ce->create_object && !ce->constructor &&
!ce->destructor && !ce->__get && !ce->__set &&
!(ce->ce_flags & forbidden_flags) &&
(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
Expand Down Expand Up @@ -228,7 +228,7 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va
zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1(
script, op_array, opline);
if (ce && !ce->create_object && !ce->constructor &&
!ce->destructor && !ce->__get && !ce->__set && !ce->parent) {
!ce->destructor && !ce->__get && !ce->__set && !ce->num_parents) {
return 1;
}
break;
Expand Down
20 changes: 11 additions & 9 deletions Zend/Optimizer/ssa_integrity.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex
for (i = 0; i < ssa->vars_count; i++) {
zend_ssa_var *var = &ssa->vars[i];
int use;
uint32_t type = ssa->var_info[i].type;

if (var->definition < 0 && !var->definition_phi && i > op_array->last_var) {
if (var->use_chain >= 0) {
Expand Down Expand Up @@ -192,14 +191,17 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex
}
} FOREACH_PHI_USE_END();

if ((type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY)) && !(type & MAY_BE_ARRAY_OF_ANY)) {
FAIL("var " VARFMT " has array key type but not value type\n", VAR(i));
}
if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY))) {
FAIL("var " VARFMT " has array value type but not key type\n", VAR(i));
}
if ((type & MAY_BE_REF) && ssa->var_info[i].ce) {
FAIL("var " VARFMT " may be ref but has ce\n", VAR(i));
if (ssa->var_info) {
uint32_t type = ssa->var_info[i].type;
if ((type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY)) && !(type & MAY_BE_ARRAY_OF_ANY)) {
FAIL("var " VARFMT " has array key type but not value type\n", VAR(i));
}
if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY))) {
FAIL("var " VARFMT " has array value type but not key type\n", VAR(i));
}
if ((type & MAY_BE_REF) && ssa->var_info[i].ce) {
FAIL("var " VARFMT " may be ref but has ce\n", VAR(i));
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions Zend/Optimizer/zend_cfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ typedef struct _zend_cfg {
#define ZEND_CFG_RECV_ENTRY (1<<24)
#define ZEND_CALL_TREE (1<<23)
#define ZEND_SSA_USE_CV_RESULTS (1<<22)
/* Chain function call opcodes by adding fake defs and uses. E.g. in a sequence
* INIT_FCALL->SEND->SEND->DO_FCALL, each element defines an SSA var that is
* used by the next one. This can be used to ensure that a worklist algorithm
* would visit DO_FCALL if any of the previous op codes were visited. */
#define ZEND_SSA_CALL_CHAINS (1<<21)

#define CRT_CONSTANT_EX(op_array, opline, node) \
(((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) ? \
Expand Down
56 changes: 48 additions & 8 deletions Zend/Optimizer/zend_dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
#include "zend_func_info.h"
#include "zend_call_graph.h"
#include "zend_dump.h"
#include "zend_smart_str.h"
#include "ext/standard/php_string.h"
#include "zend_types.h"

void zend_dump_ht(HashTable *ht)
{
Expand Down Expand Up @@ -76,6 +78,39 @@ void zend_dump_const(const zval *zv)
case IS_ARRAY:
fprintf(stderr, " array(...)");
break;
case IS_PNR: {
zend_packed_name_reference pnr = Z_PNR_P(zv);
zend_string *name;
const zend_type_list *type_args;
ZEND_PNR_UNPACK(pnr, name, type_args);

smart_str str = {0};
smart_str_append(&str, name);

if (type_args != &zend_empty_type_list) {
smart_str_appendc(&str, '<');
zend_type *single_type;
ZEND_TYPE_LIST_FOREACH((zend_type_list*)type_args, single_type) {
if (single_type != type_args->types) {
smart_str_appendc(&str, ',');
}
zend_string *type_str = zend_type_to_string(*single_type, NULL);
smart_str_append(&str, type_str);
zend_string_release(type_str);
} ZEND_TYPE_LIST_FOREACH_END();
smart_str_appendc(&str, '>');
}

name = smart_str_extract(&str);
zend_string *escaped_string = php_addcslashes(name, "\0..\37\"\\\177..\377", 2);
zend_string_release(name);

fprintf(stderr, " pnr(\"%s\")", ZSTR_VAL(escaped_string));

zend_string_release(escaped_string);

break;
}
default:
fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv));
break;
Expand Down Expand Up @@ -373,7 +408,11 @@ ZEND_API void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *s
} else {
fprintf(stderr, "#?.");
}
zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : var_type), var_num);
if (var_num >= 0) {
zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : var_type), var_num);
} else {
fprintf(stderr, "?");
}

if (ssa_var_num >= 0 && ssa->vars) {
if (ssa->vars[ssa_var_num].no_val) {
Expand Down Expand Up @@ -446,13 +485,14 @@ ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block
uint32_t n = 0;

if (!ssa_op || ssa_op->result_use < 0) {
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
if (ssa_op && ssa_op->result_def >= 0) {
int ssa_var_num = ssa_op->result_def;
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
} else {
zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
}
if (ssa_op && ssa_op->result_def >= 0) {
int ssa_var_num = ssa_op->result_def;
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type,
(opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) ? EX_VAR_TO_NUM(opline->result.var) : -1,
dump_flags);
fprintf(stderr, " = ");
} else if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
fprintf(stderr, " = ");
}
}
Expand Down
Loading