Make JIT parameters configurable through opcache.jit_... options

This commit is contained in:
Dmitry Stogov 2020-05-19 13:35:02 +03:00
parent 33b5c026aa
commit 8c19e611aa
6 changed files with 92 additions and 58 deletions

View File

@ -369,7 +369,7 @@ static void *dasm_link_and_encode(dasm_State **dasm_state,
} else {
if (JIT_G(debug) & (ZEND_JIT_DEBUG_ASM_STUBS|ZEND_JIT_DEBUG_ASM)) {
zend_jit_disasm_add_symbol(name, (uintptr_t)entry, size);
if (trace_num || (JIT_G(debug) & ZEND_JIT_DEBUG_ASM_STUBS) != 0) {
if ((JIT_G(debug) & (trace_num ? ZEND_JIT_DEBUG_ASM : ZEND_JIT_DEBUG_ASM_STUBS)) != 0) {
zend_jit_disasm(
name,
(op_array && op_array->filename) ? ZSTR_VAL(op_array->filename) : NULL,
@ -3206,7 +3206,7 @@ void zend_jit_check_funcs(HashTable *function_table, zend_bool is_method) {
ZEND_COUNTER_INFO(op_array) = 0;
jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array);
opline->handler = jit_extension->orig_handler;
if (((double)counter / (double)zend_jit_profile_counter) > ZEND_JIT_PROF_THRESHOLD) {
if (((double)counter / (double)zend_jit_profile_counter) > JIT_G(prof_threshold)) {
zend_real_jit_func(op_array, NULL, NULL);
}
}
@ -3651,7 +3651,7 @@ ZEND_EXT_API int zend_jit_config(zend_string *jit, int stage)
}
failure:
zend_error(E_WARNING, "Invalid opcache.jit setting. Should be \"disable\", \"on\", \"off\" or 4-digit number");
zend_error(E_WARNING, "Invalid \"opcache.jit\" setting. Should be \"disable\", \"on\", \"off\" or 4-digit number");
JIT_G(enabled) = 0;
JIT_G(on) = 0;
return FAILURE;

View File

@ -37,28 +37,10 @@
#define ZEND_JIT_REG_ALLOC_GLOBAL (1<<1) /* global linear scan register allocation */
#define ZEND_JIT_CPU_AVX (1<<2) /* use AVX instructions, if available */
//#define ZEND_JIT_LEVEL(n) ((n) % 10)
//#define ZEND_JIT_TRIGGER(n) (((n) / 10) % 10)
//#define ZEND_JIT_REG_ALLOC(n) (((n) / 100) % 10)
//#define ZEND_JIT_CPU_FLAGS(n) (((n) / 1000) % 10)
#define ZEND_JIT_DEFAULT_OPTIONS "1205"
#define ZEND_JIT_DEFAULT_BUFFER_SIZE "0"
/* Makes profile based JIT (opcache.jit=2*) to generate code only for most
* often called functions (above the threshold).
* TODO: this setting should be configurable
*/
#define ZEND_JIT_PROF_THRESHOLD 0.005
/* Hot/Trace Counters based JIT parameters.
* TODO: this setting should be configurable
*/
#define ZEND_JIT_COUNTER_FUNC_COST 1
#define ZEND_JIT_COUNTER_RET_COST 15
#define ZEND_JIT_COUNTER_LOOP_COST 2
#define ZEND_JIT_COUNTER_INIT 127
#define ZEND_JIT_COUNTER_INIT 32531
#define ZEND_JIT_DEBUG_ASM (1<<0)
#define ZEND_JIT_DEBUG_SSA (1<<1)
@ -93,14 +75,7 @@
#define ZEND_JIT_TRACE_MAX_FUNCS 30 /* max number of different functions in a single trace */
#define ZEND_JIT_TRACE_MAX_CALL_DEPTH 10 /* max depth of inlined calls */
#define ZEND_JIT_TRACE_MAX_RET_DEPTH 4 /* max depth of inlined returns */
#define ZEND_JIT_TRACE_MAX_RECURSION 2 /* max number of recursive inlined calls */
#define ZEND_JIT_TRACE_MAX_UNROLL_LOOPS 8 /* max number of unrolled loops */
#define ZEND_JIT_TRACE_HOT_SIDE_COUNT 8 /* number of exits before taking side trace */
#define ZEND_JIT_TRACE_HOT_RETURN_COUNT 8 /* number of returns before taking continuation trace */
#define ZEND_JIT_TRACE_MAX_ROOT_FAILURES 16 /* number of attempts to record/compile a root trace */
#define ZEND_JIT_TRACE_MAX_SIDE_FAILURES 4 /* number of attempts to record/compile a side trace */
#define ZEND_JIT_TRACE_MAX_LOOPS_UNROLL 10 /* max number of unrolled loops */
#define ZEND_JIT_TRACE_BAD_ROOT_SLOTS 64 /* number of slots in bad root trace cache */
@ -119,6 +94,15 @@ typedef struct _zend_jit_globals {
zend_long buffer_size;
zend_long debug;
zend_long bisect_limit;
double prof_threshold;
zend_long hot_loop;
zend_long hot_func;
zend_long hot_return;
zend_long hot_side_exit; /* number of exits before taking side trace */
zend_long blacklist_root_trace; /* number of attempts to JIT a root trace before blacklist it */
zend_long blacklist_side_trace; /* number of attempts to JIT a side trace before blacklist it */
zend_long max_recursion_unroll; /* max number of recursive inlined calls/returns unrolls */
zend_long max_loops_unroll; /* max number of unrolled loops */
zend_sym_node *symbols; /* symbols for disassembler */

View File

@ -4342,7 +4342,7 @@ static zend_bool zend_jit_trace_is_bad_root(const zend_op *opline, zend_jit_trac
for (i = 0; i < ZEND_JIT_TRACE_BAD_ROOT_SLOTS; i++) {
if (cache_opline[i] == opline) {
if (cache_count[i] >= ZEND_JIT_TRACE_MAX_ROOT_FAILURES - 1) {
if (cache_count[i] >= JIT_G(blacklist_root_trace) - 1) {
cache_opline[i] = NULL;
return 1;
} else {
@ -4749,7 +4749,7 @@ static zend_bool zend_jit_trace_exit_is_bad(uint32_t trace_num, uint32_t exit_nu
uint8_t *counter = JIT_G(exit_counters) +
zend_jit_traces[trace_num].exit_counters + exit_num;
if (*counter + 1 >= ZEND_JIT_TRACE_HOT_SIDE_COUNT + ZEND_JIT_TRACE_MAX_SIDE_FAILURES) {
if (*counter + 1 >= JIT_G(hot_side_exit) + JIT_G(blacklist_side_trace)) {
return 1;
}
(*counter)++;
@ -4761,7 +4761,7 @@ static zend_bool zend_jit_trace_exit_is_hot(uint32_t trace_num, uint32_t exit_nu
uint8_t *counter = JIT_G(exit_counters) +
zend_jit_traces[trace_num].exit_counters + exit_num;
if (*counter + 1 >= ZEND_JIT_TRACE_HOT_SIDE_COUNT) {
if (*counter + 1 >= JIT_G(hot_side_exit)) {
return 1;
}
(*counter)++;

View File

@ -199,7 +199,7 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_func_counter_helper(ZEND_OPCODE_H
const zend_op *opline = EX(opline);
#endif
*(jit_extension->counter) -= ZEND_JIT_COUNTER_FUNC_COST;
*(jit_extension->counter) -= ((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1) / JIT_G(hot_func));
if (UNEXPECTED(*(jit_extension->counter) <= 0)) {
*(jit_extension->counter) = ZEND_JIT_COUNTER_INIT;
@ -219,7 +219,7 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_loop_counter_helper(ZEND_OPCODE_H
const zend_op *opline = EX(opline);
#endif
*(jit_extension->counter) -= ZEND_JIT_COUNTER_LOOP_COST;
*(jit_extension->counter) -= ((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop));
if (UNEXPECTED(*(jit_extension->counter) <= 0)) {
*(jit_extension->counter) = ZEND_JIT_COUNTER_INIT;
@ -313,17 +313,20 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_trace_c
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_func_trace_helper(ZEND_OPCODE_HANDLER_ARGS)
{
ZEND_OPCODE_TAIL_CALL_EX(zend_jit_trace_counter_helper, ZEND_JIT_COUNTER_FUNC_COST);
ZEND_OPCODE_TAIL_CALL_EX(zend_jit_trace_counter_helper,
((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1) / JIT_G(hot_func)));
}
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_ret_trace_helper(ZEND_OPCODE_HANDLER_ARGS)
{
ZEND_OPCODE_TAIL_CALL_EX(zend_jit_trace_counter_helper, ZEND_JIT_COUNTER_RET_COST);
ZEND_OPCODE_TAIL_CALL_EX(zend_jit_trace_counter_helper,
((ZEND_JIT_COUNTER_INIT + JIT_G(hot_return) - 1) / JIT_G(hot_return)));
}
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_loop_trace_helper(ZEND_OPCODE_HANDLER_ARGS)
{
ZEND_OPCODE_TAIL_CALL_EX(zend_jit_trace_counter_helper, ZEND_JIT_COUNTER_LOOP_COST);
ZEND_OPCODE_TAIL_CALL_EX(zend_jit_trace_counter_helper,
((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop)));
}
#define TRACE_RECORD(_op, _info, _ptr) \
@ -413,7 +416,7 @@ static int zend_jit_trace_bad_inner_loop(const zend_op *opline)
if (cache_opline[i] == opline) {
if ((cache_stop[i] == ZEND_JIT_TRACE_STOP_INNER_LOOP
|| cache_stop[i] == ZEND_JIT_TRACE_STOP_LOOP_EXIT)
&& cache_count[i] > ZEND_JIT_TRACE_MAX_ROOT_FAILURES / 2) {
&& cache_count[i] > JIT_G(blacklist_root_trace) / 2) {
return 1;
}
break;
@ -432,7 +435,7 @@ static int zend_jit_trace_bad_compiled_loop(const zend_op *opline)
for (i = 0; i < ZEND_JIT_TRACE_BAD_ROOT_SLOTS; i++) {
if (cache_opline[i] == opline) {
if (cache_stop[i] == ZEND_JIT_TRACE_STOP_COMPILED_LOOP
&& cache_count[i] >= ZEND_JIT_TRACE_MAX_ROOT_FAILURES - 1) {
&& cache_count[i] >= JIT_G(blacklist_root_trace) - 1) {
return 1;
}
break;
@ -451,7 +454,7 @@ static int zend_jit_trace_bad_loop_exit(const zend_op *opline)
for (i = 0; i < ZEND_JIT_TRACE_BAD_ROOT_SLOTS; i++) {
if (cache_opline[i] == opline) {
if (cache_stop[i] == ZEND_JIT_TRACE_STOP_LOOP_EXIT
&& cache_count[i] >= ZEND_JIT_TRACE_MAX_ROOT_FAILURES - 1) {
&& cache_count[i] >= JIT_G(blacklist_root_trace) - 1) {
return 1;
}
break;
@ -524,9 +527,6 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
int backtrack_ret_recursion_level = 0;
int loop_unroll_limit = 0;
const zend_op_array *unrolled_calls[ZEND_JIT_TRACE_MAX_CALL_DEPTH + ZEND_JIT_TRACE_MAX_RET_DEPTH];
#if ZEND_JIT_DETECT_UNROLLED_LOOPS
uint32_t unrolled_loops[ZEND_JIT_TRACE_MAX_UNROLL_LOOPS];
#endif
zend_bool is_toplevel;
#ifdef HAVE_GCC_GLOBAL_REGS
zend_execute_data *prev_execute_data = ex;
@ -693,12 +693,12 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
count = zend_jit_trace_recursive_call_count(&EX(func)->op_array, unrolled_calls, ret_level, level);
if (opline == orig_opline) {
if (count + 1 >= ZEND_JIT_TRACE_MAX_RECURSION) {
if (count + 1 >= JIT_G(max_recursion_unroll)) {
stop = ZEND_JIT_TRACE_STOP_RECURSIVE_CALL;
break;
}
backtrack_recursion = idx;
} else if (count >= ZEND_JIT_TRACE_MAX_RECURSION) {
} else if (count >= JIT_G(max_recursion_unroll)) {
stop = ZEND_JIT_TRACE_STOP_DEEP_RECURSION;
break;
}
@ -725,13 +725,13 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
TRACE_RECORD(ZEND_JIT_TRACE_BACK, 0, &EX(func)->op_array);
count = zend_jit_trace_recursive_ret_count(&EX(func)->op_array, unrolled_calls, ret_level);
if (opline == orig_opline) {
if (count + 1 >= ZEND_JIT_TRACE_MAX_RECURSION) {
if (count + 1 >= JIT_G(max_recursion_unroll)) {
stop = ZEND_JIT_TRACE_STOP_RECURSIVE_RET;
break;
}
backtrack_ret_recursion = idx;
backtrack_ret_recursion_level = ret_level;
} else if (count >= ZEND_JIT_TRACE_MAX_RECURSION) {
} else if (count >= JIT_G(max_recursion_unroll)) {
stop = ZEND_JIT_TRACE_STOP_DEEP_RECURSION;
break;
}
@ -828,7 +828,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
break;
}
}
if (loop_unroll_limit < ZEND_JIT_TRACE_MAX_UNROLL_LOOPS) {
if (loop_unroll_limit < JIT_G(max_loops_unroll)) {
loop_unroll_limit++;
} else {
stop = ZEND_JIT_TRACE_STOP_LOOP_UNROLL;

View File

@ -2209,7 +2209,7 @@ static int zend_jit_hybrid_func_hot_counter_stub(dasm_State **Dst)
| mov r0, EX->func
| mov r1, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
| mov r2, aword [r1 + offsetof(zend_jit_op_array_hot_extension, counter)]
| sub word [r2], ZEND_JIT_COUNTER_FUNC_COST
| sub word [r2], ((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1) / JIT_G(hot_func))
| jle >1
| GET_IP r2
| sub r2, aword [r0 + offsetof(zend_op_array, opcodes)]
@ -2246,7 +2246,7 @@ static int zend_jit_hybrid_loop_hot_counter_stub(dasm_State **Dst)
| mov r0, EX->func
| mov r1, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
| mov r2, aword [r1 + offsetof(zend_jit_op_array_hot_extension, counter)]
| sub word [r2], ZEND_JIT_COUNTER_LOOP_COST
| sub word [r2], ((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop))
| jle >1
| GET_IP r2
| sub r2, aword [r0 + offsetof(zend_op_array, opcodes)]
@ -2305,7 +2305,8 @@ static int zend_jit_hybrid_func_trace_counter_stub(dasm_State **Dst)
|->hybrid_func_trace_counter:
return zend_jit_hybrid_trace_counter_stub(Dst, ZEND_JIT_COUNTER_FUNC_COST);
return zend_jit_hybrid_trace_counter_stub(Dst,
((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1) / JIT_G(hot_func)));
}
static int zend_jit_hybrid_ret_trace_counter_stub(dasm_State **Dst)
@ -2316,7 +2317,8 @@ static int zend_jit_hybrid_ret_trace_counter_stub(dasm_State **Dst)
|->hybrid_ret_trace_counter:
return zend_jit_hybrid_trace_counter_stub(Dst, ZEND_JIT_COUNTER_RET_COST);
return zend_jit_hybrid_trace_counter_stub(Dst,
((ZEND_JIT_COUNTER_INIT + JIT_G(hot_return) - 1) / JIT_G(hot_return)));
}
static int zend_jit_hybrid_loop_trace_counter_stub(dasm_State **Dst)
@ -2327,7 +2329,8 @@ static int zend_jit_hybrid_loop_trace_counter_stub(dasm_State **Dst)
|->hybrid_loop_trace_counter:
return zend_jit_hybrid_trace_counter_stub(Dst, ZEND_JIT_COUNTER_LOOP_COST);
return zend_jit_hybrid_trace_counter_stub(Dst,
((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop)));
}
static int zend_jit_trace_halt_stub(dasm_State **Dst)

View File

@ -214,6 +214,44 @@ static ZEND_INI_MH(OnUpdateJitDebug)
}
return FAILURE;
}
static ZEND_INI_MH(OnUpdateCounter)
{
zend_long val = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
if (val > 0 && val < 256) {
zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
*p = val;
return SUCCESS;
}
zend_error(E_WARNING, "Invalid \"%s\" setting. Should be between 1 and 256", ZSTR_VAL(entry->name));
return FAILURE;
}
static ZEND_INI_MH(OnUpdateUnrollR)
{
zend_long val = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
if (val > 0 && val < ZEND_JIT_TRACE_MAX_CALL_DEPTH && val < ZEND_JIT_TRACE_MAX_RET_DEPTH) {
zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
*p = val;
return SUCCESS;
}
zend_error(E_WARNING, "Invalid \"%s\" setting. Should be between 1 and %d", ZSTR_VAL(entry->name),
MIN(ZEND_JIT_TRACE_MAX_CALL_DEPTH, ZEND_JIT_TRACE_MAX_CALL_DEPTH));
return FAILURE;
}
static ZEND_INI_MH(OnUpdateUnrollL)
{
zend_long val = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
if (val > 0 && val < ZEND_JIT_TRACE_MAX_LOOPS_UNROLL) {
zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
*p = val;
return SUCCESS;
}
zend_error(E_WARNING, "Invalid \"%s\" setting. Should be between 1 and %d", ZSTR_VAL(entry->name),
ZEND_JIT_TRACE_MAX_LOOPS_UNROLL);
return FAILURE;
}
#endif
ZEND_INI_BEGIN()
@ -273,10 +311,19 @@ ZEND_INI_BEGIN()
STD_PHP_INI_ENTRY("opcache.cache_id" , "" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.cache_id, zend_accel_globals, accel_globals)
#endif
#ifdef HAVE_JIT
STD_PHP_INI_ENTRY("opcache.jit" , ZEND_JIT_DEFAULT_OPTIONS, PHP_INI_ALL, OnUpdateJit, options, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_buffer_size" , ZEND_JIT_DEFAULT_BUFFER_SIZE, PHP_INI_SYSTEM, OnUpdateLong, buffer_size, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_debug" , "0", PHP_INI_ALL, OnUpdateJitDebug, debug, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_bisect_limit" , "0", PHP_INI_ALL, OnUpdateLong, bisect_limit, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit" , ZEND_JIT_DEFAULT_OPTIONS, PHP_INI_ALL, OnUpdateJit, options, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_buffer_size" , ZEND_JIT_DEFAULT_BUFFER_SIZE, PHP_INI_SYSTEM, OnUpdateLong, buffer_size, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_debug" , "0", PHP_INI_ALL, OnUpdateJitDebug, debug, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_bisect_limit" , "0", PHP_INI_ALL, OnUpdateLong, bisect_limit, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_prof_threshold" , "0.005", PHP_INI_ALL, OnUpdateReal, prof_threshold, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_hot_loop" , "64", PHP_INI_SYSTEM, OnUpdateCounter, hot_loop, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_hot_func" , "127", PHP_INI_SYSTEM, OnUpdateCounter, hot_func, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_hot_return" , "8", PHP_INI_SYSTEM, OnUpdateCounter, hot_return, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_hot_side_exit" , "8", PHP_INI_ALL, OnUpdateCounter, hot_side_exit, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_blacklist_root_trace" , "16", PHP_INI_ALL, OnUpdateCounter, blacklist_root_trace, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_blacklist_side_trace" , "8", PHP_INI_ALL, OnUpdateCounter, blacklist_side_trace, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_max_recursion_unroll" , "2", PHP_INI_ALL, OnUpdateUnrollR, max_recursion_unroll, zend_jit_globals, jit_globals)
STD_PHP_INI_ENTRY("opcache.jit_max_loops_unroll" , "8", PHP_INI_ALL, OnUpdateUnrollL, max_loops_unroll, zend_jit_globals, jit_globals)
#endif
ZEND_INI_END()