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
176 changes: 169 additions & 7 deletions ext/opcache/jit/ir/ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted
{
char buf[128];

if (insn->op == IR_FUNC || insn->op == IR_SYM) {
if (insn->op == IR_FUNC || insn->op == IR_SYM || insn->op == IR_LABEL) {
fprintf(f, "%s", ir_get_str(ctx, insn->val.name));
return;
} else if (insn->op == IR_STR) {
Expand Down Expand Up @@ -290,6 +290,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted
#define ir_op_kind_prb IR_OPND_PROB
#define ir_op_kind_opt IR_OPND_PROB
#define ir_op_kind_pro IR_OPND_PROTO
#define ir_op_kind_lbl IR_OPND_LABEL_REF

#define _IR_OP_FLAGS(name, flags, op1, op2, op3) \
IR_OP_FLAGS(ir_op_flag_ ## flags, ir_op_kind_ ## op1, ir_op_kind_ ## op2, ir_op_kind_ ## op3),
Expand Down Expand Up @@ -689,6 +690,13 @@ ir_ref ir_const_str(ir_ctx *ctx, ir_ref str)
return ir_const_ex(ctx, val, IR_ADDR, IR_OPTX(IR_STR, IR_ADDR, 0));
}

ir_ref ir_const_label(ir_ctx *ctx, ir_ref str)
{
ir_val val;
val.u64 = str;
return ir_const_ex(ctx, val, IR_ADDR, IR_OPTX(IR_LABEL, IR_ADDR, 0));
}

ir_ref ir_str(ir_ctx *ctx, const char *s)
{
size_t len;
Expand Down Expand Up @@ -879,6 +887,17 @@ static ir_ref _ir_fold_cse(ir_ctx *ctx, uint32_t opt, ir_ref op1, ir_ref op2, ir
return IR_UNUSED;
}

IR_ALWAYS_INLINE ir_ref _ir_fold_cast(ir_ctx *ctx, ir_ref ref, ir_type type)
{
if (ctx->ir_base[ref].type == type) {
return ref;
} else if (IR_IS_CONST_REF(ref) && !IR_IS_SYM_CONST(ctx->ir_base[ref].op)) {
return ir_const(ctx, ctx->ir_base[ref].val, type);
} else {
return ir_emit1(ctx, IR_OPT(IR_BITCAST, type), ref);
}
}

#define IR_FOLD(X) IR_FOLD1(X, __LINE__)
#define IR_FOLD1(X, Y) IR_FOLD2(X, Y)
#define IR_FOLD2(X, Y) case IR_RULE_ ## Y:
Expand Down Expand Up @@ -1158,7 +1177,7 @@ ir_ref ir_bind(ir_ctx *ctx, ir_ref var, ir_ref def)
IR_ASSERT(var < 0);
if (!ir_hashtab_add(ctx->binding, def, var)) {
/* Add a copy with different binding */
def = ir_emit2(ctx, IR_OPT(IR_COPY, ctx->ir_base[def].type), def, 1);
def = ir_emit2(ctx, IR_OPT(IR_COPY, ctx->ir_base[def].type), def, IR_COPY_HARD);
ir_hashtab_add(ctx->binding, def, var);
}
return def;
Expand Down Expand Up @@ -1836,8 +1855,49 @@ int ir_mem_flush(void *ptr, size_t size)
return 1;
}
#else

#if defined(__linux__) && defined(__x86_64__) && defined(PKEY_DISABLE_WRITE)
# define HAVE_PKEY_MPROTECT 1
#endif

#ifdef HAVE_PKEY_MPROTECT

#ifndef PKEY_DISABLE_EXECUTE
# define PKEY_DISABLE_EXECUTE 0
#endif

int pkey_mprotect(void* addr, size_t len, int prot, int pkey) __attribute__((weak));
int pkey_alloc(unsigned int, unsigned int) __attribute__((weak));
int pkey_free(int) __attribute__((weak));
int pkey_set(int, unsigned) __attribute__((weak));

static int ir_pkey = 0;
#endif

void *ir_mem_mmap(size_t size)
{
#ifdef HAVE_PKEY_MPROTECT
if (!ir_pkey && pkey_mprotect) {
int key = pkey_alloc(0, PKEY_DISABLE_WRITE);
if (key > 0) {
ir_pkey = key;
}
}
if (ir_pkey > 0) {
void *ret = mmap(NULL, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ret == MAP_FAILED) {
return NULL;
}
if (pkey_mprotect(ret, size, PROT_EXEC|PROT_READ|PROT_WRITE, ir_pkey) != 0) {
#ifdef IR_DEBUG
fprintf(stderr, "pkey_mprotect() failed\n");
#endif
munmap(ret, size);
return NULL;
}
return ret;
}
#endif
int prot_flags = PROT_EXEC;
#if defined(__NetBSD__)
prot_flags |= PROT_MPROTECT(PROT_READ|PROT_WRITE);
Expand All @@ -1852,11 +1912,28 @@ void *ir_mem_mmap(size_t size)
int ir_mem_unmap(void *ptr, size_t size)
{
munmap(ptr, size);
#ifdef HAVE_PKEY_MPROTECT
// if (ir_pkey > 0) {
// pkey_free(ir_pkey);
// ir_pkey = 0;
// }
#endif
return 1;
}

int ir_mem_protect(void *ptr, size_t size)
{
#ifdef HAVE_PKEY_MPROTECT
if (ir_pkey > 0) {
if (pkey_set(ir_pkey, PKEY_DISABLE_WRITE)) {
#ifdef IR_DEBUG
fprintf(stderr, "mprotect() failed\n");
#endif
return 0;
}
return 1;
}
#endif
if (mprotect(ptr, size, PROT_READ | PROT_EXEC) != 0) {
#ifdef IR_DEBUG
fprintf(stderr, "mprotect() failed\n");
Expand All @@ -1868,6 +1945,17 @@ int ir_mem_protect(void *ptr, size_t size)

int ir_mem_unprotect(void *ptr, size_t size)
{
#ifdef HAVE_PKEY_MPROTECT
if (ir_pkey > 0) {
if (pkey_set(ir_pkey, PKEY_DISABLE_EXECUTE)) {
#ifdef IR_DEBUG
fprintf(stderr, "mprotect() failed\n");
#endif
return 0;
}
return 1;
}
#endif
if (mprotect(ptr, size, PROT_READ | PROT_WRITE) != 0) {
#ifdef IR_DEBUG
fprintf(stderr, "mprotect() failed\n");
Expand Down Expand Up @@ -2070,7 +2158,26 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_load_i(ir_ctx *ctx, ir_ref ref, ir_type
}
} else if (insn->op == IR_RSTORE) {
modified_regset |= (1 << insn->op3);
} else if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN || insn->op == IR_CALL || insn->op == IR_VSTORE) {
} else if (insn->op == IR_CALL) {
ir_insn *func = &ctx->ir_base[insn->op2];
ir_ref func_proto;
const ir_proto_t *proto;

if (func->op == IR_FUNC || func->op == IR_FUNC_ADDR) {
func_proto = func->proto;
} else if (func->op == IR_PROTO) {
func_proto = func->op2;
} else {
break;
}
if (!func_proto) {
break;
}
proto = (const ir_proto_t *)ir_get_str(ctx, func_proto);
if (!(proto->flags & (IR_CONST_FUNC|IR_PURE_FUNC))) {
break;
}
} else if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN || insn->op == IR_VSTORE) {
return IR_UNUSED;
}
ref = insn->op1;
Expand Down Expand Up @@ -2116,7 +2223,26 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_vload_i(ir_ctx *ctx, ir_ref ref, ir_typ
break;
}
}
} else if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN || insn->op == IR_CALL || insn->op == IR_STORE) {
} else if (insn->op == IR_CALL) {
ir_insn *func = &ctx->ir_base[insn->op2];
ir_ref func_proto;
const ir_proto_t *proto;

if (func->op == IR_FUNC || func->op == IR_FUNC_ADDR) {
func_proto = func->proto;
} else if (func->op == IR_PROTO) {
func_proto = func->op2;
} else {
break;
}
if (!func_proto) {
break;
}
proto = (const ir_proto_t *)ir_get_str(ctx, func_proto);
if (!(proto->flags & (IR_CONST_FUNC|IR_PURE_FUNC))) {
break;
}
} else if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN || insn->op == IR_STORE) {
break;
}
ref = insn->op1;
Expand Down Expand Up @@ -3013,6 +3139,16 @@ void _ir_IJMP(ir_ctx *ctx, ir_ref addr)
ctx->control = IR_UNUSED;
}

