diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index e2e85f3431904..e004dcb0c2fe5 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -23,6 +23,7 @@ #include "Zend/zend_exceptions.h" #include "Zend/zend_constants.h" #include "Zend/zend_ini.h" +#include "Zend/zend_attributes.h" #include "zend_smart_str.h" #include "jit/zend_jit.h" @@ -3384,21 +3385,18 @@ static int zend_jit_setup_hot_counters(zend_op_array *op_array) return SUCCESS; } -static int zend_needs_manual_jit(const zend_op_array *op_array) +static int zend_jit_disabled(const zend_op_array *op_array) { - if (op_array->doc_comment) { - const char *s = ZSTR_VAL(op_array->doc_comment); - const char *p = strstr(s, "@jit"); + zend_attribute *jit = zend_get_attribute_str(op_array->attributes, "jit", sizeof("jit")-1); - if (p) { - size_t l = ZSTR_LEN(op_array->doc_comment); + if (jit == NULL || jit->argc == 0) { + return 0; + } - if ((p == s + 3 || *(p-1) <= ' ') && - (p + 6 == s + l || *(p+4) <= ' ')) { - return 1; - } - } + if (zend_string_equals_literal_ci(Z_STR(jit->args[0].value), "off")) { + return 1; } + return 0; } @@ -3406,6 +3404,10 @@ static int zend_needs_manual_jit(const zend_op_array *op_array) ZEND_EXT_API int zend_jit_op_array(zend_op_array *op_array, zend_script *script) { + if (zend_jit_disabled(op_array)) { + return SUCCESS; + } + if (dasm_ptr == NULL) { return FAILURE; } @@ -3455,12 +3457,6 @@ ZEND_EXT_API int zend_jit_op_array(zend_op_array *op_array, zend_script *script) return zend_jit_setup_hot_trace_counters(op_array); } else if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD) { return zend_real_jit_func(op_array, script, NULL); - } else if (JIT_G(trigger) == ZEND_JIT_ON_DOC_COMMENT) { - if (zend_needs_manual_jit(op_array)) { - return zend_real_jit_func(op_array, script, NULL); - } else { - return SUCCESS; - } } else { ZEND_UNREACHABLE(); } @@ -3495,21 +3491,19 @@ ZEND_EXT_API int zend_jit_script(zend_script *script) goto jit_failure; } } - } else if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD || - JIT_G(trigger) == ZEND_JIT_ON_DOC_COMMENT) { + } else if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD) { + int do_jit = 1; - if (JIT_G(trigger) == ZEND_JIT_ON_DOC_COMMENT) { - int do_jit = 0; - for (i = 0; i < call_graph.op_arrays_count; i++) { - if (zend_needs_manual_jit(call_graph.op_arrays[i])) { - do_jit = 1; - break; - } - } - if (!do_jit) { - goto jit_failure; + for (i = 0; i < call_graph.op_arrays_count; i++) { + if (zend_jit_disabled(call_graph.op_arrays[i])) { + do_jit = 0; + break; } } + if (!do_jit) { + goto jit_failure; + } + for (i = 0; i < call_graph.op_arrays_count; i++) { info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); if (info) { @@ -3531,10 +3525,6 @@ ZEND_EXT_API int zend_jit_script(zend_script *script) } for (i = 0; i < call_graph.op_arrays_count; i++) { - if (JIT_G(trigger) == ZEND_JIT_ON_DOC_COMMENT && - !zend_needs_manual_jit(call_graph.op_arrays[i])) { - continue; - } info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); if (info) { if (zend_jit_op_array_analyze2(call_graph.op_arrays[i], script, &info->ssa, ZCG(accel_directives).optimization_level) != SUCCESS) { @@ -3546,10 +3536,6 @@ ZEND_EXT_API int zend_jit_script(zend_script *script) if (JIT_G(debug) & ZEND_JIT_DEBUG_SSA) { for (i = 0; i < call_graph.op_arrays_count; i++) { - if (JIT_G(trigger) == ZEND_JIT_ON_DOC_COMMENT && - !zend_needs_manual_jit(call_graph.op_arrays[i])) { - continue; - } info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); if (info) { zend_dump_op_array(call_graph.op_arrays[i], ZEND_DUMP_HIDE_UNREACHABLE|ZEND_DUMP_RC_INFERENCE|ZEND_DUMP_SSA, "JIT", &info->ssa); @@ -3558,10 +3544,6 @@ ZEND_EXT_API int zend_jit_script(zend_script *script) } for (i = 0; i < call_graph.op_arrays_count; i++) { - if (JIT_G(trigger) == ZEND_JIT_ON_DOC_COMMENT && - !zend_needs_manual_jit(call_graph.op_arrays[i])) { - continue; - } info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); if (info) { if (zend_jit(call_graph.op_arrays[i], &info->ssa, NULL) != SUCCESS) { @@ -3602,8 +3584,7 @@ ZEND_EXT_API int zend_jit_script(zend_script *script) return SUCCESS; jit_failure: - if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD || - JIT_G(trigger) == ZEND_JIT_ON_DOC_COMMENT) { + if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD) { for (i = 0; i < call_graph.op_arrays_count; i++) { ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL); } @@ -3716,6 +3697,7 @@ static int zend_jit_parse_config_num(zend_long jit) jit /= 10; if (jit % 10 > 5) return FAILURE; + if (jit % 10 == 4) return FAILURE; JIT_G(trigger) = jit % 10; jit /= 10; diff --git a/ext/opcache/jit/zend_jit.h b/ext/opcache/jit/zend_jit.h index 7ffc80ef36f30..7e2ceb8fe63f7 100644 --- a/ext/opcache/jit/zend_jit.h +++ b/ext/opcache/jit/zend_jit.h @@ -28,9 +28,9 @@ #define ZEND_JIT_ON_SCRIPT_LOAD 0 #define ZEND_JIT_ON_FIRST_EXEC 1 -#define ZEND_JIT_ON_PROF_REQUEST 2 /* compile the most frequently caled on first request functions */ +#define ZEND_JIT_ON_PROF_REQUEST 2 /* compile the most frequently called on first request functions */ #define ZEND_JIT_ON_HOT_COUNTERS 3 /* compile functions after N calls or loop iterations */ -#define ZEND_JIT_ON_DOC_COMMENT 4 /* compile functions with "@jit" tag in doc-comments */ +#define ZEND_JIT_ON_ATTRIBUTE 4 /* compile functions with @@Jit attribute, unused at the moment */ #define ZEND_JIT_ON_HOT_TRACE 5 /* trace functions after N calls or loop iterations */ #define ZEND_JIT_REG_ALLOC_LOCAL (1<<0) /* local linear scan register allocation */ @@ -176,4 +176,6 @@ struct _zend_lifetime_interval { zend_lifetime_interval *list_next; }; +zend_class_entry *zend_ce_opcache_jit_attribute; + #endif /* HAVE_JIT_H */ diff --git a/ext/opcache/tests/jit/attributes_001.phpt b/ext/opcache/tests/jit/attributes_001.phpt new file mode 100644 index 0000000000000..20c60a31fc9b9 --- /dev/null +++ b/ext/opcache/tests/jit/attributes_001.phpt @@ -0,0 +1,22 @@ +--TEST-- +JIT disabled with attributes, assert no jit debug output +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +opcache.jit=1205 +opcache.jit_debug=1 +opcache.protect_memory=1 +--SKIPIF-- + +--FILE-- + +okey +--EXPECT-- +okey diff --git a/ext/opcache/tests/jit/attributes_002.phpt b/ext/opcache/tests/jit/attributes_002.phpt new file mode 100644 index 0000000000000..059f1577856aa --- /dev/null +++ b/ext/opcache/tests/jit/attributes_002.phpt @@ -0,0 +1,21 @@ +--TEST-- +JIT trigger with attributes +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +opcache.jit=1205 +opcache.jit_debug=1 +opcache.protect_memory=1 +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: @@Jit argument $mode only supports the value 'off' %s diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 4258ddd6864d2..f1372fcfa2bd7 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -31,6 +31,7 @@ #include "zend_virtual_cwd.h" #include "ext/standard/info.h" #include "ext/standard/php_filestat.h" +#include "Zend/zend_attributes.h" #include "opcache_arginfo.h" #if HAVE_JIT @@ -370,12 +371,55 @@ static ZEND_NAMED_FUNCTION(accel_is_readable) } } +#if HAVE_JIT +static void zend_jit_validate_opcache_attribute(zend_attribute *jit, uint32_t target, zend_class_entry *scope) +{ + if (jit->argc == 0) { + zend_error(E_COMPILE_ERROR, "Too few arguments to attribute @@Jit, %d passed and exactly 1 expected", jit->argc); + } + + if (jit->argc > 1) { + zend_error(E_COMPILE_ERROR, "Too many arguments to attribute @@Jit, %d passed and exactly 1 expected", jit->argc); + } + + if (jit->argc == 1) { + zend_string *trigger; + + if (Z_TYPE(jit->args[0].value) != IS_STRING) { + zend_error(E_COMPILE_ERROR, "@@Jit argument $mode must be a string"); + } + + trigger = Z_STR(jit->args[0].value); + + if (!zend_string_equals_literal_ci(trigger, "off")) { + zend_error(E_COMPILE_ERROR, "@@Jit argument $mode only supports the value 'off'"); + } + } +} +#endif + static ZEND_MINIT_FUNCTION(zend_accelerator) { (void)type; /* keep the compiler happy */ +#if HAVE_JIT + zend_class_entry ce; + zend_internal_attribute *attr; +#endif REGISTER_INI_ENTRIES(); +#if HAVE_JIT + INIT_CLASS_ENTRY(ce, "Jit", NULL); + zend_ce_opcache_jit_attribute = zend_register_internal_class(&ce); + zend_ce_opcache_jit_attribute->ce_flags |= ZEND_ACC_FINAL; + + attr = zend_internal_attribute_register( + zend_ce_opcache_jit_attribute, + ZEND_ATTRIBUTE_TARGET_FUNCTION | ZEND_ATTRIBUTE_TARGET_METHOD + ); + attr->validator = zend_jit_validate_opcache_attribute; +#endif + return SUCCESS; }