} 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,
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);
}
}
}
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;
#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)
#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 */
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 */
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 {
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)++;
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)++;
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;
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;
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) \
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;
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;
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;
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;
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;
}
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;
}
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;
| 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)]
| 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)]
|->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)
|->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)
|->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)
}
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()
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()