ir_ref _ir_IGOTO(ir_ctx *ctx, ir_ref addr)
{
ir_ref ref;

IR_ASSERT(ctx->control);
ctx->control = ref = ir_emit2(ctx, IR_IGOTO, ctx->control, addr);
ctx->control = IR_UNUSED;
return ref;
}

ir_ref _ir_ADD_OFFSET(ir_ctx *ctx, ir_ref addr, uintptr_t offset)
{
if (offset) {
Expand Down Expand Up @@ -3135,6 +3271,18 @@ void _ir_VSTORE(ir_ctx *ctx, ir_ref var, ir_ref val)
ctx->control = ir_emit3(ctx, IR_VSTORE, ctx->control, var, val);
}

ir_ref _ir_VLOAD_v(ir_ctx *ctx, ir_type type, ir_ref var)
{
IR_ASSERT(ctx->control);
return ctx->control = ir_emit2(ctx, IR_OPT(IR_VLOAD_v, type), ctx->control, var);
}

void _ir_VSTORE_v(ir_ctx *ctx, ir_ref var, ir_ref val)
{
IR_ASSERT(ctx->control);
ctx->control = ir_emit3(ctx, IR_VSTORE_v, ctx->control, var, val);
}

ir_ref _ir_TLS(ir_ctx *ctx, ir_ref index, ir_ref offset)
{
IR_ASSERT(ctx->control);
Expand Down Expand Up @@ -3193,6 +3341,18 @@ void _ir_STORE(ir_ctx *ctx, ir_ref addr, ir_ref val)
ctx->control = ir_emit3(ctx, IR_STORE, ctx->control, addr, val);
}

ir_ref _ir_LOAD_v(ir_ctx *ctx, ir_type type, ir_ref addr)
{
IR_ASSERT(ctx->control);
return ctx->control = ir_emit2(ctx, IR_OPT(IR_LOAD_v, type), ctx->control, addr);
}

void _ir_STORE_v(ir_ctx *ctx, ir_ref addr, ir_ref val)
{
IR_ASSERT(ctx->control);
ctx->control = ir_emit3(ctx, IR_STORE_v, ctx->control, addr, val);
}

void _ir_VA_START(ir_ctx *ctx, ir_ref list)
{
IR_ASSERT(ctx->control);
Expand All @@ -3217,11 +3377,13 @@ ir_ref _ir_VA_ARG(ir_ctx *ctx, ir_type type, ir_ref list)
return ctx->control = ir_emit2(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list);
}

ir_ref _ir_VA_ARG_EX(ir_ctx *ctx, ir_type type, ir_ref list, size_t size)
ir_ref _ir_VA_ARG_EX(ir_ctx *ctx, ir_type type, ir_ref list, size_t size, size_t align)
{
IR_ASSERT(ctx->control);
IR_ASSERT(size <= 0x7fffffff);
return ctx->control = ir_emit3(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list, (ir_ref)size);
IR_ASSERT(size <= 0x0fffffff);
IR_ASSERT(align != 0 && ((align & (align - 1)) == 0) && align <= 128);
return ctx->control = ir_emit3(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list,
(ir_ref)IR_VA_ARG_OP3(size, align));
}

ir_ref _ir_BLOCK_BEGIN(ir_ctx *ctx)
Expand Down
24 changes: 21 additions & 3 deletions ext/opcache/jit/ir/ir.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ typedef enum _ir_type {
* prb - branch probability 1-99 (0 - unspecified): (IF_TRUE, IF_FALSE, CASE_VAL, CASE_DEFAULT)
* opt - optional number
* pro - function prototype
* lbl - label used as value (a reference to constant): (BEGIN)
*
* The order of IR opcodes is carefully selected for efficient folding.
* - foldable instruction go first
Expand Down Expand Up @@ -322,6 +323,7 @@ typedef enum _ir_type {
_(FUNC_ADDR, r0, ___, ___, ___) /* constant func ref */ \
_(FUNC, r0, ___, ___, ___) /* constant func ref */ \
_(SYM, r0, ___, ___, ___) /* constant symbol ref */ \
_(LABEL, r0, ___, ___, ___) /* label address ref */ \
_(STR, r0, ___, ___, ___) /* constant str ref */ \
\
/* call ops */ \
Expand All @@ -334,11 +336,15 @@ typedef enum _ir_type {
_(BLOCK_BEGIN, a1, src, ___, ___) /* stacksave */ \
_(BLOCK_END, a2, src, def, ___) /* stackrestore */ \
_(VLOAD, l2, src, var, ___) /* load value of local var */ \
_(VLOAD_v, l2, src, var, ___) /* volatile variant of VLOAD */ \
_(VSTORE, s3, src, var, def) /* store value to local var */ \
_(VSTORE_v, s3, src, var, def) /* volatile variant of VSTORE */ \
_(RLOAD, l1X2, src, num, opt) /* load value from register */ \
_(RSTORE, s2X1, src, def, num) /* store value into register */ \
_(LOAD, l2, src, ref, ___) /* load from memory */ \
_(LOAD_v, l2, src, ref, ___) /* volatile variant of VLOAD */ \
_(STORE, s3, src, ref, def) /* store to memory */ \
_(STORE_v, s3, src, ref, def) /* volatile variant of VSTORE */ \
_(TLS, l1X2, src, num, num) /* thread local variable */ \
_(TRAP, x1, src, ___, ___) /* DebugBreak */ \
/* memory reference ops (A, H, U, S, TMP, STR, NEW, X, V) ??? */ \
Expand All @@ -360,7 +366,7 @@ typedef enum _ir_type {
/* control-flow nodes */ \
_(START, S0X1, ret, ___, ___) /* function start */ \
_(ENTRY, S1X1, src, num, ___) /* entry with a fake src edge */ \
_(BEGIN, S1, src, ___, ___) /* block start */ \
_(BEGIN, S1X1, src, lbl, ___) /* block start, optional &&lbl */ \
_(IF_TRUE, S1X1, src, prb, ___) /* IF TRUE proj. */ \
_(IF_FALSE, S1X1, src, prb, ___) /* IF FALSE proj. */ \
_(CASE_VAL, S2X1, src, def, prb) /* switch proj. */ \
Expand All @@ -372,8 +378,9 @@ typedef enum _ir_type {
_(LOOP_END, E1, src, ___, ___) /* loop end */ \
_(IF, E2, src, def, ___) /* conditional control split */ \
_(SWITCH, E2, src, def, ___) /* multi-way control split */ \
_(IGOTO, E2, src, def, ___) /* computed goto (internal) */ \
_(IJMP, T2X1, src, def, ret) /* computed goto (terminating) */ \
_(RETURN, T2X1, src, def, ret) /* function return */ \
_(IJMP, T2X1, src, def, ret) /* computed goto */ \
_(UNREACHABLE, T1X2, src, ___, ret) /* unreachable (tailcall, etc) */ \
\
/* deoptimization helper */ \
Expand All @@ -400,6 +407,13 @@ typedef enum _ir_op {
#define IR_OPTX(op, type, n) ((uint32_t)(op) | ((uint32_t)(type) << IR_OPT_TYPE_SHIFT) | ((uint32_t)(n) << IR_OPT_INPUTS_SHIFT))
#define IR_OPT_TYPE(opt) (((opt) & IR_OPT_TYPE_MASK) >> IR_OPT_TYPE_SHIFT)

/* "opt" modifiers */
#define IR_COPY_HARD (1<<0)

#define IR_VA_ARG_SIZE(op3) (((uint32_t)(op3) >> 3))
#define IR_VA_ARG_ALIGN(op3) (1U << ((uint32_t)(op3) & 0x7))
#define IR_VA_ARG_OP3(s, a) (((s) << 3) | ir_ntzl(a))

/* IR References */
typedef int32_t ir_ref;

Expand Down Expand Up @@ -533,6 +547,9 @@ void ir_strtab_free(ir_strtab *strtab);
#define IR_EXTERN (1<<5)
#define IR_CONST (1<<6)

#define IR_CONST_FUNC (1<<6)
#define IR_PURE_FUNC (1<<7)

#define IR_INITIALIZED (1<<7) /* sym data flag: constant or an initialized variable */
#define IR_CONST_STRING (1<<8) /* sym data flag: constant string */

Expand Down Expand Up @@ -648,7 +665,6 @@ struct _ir_ctx {
ir_ref vars; /* list of VARs (used by register allocator) */
};
ir_snapshot_create_t snapshot_create;
int32_t stack_frame_alignment;
int32_t stack_frame_size; /* spill stack frame size (used by register allocator and code generator) */
int32_t call_stack_size; /* stack for parameter passing (used by register allocator and code generator) */
uint64_t used_preserved_regs;
Expand Down Expand Up @@ -698,6 +714,7 @@ ir_ref ir_const_func_addr(ir_ctx *ctx, uintptr_t c, ir_ref proto);
ir_ref ir_const_func(ir_ctx *ctx, ir_ref str, ir_ref proto);
ir_ref ir_const_sym(ir_ctx *ctx, ir_ref str);
ir_ref ir_const_str(ir_ctx *ctx, ir_ref str);
ir_ref ir_const_label(ir_ctx *ctx, ir_ref str);

ir_ref ir_unique_const_addr(ir_ctx *ctx, uintptr_t c);

Expand Down Expand Up @@ -893,6 +910,7 @@ struct _ir_loader {
void*(*resolve_sym_name) (ir_loader *loader, const char *name, uint32_t flags);
bool (*has_sym) (ir_loader *loader, const char *name);
bool (*add_sym) (ir_loader *loader, const char *name, void *addr);
bool (*add_label) (ir_loader *loader, const char *name, void *addr);
};

void ir_loader_init(void);
Expand Down
Loading