Skip to content
Merged
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
6 changes: 5 additions & 1 deletion ext/json/json_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
zend_ulong index;

ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) {
bool need_dtor = false;
zval tmp;
ZVAL_UNDEF(&tmp);

Expand All @@ -264,6 +265,7 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
if ((prop_info->flags & ZEND_ACC_VIRTUAL) && !prop_info->hooks[ZEND_PROPERTY_HOOK_GET]) {
continue;
}
need_dtor = true;
data = zend_read_property_ex(prop_info->ce, Z_OBJ_P(val), prop_info->name, /* silent */ true, &tmp);
if (EG(exception)) {
PHP_JSON_HASH_UNPROTECT_RECURSION(recursion_rc);
Expand Down Expand Up @@ -303,7 +305,9 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
zval_ptr_dtor(&tmp);
return FAILURE;
}
zval_ptr_dtor(&tmp);
if (UNEXPECTED(need_dtor)) {
zval_ptr_dtor(&tmp);
}

smart_str_appendc(buf, ',');
} ZEND_HASH_FOREACH_END();
Expand Down
1 change: 1 addition & 0 deletions ext/opcache/jit/ir/ir.h
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,7 @@ IR_ALWAYS_INLINE void *ir_jit_compile(ir_ctx *ctx, int opt_level, size_t *size)
#define IR_ERROR_UNSUPPORTED_CODE_RULE 3
#define IR_ERROR_LINK 4
#define IR_ERROR_ENCODE 5
#define IR_ERROR_TOO_LARGE 6

/* IR Memmory Allocation */
#ifndef ir_mem_malloc
Expand Down
39 changes: 37 additions & 2 deletions ext/opcache/jit/ir/ir_aarch64.dasc
Original file line number Diff line number Diff line change
Expand Up @@ -658,16 +658,35 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co
flags = IR_OP2_MUST_BE_IN_REG;
constraints->tmp_regs[0] = IR_TMP_REG(3, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF);
n = 1;
insn = &ctx->ir_base[ref];
if (IR_IS_CONST_REF(insn->op2)) {
constraints->tmp_regs[1] = IR_TMP_REG(2, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF);
n = 2;
}
break;
case IR_VA_ARG:
flags = IR_USE_MUST_BE_IN_REG | IR_OP2_MUST_BE_IN_REG;
constraints->tmp_regs[0] = IR_TMP_REG(3, IR_ADDR, IR_LOAD_SUB_REF, IR_SAVE_SUB_REF);
n = 1;
insn = &ctx->ir_base[ref];
if (IR_IS_CONST_REF(insn->op2)) {
constraints->tmp_regs[1] = IR_TMP_REG(2, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF);
n = 2;
}
break;
case IR_VA_COPY:
flags = IR_OP2_MUST_BE_IN_REG | IR_OP3_MUST_BE_IN_REG;
constraints->tmp_regs[0] = IR_TMP_REG(1, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF);
n = 1;
insn = &ctx->ir_base[ref];
if (IR_IS_CONST_REF(insn->op2)) {
constraints->tmp_regs[n] = IR_TMP_REG(2, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF);
n++;
}
if (IR_IS_CONST_REF(insn->op3)) {
constraints->tmp_regs[n] = IR_TMP_REG(3, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF);
n++;
}
break;
}
constraints->tmps_count = n;
Expand Down Expand Up @@ -6143,6 +6162,14 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr)
dasm_setup(&data.dasm_state, dasm_actions);
/* labels for each block + for each constant + rodata label + jmp_table label + for each entry + exit_table label */
dasm_growpc(&data.dasm_state, ctx->cfg_blocks_count + 1 + ctx->consts_count + 1 + 1 + 1 + ctx->entries_count + 1);
if (data.dasm_state->status != DASM_S_OK) {
IR_ASSERT(data.dasm_state->status == DASM_S_NOMEM);
dasm_free(&data.dasm_state);
ctx->data = NULL;
ctx->status = IR_ERROR_TOO_LARGE;
return NULL;
}

data.emit_constants = ir_bitset_malloc(ctx->consts_count);

if (!(ctx->flags & IR_SKIP_PROLOGUE)) {
Expand Down Expand Up @@ -6509,12 +6536,20 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr)
return NULL;
}

if (data.dasm_state->status != DASM_S_OK) {
IR_ASSERT(data.dasm_state->status == DASM_S_NOMEM);
dasm_free(&data.dasm_state);
ctx->data = NULL;
ctx->status = IR_ERROR_TOO_LARGE;
return NULL;
}

