Skip to content

Commit 420e5fa

Browse files
committed
Tailcall VM with indirect dispatching
1 parent 399cb4c commit 420e5fa

21 files changed

+19105
-3679
lines changed

‎Zend/bench.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ function total()
382382
}
383383

384384
$t0 = $t = start_test();
385+
/*
385386
simple();
386387
$t = end_test($t, "simple");
387388
simplecall();
@@ -397,6 +398,7 @@ function total()
397398
ackermann(7);
398399
$t = end_test($t, "ackermann(7)");
399400
ary(50000);
401+
*/
400402
$t = end_test($t, "ary(50000)");
401403
ary2(50000);
402404
$t = end_test($t, "ary2(50000)");
@@ -418,5 +420,7 @@ function total()
418420
$t = end_test($t, "sieve(30)");
419421
strcat(200000);
420422
$t = end_test($t, "strcat(200000)");
423+
/*
424+
*/
421425
total();
422426
?>

‎Zend/zend_compile.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "zend_types.h"
2525
#include "zend_map_ptr.h"
2626
#include "zend_alloc.h"
27+
#include "zend_vm_opcodes.h"
2728

2829
#include <stdarg.h>
2930
#include <stdint.h>
@@ -135,7 +136,7 @@ void zend_const_expr_to_zval(zval *result, zend_ast **ast_ptr, bool allow_dynami
135136
typedef int (*user_opcode_handler_t) (zend_execute_data *execute_data);
136137

137138
struct _zend_op {
138-
const void *handler;
139+
zend_vm_opcode_handler_t handler;
139140
znode_op op1;
140141
znode_op op2;
141142
znode_op result;

‎Zend/zend_execute.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,27 @@
6969
# define ZEND_VM_FP_GLOBAL_REG "x18"
7070
# define ZEND_VM_IP_GLOBAL_REG "x19"
7171
# endif
72+
#elif ZEND_VM_TAIL_CALL_DISPATCH
73+
/* We use the arg1 and arg2 registers of the preserve_none calling convention */
74+
# if defined(i386)
75+
# define ZEND_VM_FP_LOCAL_REG "%esi"
76+
# define ZEND_VM_IP_LOCAL_REG "%edi"
77+
# elif defined(__x86_64__)
78+
# define ZEND_VM_FP_LOCAL_REG "%r12"
79+
# define ZEND_VM_IP_LOCAL_REG "%r13"
80+
# elif defined(__powerpc64__)
81+
# define ZEND_VM_FP_LOCAL_REG "r14"
82+
# define ZEND_VM_IP_LOCAL_REG "r15"
83+
# elif defined(__powerpc64__)
84+
# define ZEND_VM_FP_LOCAL_REG "r14"
85+
# define ZEND_VM_IP_LOCAL_REG "r15"
86+
# elif defined(__aarch64__)
87+
# define ZEND_VM_FP_LOCAL_REG "x27"
88+
# define ZEND_VM_IP_LOCAL_REG "x28"
89+
#elif defined(__riscv) && __riscv_xlen == 64
90+
# define ZEND_VM_FP_LOCAL_REG "x18"
91+
# define ZEND_VM_IP_LOCAL_REG "x19"
92+
# endif
7293
#endif
7394

7495
#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))

‎Zend/zend_portability.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,18 @@ char *alloca();
313313
# define ZEND_FASTCALL
314314
#endif
315315

316+
#if __has_attribute(preserve_none) && !defined(__SANITIZE_ADDRESS__)
317+
# define HAVE_PRESERVE_NONE
318+
# define ZEND_PRESERVE_NONE __attribute__((preserve_none))
319+
#else
320+
# define ZEND_PRESERVE_NONE
321+
#endif
322+
323+
#if __has_attribute(musttail)
324+
# define HAVE_MUSTTAIL
325+
# define ZEND_MUSTTAIL __attribute__((musttail))
326+
#endif
327+
316328
#if (defined(__GNUC__) && __GNUC__ >= 3 && !defined(__INTEL_COMPILER) && !defined(__APPLE__) && !defined(__hpux) && !defined(_AIX) && !defined(__osf__)) || __has_attribute(noreturn)
317329
# define HAVE_NORETURN
318330
# define ZEND_NORETURN __attribute__((noreturn))

‎Zend/zend_vm_execute.h

Lines changed: 18265 additions & 3451 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Zend/zend_vm_execute.skl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@
1010
#ifdef _WIN64
1111
/* See save_xmm_x86_64_ms_masm.asm */
1212
void {%EXECUTOR_NAME%}_ex_real(zend_execute_data *ex)
13+
#elif ZEND_VM_TAIL_CALL_DISPATCH
14+
ZEND_API ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV {%EXECUTOR_NAME%}_ex_real(zend_execute_data *ex, zend_op *_op);
15+
ZEND_API void {%EXECUTOR_NAME%}_ex(zend_execute_data *ex)
16+
{
17+
{%EXECUTOR_NAME%}_ex_real(ex, NULL);
18+
}
19+
/* Signature must be compatible with zend_vm_opcode_handler_t (musttail) */
20+
ZEND_API ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV {%EXECUTOR_NAME%}_ex_real(zend_execute_data *ex, zend_op *_op)
1321
#else
1422
ZEND_API void {%EXECUTOR_NAME%}_ex(zend_execute_data *ex)
1523
#endif

‎Zend/zend_vm_gen.php

Lines changed: 221 additions & 67 deletions
Large diffs are not rendered by default.

‎Zend/zend_vm_opcodes.h

Lines changed: 32 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎ext/opcache/jit/ir/ir.h

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ typedef enum _ir_type {
315315
/* call ops */ \
316316
_(CALL, xN, src, def, def) /* CALL(src, func, args...) */ \
317317
_(TAILCALL, xN, src, def, def) /* CALL+RETURN */ \
318+
_(RET2, d1, def, ___, ___) /* Fetch second int ret val */ \
318319
\
319320
/* memory reference and load/store ops */ \
320321
_(ALLOCA, a2, src, def, ___) /* alloca(def) */ \
@@ -509,33 +510,34 @@ void ir_strtab_free(ir_strtab *strtab);
509510
/* IR Context Flags */
510511
#define IR_FUNCTION (1<<0) /* Generate a function. */
511512
#define IR_FASTCALL_FUNC (1<<1) /* Generate a function with fastcall calling convention, x86 32-bit only. */
512-
#define IR_VARARG_FUNC (1<<2)
513-
#define IR_BUILTIN_FUNC (1<<3)
514-
#define IR_STATIC (1<<4)
515-
#define IR_EXTERN (1<<5)
516-
#define IR_CONST (1<<6)
517-
518-
#define IR_INITIALIZED (1<<7) /* sym data flag: constant or an initialized variable */
519-
#define IR_CONST_STRING (1<<8) /* sym data flag: constant string */
520-
521-
#define IR_SKIP_PROLOGUE (1<<8) /* Don't generate function prologue. */
522-
#define IR_USE_FRAME_POINTER (1<<9)
523-
#define IR_PREALLOCATED_STACK (1<<10)
524-
#define IR_NO_STACK_COMBINE (1<<11)
525-
#define IR_START_BR_TARGET (1<<12)
526-
#define IR_ENTRY_BR_TARGET (1<<13)
527-
#define IR_GEN_ENDBR (1<<14)
528-
#define IR_MERGE_EMPTY_ENTRIES (1<<15)
529-
530-
#define IR_OPT_INLINE (1<<16)
531-
#define IR_OPT_FOLDING (1<<17)
532-
#define IR_OPT_CFG (1<<18) /* merge BBs, by remove END->BEGIN nodes during CFG construction */
533-
#define IR_OPT_MEM2SSA (1<<19)
534-
#define IR_OPT_CODEGEN (1<<20)
535-
#define IR_GEN_NATIVE (1<<21)
536-
#define IR_GEN_CODE (1<<22) /* C or LLVM */
537-
538-
#define IR_GEN_CACHE_DEMOTE (1<<23) /* Demote the generated code from closest CPU caches */
513+
#define IR_PRESERVE_NONE_FUNC (1<<2) /* Generate a function with preserve_none calling convention */
514+
#define IR_VARARG_FUNC (1<<3)
515+
#define IR_BUILTIN_FUNC (1<<4)
516+
#define IR_STATIC (1<<5)
517+
#define IR_EXTERN (1<<6)
518+
#define IR_CONST (1<<7)
519+
520+
#define IR_INITIALIZED (1<<8) /* sym data flag: constant or an initialized variable */
521+
#define IR_CONST_STRING (1<<9) /* sym data flag: constant string */
522+
523+
#define IR_SKIP_PROLOGUE (1<<9) /* Don't generate function prologue. */
524+
#define IR_USE_FRAME_POINTER (1<<10)
525+
#define IR_PREALLOCATED_STACK (1<<11)
526+
#define IR_NO_STACK_COMBINE (1<<12)
527+
#define IR_START_BR_TARGET (1<<13)
528+
#define IR_ENTRY_BR_TARGET (1<<14)
529+
#define IR_GEN_ENDBR (1<<15)
530+
#define IR_MERGE_EMPTY_ENTRIES (1<<16)
531+
532+
#define IR_OPT_INLINE (1<<17)
533+
#define IR_OPT_FOLDING (1<<18)
534+
#define IR_OPT_CFG (1<<19) /* merge BBs, by remove END->BEGIN nodes during CFG construction */
535+
#define IR_OPT_MEM2SSA (1<<20)
536+
#define IR_OPT_CODEGEN (1<<21)
537+
#define IR_GEN_NATIVE (1<<22)
538+
#define IR_GEN_CODE (1<<23) /* C or LLVM */
539+
540+
#define IR_GEN_CACHE_DEMOTE (1<<24) /* Demote the generated code from closest CPU caches */
539541

540542
/* debug related */
541543
#ifdef IR_DEBUG

‎ext/opcache/jit/ir/ir_builder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,9 @@ extern "C" {
517517
#define ir_CALL_6(type, func, a, b, c, d, e, f) _ir_CALL_6(_ir_CTX, type, func, a, b, c, d, e, f)
518518
#define ir_CALL_N(type, func, count, args) _ir_CALL_N(_ir_CTX, type, func, count, args)
519519

520+
//#define ir_RET2(type, _op1) ir_fold1(_ir_CTX, IR_OPT(IR_RET2, type), (_op1))
521+
//#define ir_RET2_A(_op1) ir_RET2(IR_ADDR, op1)
522+
520523
#define ir_TAILCALL(type, func) _ir_TAILCALL(_ir_CTX, type, func)
521524
#define ir_TAILCALL_1(type, func, a1) _ir_TAILCALL_1(_ir_CTX, type, func, a1)
522525
#define ir_TAILCALL_2(type, func, a1, a2) _ir_TAILCALL_2(_ir_CTX, type, func, a1, a2)

‎ext/opcache/jit/ir/ir_emit.c

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ static const int8_t _ir_fp_reg_params[IR_REG_FP_ARGS];
6767
#else
6868
static const int8_t *_ir_fp_reg_params;
6969
#endif
70+
#ifdef IR_HAVE_PRESERVE_NONE
71+
static const int8_t _ir_int_pn_reg_params[IR_REG_INT_PNARGS];
72+
#endif
7073

7174
static const ir_proto_t *ir_call_proto(const ir_ctx *ctx, ir_insn *insn)
7275
{
@@ -98,12 +101,14 @@ bool ir_is_fastcall(const ir_ctx *ctx, const ir_insn *insn)
98101
if (func->proto) {
99102
const ir_proto_t *proto = (const ir_proto_t *)ir_get_str(ctx, func->proto);
100103

104+
IR_CHECK_CALLING_CONV(proto->flags);
101105
return (proto->flags & IR_FASTCALL_FUNC) != 0;
102106
}
103107
}
104108
} else if (ctx->ir_base[insn->op2].op == IR_PROTO) {
105109
const ir_proto_t *proto = (const ir_proto_t *)ir_get_str(ctx, ctx->ir_base[insn->op2].op2);
106110

111+
IR_CHECK_CALLING_CONV(proto->flags);
107112
return (proto->flags & IR_FASTCALL_FUNC) != 0;
108113
}
109114
return 0;
@@ -117,6 +122,35 @@ bool ir_is_fastcall(const ir_ctx *ctx, const ir_insn *insn)
117122
}
118123
#endif
119124

125+
#ifdef IR_HAVE_PRESERVE_NONE
126+
bool ir_is_preserve_none(const ir_ctx *ctx, const ir_insn *insn)
127+
{
128+
if (IR_IS_CONST_REF(insn->op2)) {
129+
const ir_insn *func = &ctx->ir_base[insn->op2];
130+
131+
if (func->op == IR_FUNC || func->op == IR_FUNC_ADDR) {
132+
if (func->proto) {
133+
const ir_proto_t *proto = (const ir_proto_t *)ir_get_str(ctx, func->proto);
134+
135+
IR_CHECK_CALLING_CONV(proto->flags);
136+
return (proto->flags & IR_PRESERVE_NONE_FUNC) != 0;
137+
}
138+
}
139+
} else if (ctx->ir_base[insn->op2].op == IR_PROTO) {
140+
const ir_proto_t *proto = (const ir_proto_t *)ir_get_str(ctx, ctx->ir_base[insn->op2].op2);
141+
142+
IR_CHECK_CALLING_CONV(proto->flags);
143+
return (proto->flags & IR_PRESERVE_NONE_FUNC) != 0;
144+
}
145+
return 0;
146+
}
147+
#else
148+
bool ir_is_preserve_none(const ir_ctx *ctx, const ir_insn *insn)
149+
{
150+
return 0;
151+
}
152+
#endif
153+
120154
bool ir_is_vararg(const ir_ctx *ctx, ir_insn *insn)
121155
{
122156
const ir_proto_t *proto = ir_call_proto(ctx, insn);
@@ -152,6 +186,8 @@ static ir_reg ir_get_param_reg(const ir_ctx *ctx, ir_ref ref)
152186
const int8_t *int_reg_params = _ir_int_reg_params;
153187
const int8_t *fp_reg_params = _ir_fp_reg_params;
154188

189+
IR_CHECK_CALLING_CONV(ctx->flags);
190+
155191
#ifdef IR_HAVE_FASTCALL
156192
if (sizeof(void*) == 4 && (ctx->flags & IR_FASTCALL_FUNC)) {
157193
int_reg_params_count = IR_REG_INT_FCARGS;
@@ -160,6 +196,12 @@ static ir_reg ir_get_param_reg(const ir_ctx *ctx, ir_ref ref)
160196
fp_reg_params = _ir_fp_fc_reg_params;
161197
}
162198
#endif
199+
#ifdef IR_HAVE_PRESERVE_NONE
200+
if (ctx->flags & IR_PRESERVE_NONE_FUNC) {
201+
int_reg_params_count = IR_REG_INT_PNARGS;
202+
int_reg_params = _ir_int_pn_reg_params;
203+
}
204+
#endif
163205

164206
for (i = use_list->count, p = &ctx->use_edges[use_list->refs]; i > 0; p++, i--) {
165207
use = *p;
@@ -210,6 +252,8 @@ static int ir_get_args_regs(const ir_ctx *ctx, const ir_insn *insn, int8_t *regs
210252
const int8_t *int_reg_params = _ir_int_reg_params;
211253
const int8_t *fp_reg_params = _ir_fp_reg_params;
212254

255+
IR_CHECK_CALLING_CONV(ctx->flags);
256+
213257
#ifdef IR_HAVE_FASTCALL
214258
if (sizeof(void*) == 4 && ir_is_fastcall(ctx, insn)) {
215259
int_reg_params_count = IR_REG_INT_FCARGS;
@@ -218,6 +262,12 @@ static int ir_get_args_regs(const ir_ctx *ctx, const ir_insn *insn, int8_t *regs
218262
fp_reg_params = _ir_fp_fc_reg_params;
219263
}
220264
#endif
265+
#ifdef IR_HAVE_PRESERVE_NONE
266+
if (ctx->flags & IR_PRESERVE_NONE_FUNC) {
267+
int_reg_params_count = IR_REG_INT_PNARGS;
268+
int_reg_params = _ir_int_pn_reg_params;
269+
}
270+
#endif
221271

222272
n = insn->inputs_count;
223273
n = IR_MIN(n, IR_MAX_REG_ARGS + 2);
@@ -415,9 +465,9 @@ static int ir_const_label(ir_ctx *ctx, ir_ref ref)
415465
}
416466

417467
#if defined(IR_TARGET_X86) || defined(IR_TARGET_X64)
418-
# include "ir_emit_x86.h"
468+
# include <ir_emit_x86.h>
419469
#elif defined(IR_TARGET_AARCH64)
420-
# include "ir_emit_aarch64.h"
470+
# include <ir_emit_aarch64.h>
421471
#else
422472
# error "Unknown IR target"
423473
#endif

‎ext/opcache/jit/ir/ir_gcm.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,20 @@ static void ir_gcm_schedule_late(ir_ctx *ctx, ir_ref ref, uint32_t b)
558558
}
559559
}
560560
}
561+
562+
/* RET2 is a projection of CALL and must be scheduled into the same block */
563+
if (ctx->ir_base[ref].op == IR_CALL) {
564+
ir_use_list *use_list = &ctx->use_lists[ref];
565+
ir_ref n, *p, use;
566+
567+
for (n = use_list->count, p = &ctx->use_edges[use_list->refs]; n < 0; p++, n--) {
568+
use = *p;
569+
if (ctx->ir_base[use].op == IR_RET2) {
570+
ctx->cfg_map[use] = b;
571+
break;
572+
}
573+
}
574+
}
561575
}
562576
}
563577

‎ext/opcache/jit/ir/ir_private.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,8 +1420,16 @@ void ir_fix_stack_frame(ir_ctx *ctx);
14201420
/* Utility */
14211421
ir_type ir_get_return_type(ir_ctx *ctx);
14221422
bool ir_is_fastcall(const ir_ctx *ctx, const ir_insn *insn);
1423+
bool ir_is_preserve_none(const ir_ctx *ctx, const ir_insn *insn);
14231424
bool ir_is_vararg(const ir_ctx *ctx, ir_insn *insn);
14241425

1426+
#define IR_CHECK_CALLING_CONV(flags) do { \
1427+
uint32_t __conv = (flags) & (IR_FASTCALL_FUNC|IR_PRESERVE_NONE_FUNC); \
1428+
(void)__conv; \
1429+
IR_ASSERT(__conv == 0 || __conv == IR_FASTCALL_FUNC \
1430+
|| __conv == IR_PRESERVE_NONE_FUNC); \
1431+
} while (0)
1432+
14251433
//#define IR_BITSET_LIVENESS
14261434

14271435
#endif /* IR_PRIVATE_H */

0 commit comments

Comments
 (0)