From 2f710f5bb26e95ade5095ed858e99fd07118029f Mon Sep 17 00:00:00 2001 From: codinghuang Date: Sun, 31 Jan 2021 18:51:13 +0800 Subject: [PATCH] Support custom passes in Optimizer --- Zend/Optimizer/zend_optimizer.c | 42 +++++++++++++++++++ Zend/Optimizer/zend_optimizer.h | 4 ++ ext/zend_test/test.c | 18 ++++++++ .../tests/optimizer_register_pass.phpt | 14 +++++++ 4 files changed, 78 insertions(+) create mode 100644 ext/zend_test/tests/optimizer_register_pass.phpt diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index ffce27b3c07..049aad3878a 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -31,6 +31,15 @@ #include "zend_inference.h" #include "zend_dump.h" +#ifndef ZEND_OPTIMIZER_MAX_REGISTERED_PASSES +# define ZEND_OPTIMIZER_MAX_REGISTERED_PASSES 32 +#endif + +struct { + zend_optimizer_pass_t pass[ZEND_OPTIMIZER_MAX_REGISTERED_PASSES]; + int last; +} zend_optimizer_registered_passes = {{NULL}, 0}; + static void zend_optimizer_zval_dtor_wrapper(zval *zvalue) { zval_ptr_dtor_nogc(zvalue); @@ -1402,6 +1411,16 @@ static void step_dump_after_optimizer(zend_op_array *op_array, void *context) { zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); } +static void zend_optimizer_call_registered_passes(zend_script *script, void *ctx) { + for (int i = 0; i < zend_optimizer_registered_passes.last; i++) { + if (!zend_optimizer_registered_passes.pass[i]) { + continue; + } + + zend_optimizer_registered_passes.pass[i](script, ctx); + } +} + ZEND_API int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level) { zend_class_entry *ce; @@ -1541,6 +1560,8 @@ ZEND_API int zend_optimize_script(zend_script *script, zend_long optimization_le } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); + zend_optimizer_call_registered_passes(script, &ctx); + if ((debug_level & ZEND_DUMP_AFTER_OPTIMIZER) && (ZEND_OPTIMIZER_PASS_7 & optimization_level)) { zend_foreach_op_array(script, step_dump_after_optimizer, NULL); @@ -1554,6 +1575,27 @@ ZEND_API int zend_optimize_script(zend_script *script, zend_long optimization_le return 1; } +ZEND_API int zend_optimizer_register_pass(zend_optimizer_pass_t pass) +{ + if (!pass) { + return -1; + } + + if (zend_optimizer_registered_passes.last == ZEND_OPTIMIZER_MAX_REGISTERED_PASSES) { + return -1; + } + + zend_optimizer_registered_passes.pass[ + zend_optimizer_registered_passes.last++] = pass; + + return zend_optimizer_registered_passes.last; +} + +ZEND_API void zend_optimizer_unregister_pass(int idx) +{ + zend_optimizer_registered_passes.pass[idx-1] = NULL; +} + int zend_optimizer_startup(void) { return zend_func_info_startup(); diff --git a/Zend/Optimizer/zend_optimizer.h b/Zend/Optimizer/zend_optimizer.h index 4b733d9fb89..08b7d12a7ff 100644 --- a/Zend/Optimizer/zend_optimizer.h +++ b/Zend/Optimizer/zend_optimizer.h @@ -89,8 +89,12 @@ typedef struct _zend_script { uint32_t first_early_binding_opline; /* the linked list of delayed declarations */ } zend_script; +typedef void (*zend_optimizer_pass_t)(zend_script *, void *context); + BEGIN_EXTERN_C() ZEND_API int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level); +ZEND_API int zend_optimizer_register_pass(zend_optimizer_pass_t pass); +ZEND_API void zend_optimizer_unregister_pass(int idx); int zend_optimizer_startup(void); int zend_optimizer_shutdown(void); END_EXTERN_C() diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 42507db11e9..af4fb0950df 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -28,6 +28,7 @@ #include "zend_observer.h" #include "zend_smart_str.h" #include "zend_fibers.h" +#include "Zend/Optimizer/zend_optimizer.h" ZEND_BEGIN_MODULE_GLOBALS(zend_test) int observer_enabled; @@ -43,6 +44,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test) int observer_nesting_depth; int observer_fiber_switch; int replace_zend_execute_ex; + int register_passes; ZEND_END_MODULE_GLOBALS(zend_test) ZEND_DECLARE_MODULE_GLOBALS(zend_test) @@ -86,6 +88,16 @@ static ZEND_FUNCTION(zend_test_void_return) ZEND_PARSE_PARAMETERS_NONE(); } +static void pass1(zend_script *script, void *context) +{ + php_printf("pass1\n"); +} + +static void pass2(zend_script *script, void *context) +{ + php_printf("pass2\n"); +} + static ZEND_FUNCTION(zend_test_deprecated) { zval *arg1; @@ -365,6 +377,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("zend_test.observer.show_opcode_in_user_handler", "", PHP_INI_SYSTEM, OnUpdateString, observer_show_opcode_in_user_handler, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.observer.fiber_switch", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_fiber_switch, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.replace_zend_execute_ex", "0", PHP_INI_SYSTEM, OnUpdateBool, replace_zend_execute_ex, zend_zend_test_globals, zend_test_globals) + STD_PHP_INI_BOOLEAN("zend_test.register_passes", "0", PHP_INI_SYSTEM, OnUpdateBool, register_passes, zend_zend_test_globals, zend_test_globals) PHP_INI_END() static zend_observer_fcall_handlers observer_fcall_init(zend_execute_data *execute_data); @@ -503,6 +516,11 @@ PHP_MINIT_FUNCTION(zend_test) zend_observer_fiber_switch_register(fiber_suspend_observer); } + if (ZT_G(register_passes)) { + zend_optimizer_register_pass(pass1); + zend_optimizer_register_pass(pass2); + } + return SUCCESS; } diff --git a/ext/zend_test/tests/optimizer_register_pass.phpt b/ext/zend_test/tests/optimizer_register_pass.phpt new file mode 100644 index 00000000000..3fd6c403e06 --- /dev/null +++ b/ext/zend_test/tests/optimizer_register_pass.phpt @@ -0,0 +1,14 @@ +--TEST-- +Optimizer: Pass Registration +--EXTENSIONS-- +opcache +--INI-- +opcache.enable_cli=1 +zend_test.register_passes=1 +--FILE-- + +--EXPECT-- +pass1 +pass2