ret = dasm_link(&data.dasm_state, size_ptr);
if (ret != DASM_S_OK) {
IR_ASSERT(0);
IR_ASSERT(ret == DASM_S_NOMEM);
dasm_free(&data.dasm_state);
ctx->data = NULL;
ctx->status = IR_ERROR_LINK;
ctx->status = (ret == DASM_S_NOMEM) ? IR_ERROR_TOO_LARGE : IR_ERROR_LINK;
return NULL;
}
size = *size_ptr;
Expand Down
69 changes: 65 additions & 4 deletions ext/opcache/jit/ir/ir_check.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,41 @@ void ir_consistency_check(void)
IR_ASSERT(IR_ADD + 1 == IR_SUB);
}

static bool ir_check_use_list(const ir_ctx *ctx, ir_ref from, ir_ref to)
typedef struct {
ir_arena *arena;
ir_bitset *use_set;
ir_bitset *input_set;
} ir_check_ctx;

static bool ir_check_use_list(ir_check_ctx *check_ctx, const ir_ctx *ctx, ir_ref from, ir_ref to)
{
ir_ref n, *p;
ir_use_list *use_list = &ctx->use_lists[from];

n = use_list->count;
if (n > 16) {
/* Avoid quadratic complexity by maintaining a temporary bit-set */
ir_bitset set;

if (!check_ctx->use_set || !(set = check_ctx->use_set[from])) {
if (!check_ctx->arena) {
check_ctx->arena = ir_arena_create(sizeof(ir_arena) +
ctx->insns_count * sizeof(ir_bitset) +
ir_bitset_len(ctx->insns_count) * sizeof(ir_bitset_base_t));
}
if (!check_ctx->use_set) {
check_ctx->use_set = ir_arena_alloc(&check_ctx->arena, ctx->insns_count * sizeof(ir_bitset));
memset(check_ctx->use_set, 0, ctx->insns_count * sizeof(ir_bitset));
}
check_ctx->use_set[from] = set = (ir_bitset)ir_arena_alloc(&check_ctx->arena,
ir_bitset_len(ctx->insns_count) * sizeof(ir_bitset_base_t));
memset(set, 0, ir_bitset_len(ctx->insns_count) * sizeof(ir_bitset_base_t));
for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) {
ir_bitset_incl(set, *p);
}
}
return ir_bitset_in(set, to);
}
for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) {
if (*p == to) {
return 1;
Expand All @@ -54,12 +83,35 @@ static bool ir_check_use_list(const ir_ctx *ctx, ir_ref from, ir_ref to)
return 0;
}

static bool ir_check_input_list(const ir_ctx *ctx, ir_ref from, ir_ref to)
static bool ir_check_input_list(ir_check_ctx *check_ctx, const ir_ctx *ctx, ir_ref from, ir_ref to)
{
ir_insn *insn = &ctx->ir_base[to];
ir_ref n, j, *p;

n = ir_input_edges_count(ctx, insn);
if (n > 16) {
/* Avoid quadratic complexity by maintaining a temporary bit-set */
ir_bitset set;

if (!check_ctx->input_set || !(set = check_ctx->input_set[to])) {
if (!check_ctx->arena) {
check_ctx->arena = ir_arena_create(sizeof(ir_arena) +
ctx->insns_count * sizeof(ir_bitset) +
ir_bitset_len(ctx->insns_count) * sizeof(ir_bitset_base_t));
}
if (!check_ctx->input_set) {
check_ctx->input_set = ir_arena_alloc(&check_ctx->arena, ctx->insns_count * sizeof(ir_bitset));
memset(check_ctx->input_set, 0, ctx->insns_count * sizeof(ir_bitset));
}
check_ctx->input_set[to] = set = (ir_bitset)ir_arena_alloc(&check_ctx->arena,
ir_bitset_len(ctx->insns_count) * sizeof(ir_bitset_base_t));
memset(set, 0, ir_bitset_len(ctx->insns_count) * sizeof(ir_bitset_base_t));
for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
if (*p > 0) ir_bitset_incl(set, *p);
}
}
return ir_bitset_in(set, from);
}
for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
if (*p == from) {
return 1;
Expand Down Expand Up @@ -93,6 +145,11 @@ bool ir_check(const ir_ctx *ctx)
ir_type type;
uint32_t flags;
bool ok = 1;
ir_check_ctx check_ctx;

check_ctx.arena = NULL;
check_ctx.use_set = NULL;
check_ctx.input_set = NULL;

for (i = IR_UNUSED + 1, insn = ctx->ir_base + i; i < ctx->insns_count;) {
if (insn->op >= IR_LAST_OP) {
Expand Down Expand Up @@ -255,7 +312,7 @@ bool ir_check(const ir_ctx *ctx)
}
if (ctx->use_lists
&& use > 0
&& !ir_check_use_list(ctx, use, i)) {
&& !ir_check_use_list(&check_ctx, ctx, use, i)) {
fprintf(stderr, "ir_base[%d].ops[%d] is not in use list (%d)\n", i, j, use);
ok = 0;
}
Expand Down Expand Up @@ -313,7 +370,7 @@ bool ir_check(const ir_ctx *ctx)

for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) {
use = *p;
if (!ir_check_input_list(ctx, i, use)) {
if (!ir_check_input_list(&check_ctx, ctx, i, use)) {
fprintf(stderr, "ir_base[%d] is in use list of ir_base[%d]\n", use, i);
ok = 0;
}
Expand Down Expand Up @@ -393,6 +450,10 @@ bool ir_check(const ir_ctx *ctx)
insn += n;
}

if (check_ctx.arena) {
ir_arena_free(check_ctx.arena);
}

// if (!ok) {
// ir_dump_codegen(ctx, stderr);
// }
Expand Down
6 changes: 6 additions & 0 deletions ext/opcache/jit/ir/ir_emit.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,14 @@
do { \
size_t _sz = (sz), _need = (need); \
if (_sz < _need) { \
size_t _limit = sizeof(t) * DASM_SEC2POS(1); \
if (_need > _limit) { \
Dst_REF->status = DASM_S_NOMEM; \
return; \
} \
if (_sz < 16) _sz = 16; \
while (_sz < _need) _sz += _sz; \
if (_sz > _limit) _sz = _limit; \
(p) = (t *)ir_mem_realloc((p), _sz); \
(sz) = _sz; \
} \
Expand Down
59 changes: 54 additions & 5 deletions ext/opcache/jit/ir/ir_fold.h
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,8 @@ IR_FOLD(DIV(C_ADDR, C_ADDR))
IR_FOLD(DIV(C_I8, C_I8))
{
IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type);
if (op2_insn->val.i64 == 0) {
if (op2_insn->val.i64 == 0
|| (op2_insn->val.i64 == -1 && op1_insn->val.u8 == 0x80)) {
/* division by zero */
IR_FOLD_EMIT;
}
Expand All @@ -548,7 +549,8 @@ IR_FOLD(DIV(C_I8, C_I8))
IR_FOLD(DIV(C_I16, C_I16))
{
IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type);
if (op2_insn->val.i64 == 0) {
if (op2_insn->val.i64 == 0
|| (op2_insn->val.i64 == -1 && op1_insn->val.u16 == 0x8000)) {
/* division by zero */
IR_FOLD_EMIT;
}
Expand All @@ -558,7 +560,8 @@ IR_FOLD(DIV(C_I16, C_I16))
IR_FOLD(DIV(C_I32, C_I32))
{
IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type);
if (op2_insn->val.i64 == 0) {
if (op2_insn->val.i64 == 0
|| (op2_insn->val.i64 == -1 && op1_insn->val.u32 == 0x80000000)) {
/* division by zero */
IR_FOLD_EMIT;
}
Expand All @@ -568,7 +571,8 @@ IR_FOLD(DIV(C_I32, C_I32))
IR_FOLD(DIV(C_I64, C_I64))
{
IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type);
if (op2_insn->val.i64 == 0) {
if (op2_insn->val.i64 == 0
|| (op2_insn->val.i64 == -1 && op1_insn->val.u64 == 0x8000000000000000)) {
/* division by zero */
IR_FOLD_EMIT;
}
Expand Down Expand Up @@ -615,12 +619,27 @@ IR_FOLD(MOD(C_I64, C_I64))
}

IR_FOLD(NEG(C_I8))
{
IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type);
IR_FOLD_CONST_I((int8_t)(0 - op1_insn->val.u8));
}

IR_FOLD(NEG(C_I16))
{
IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type);
IR_FOLD_CONST_I((int16_t)(0 -op1_insn->val.u16));
}

IR_FOLD(NEG(C_I32))
{
IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type);
IR_FOLD_CONST_I((int32_t)(0 - op1_insn->val.u32));
}

IR_FOLD(NEG(C_I64))
{
IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type);
IR_FOLD_CONST_I(-op1_insn->val.u64);
IR_FOLD_CONST_I(0 - op1_insn->val.u64);
}

IR_FOLD(NEG(C_DOUBLE))
Expand Down Expand Up @@ -1841,6 +1860,12 @@ IR_FOLD(ADD(SUB, _))
if (IR_IS_TYPE_INT(IR_OPT_TYPE(opt))) {
if (op1_insn->op2 == op2) {
/* (a - b) + b => a */
if (ctx->ir_base[op1_insn->op1].type != IR_OPT_TYPE(opt)) {
opt = IR_BITCAST | (opt & IR_OPT_TYPE_MASK);
op1 = op1_insn->op1;
op2 = IR_UNUSED;
IR_FOLD_RESTART;
}
IR_FOLD_COPY(op1_insn->op1);
}
}
Expand All @@ -1852,6 +1877,12 @@ IR_FOLD(ADD(_, SUB))
if (IR_IS_TYPE_INT(IR_OPT_TYPE(opt))) {
if (op2_insn->op2 == op1) {
/* a + (b - a) => b */
if (ctx->ir_base[op2_insn->op1].type != IR_OPT_TYPE(opt)) {
opt = IR_BITCAST | (opt & IR_OPT_TYPE_MASK);
op1 = op2_insn->op1;
op2 = IR_UNUSED;
IR_FOLD_RESTART;
}
IR_FOLD_COPY(op2_insn->op1);
}
}
Expand All @@ -1863,9 +1894,21 @@ IR_FOLD(SUB(ADD, _))
if (IR_IS_TYPE_INT(IR_OPT_TYPE(opt))) {
if (op1_insn->op1 == op2) {
/* (a + b) - a => b */
if (ctx->ir_base[op1_insn->op2].type != IR_OPT_TYPE(opt)) {
opt = IR_BITCAST | (opt & IR_OPT_TYPE_MASK);
op1 = op1_insn->op2;
op2 = IR_UNUSED;
IR_FOLD_RESTART;
}
IR_FOLD_COPY(op1_insn->op2);
} else if (op1_insn->op2 == op2) {
/* (a + b) - a => b */
if (ctx->ir_base[op1_insn->op1].type != IR_OPT_TYPE(opt)) {
opt = IR_BITCAST | (opt & IR_OPT_TYPE_MASK);
op1 = op1_insn->op1;
op2 = IR_UNUSED;
IR_FOLD_RESTART;
}
IR_FOLD_COPY(op1_insn->op1);
}
}
Expand Down Expand Up @@ -1911,6 +1954,12 @@ IR_FOLD(SUB(_, SUB))
if (IR_IS_TYPE_INT(IR_OPT_TYPE(opt))) {
if (op2_insn->op1 == op1) {
/* a - (a - b) => b */
if (ctx->ir_base[op2_insn->op2].type != IR_OPT_TYPE(opt)) {
opt = IR_BITCAST | (opt & IR_OPT_TYPE_MASK);
op1 = op2_insn->op2;
op2 = IR_UNUSED;
IR_FOLD_RESTART;
}
IR_FOLD_COPY(op2_insn->op2);
}
}
Expand Down
2 changes: 2 additions & 0 deletions ext/opcache/jit/ir/ir_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ IR_ALWAYS_INLINE ir_arena* ir_arena_create(size_t size)

IR_ASSERT(size >= IR_ALIGNED_SIZE(sizeof(ir_arena), 8));
arena = (ir_arena*)ir_mem_malloc(size);
if (UNEXPECTED(!arena))return NULL;
arena->ptr = (char*) arena + IR_ALIGNED_SIZE(sizeof(ir_arena), 8);
arena->end = (char*) arena + size;
arena->prev = NULL;
Expand Down Expand Up @@ -267,6 +268,7 @@ IR_ALWAYS_INLINE void* ir_arena_alloc(ir_arena **arena_ptr, size_t size)
(size_t)(arena->end - (char*) arena);
ir_arena *new_arena = (ir_arena*)ir_mem_malloc(arena_size);

if (UNEXPECTED(!new_arena)) return NULL;
ptr = (char*) new_arena + IR_ALIGNED_SIZE(sizeof(ir_arena), 8);
new_arena->ptr = (char*) new_arena + IR_ALIGNED_SIZE(sizeof(ir_arena), 8) + size;
new_arena->end = (char*) new_arena + arena_size;
Expand Down
Loading