diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 61c425fe93b4f..5846582201ae2 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -123,6 +123,7 @@ jobs: configurationParameters: >- --${{ matrix.debug && 'enable' || 'disable' }}-debug --${{ matrix.zts && 'enable' || 'disable' }}-zts + --enable-universal-ip ${{ matrix.asan && 'CFLAGS="-fsanitize=undefined,address -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address" CC=clang-16 CXX=clang++-16 --disable-opcache-jit' || '' }} skipSlow: ${{ matrix.asan }} - name: make @@ -249,7 +250,8 @@ jobs: --with-mysqli=mysqlnd \ --with-openssl \ --with-pdo-sqlite \ - --with-valgrind + --with-valgrind \ + --enable-universal-ip - name: make run: make -j$(/usr/bin/nproc) >/dev/null - name: make install diff --git a/Zend/Zend.m4 b/Zend/Zend.m4 index abb89835a4e16..1027b59b16839 100644 --- a/Zend/Zend.m4 +++ b/Zend/Zend.m4 @@ -385,11 +385,60 @@ int emu(const opcode_handler_t *ip, void *fp) { ZEND_GCC_GLOBAL_REGS=no ]) fi + if test "$ZEND_GCC_GLOBAL_REGS" = "yes"; then AC_DEFINE([HAVE_GCC_GLOBAL_REGS], 1, [Define if the target system has support for global register variables]) fi AC_MSG_RESULT($ZEND_GCC_GLOBAL_REGS) +AC_ARG_ENABLE([universal-ip], + [AS_HELP_STRING([--enable-universal-ip], + [whether to use a GCC global register variables for IP universally])], + [ZEND_UNIVERSAL_IP=$enableval], + [ZEND_UNIVERSAL_IP=no]) + +if test "$ZEND_UNIVERSAL_IP" = "yes"; then + if test "$ZEND_GCC_GLOBAL_REGS" != "yes"; then + AC_MSG_ERROR(May not enable universal IP without --enable-universal-ip) + fi + + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include + +#if defined(__GNUC__) +# define ZEND_GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) +#else +# define ZEND_GCC_VERSION 0 +#endif +#if defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(i386) +# define ZEND_VM_IP_GLOBAL_REG "edi" +#elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__x86_64__) +# define ZEND_VM_IP_GLOBAL_REG "r15" +#elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__powerpc64__) +# define ZEND_VM_IP_GLOBAL_REG "r29" +#elif defined(__IBMC__) && ZEND_GCC_VERSION >= 4002 && defined(__powerpc64__) +# define ZEND_VM_IP_GLOBAL_REG "r29" +#elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__aarch64__) +# define ZEND_VM_IP_GLOBAL_REG "x28" +#elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__riscv) && __riscv_xlen == 64 +# define ZEND_VM_IP_GLOBAL_REG "x19" +#else +# error "global register variables are not supported" +#endif + +int main(void) { + fprintf(stderr, "%s\n", ZEND_VM_IP_GLOBAL_REG); + return 0; +} + ]])], + [zend_ip_reg=$(./conftest$EXEEXT 2>&1)], + [AC_MSG_ERROR(Unexpected, guarded above)]) + + AC_DEFINE([ZEND_UNIVERSAL_IP], 1, [Define whether to use global register variables universally]) + CFLAGS="$CFLAGS -ffixed-$zend_ip_reg" +fi + dnl Check whether __cpuid_count is available. AC_CACHE_CHECK(whether __cpuid_count is available, ac_cv_cpuid_count_available, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 7dcf2ad65b7f6..75bbdca3e6ffd 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -29,6 +29,7 @@ #include "zend_smart_str.h" #include "zend_exceptions_arginfo.h" #include "zend_observer.h" +#include "zend_universal_ip.h" ZEND_API zend_class_entry *zend_ce_throwable; ZEND_API zend_class_entry *zend_ce_exception; @@ -164,7 +165,69 @@ static zend_always_inline bool is_handle_exception_set(void) { return !execute_data || !execute_data->func || !ZEND_USER_CODE(execute_data->func->common.type) +#ifdef ZEND_UNIVERSAL_IP + || zend_universal_ip->opcode == ZEND_HANDLE_EXCEPTION; +#else || execute_data->opline->opcode == ZEND_HANDLE_EXCEPTION; +#endif +} + +#ifdef ZEND_UNIVERSAL_IP +static void zend_copy_exception_consts(uint32_t opnum) +{ + const zend_op *orig_op = &EG(opline_before_exception)[opnum]; + zend_op *exception_op = &EG(exception_op)[opnum]; + + if (orig_op->op1_type == IS_CONST) { + zval *op1 = &EG(exception_consts)[opnum * 2]; + ZVAL_COPY_VALUE(op1, RT_CONSTANT(orig_op, orig_op->op1)); + exception_op->op1.constant = (char *)op1 - (char *)exception_op; + } + if (orig_op->op2_type == IS_CONST) { + zval *op2 = &EG(exception_consts)[opnum * 2 + 1]; + ZVAL_COPY_VALUE(op2, RT_CONSTANT(orig_op, orig_op->op2)); + exception_op->op2.constant = (char *)op2 - (char *)exception_op; + } +} + +static void zend_copy_exception_ops(void) +{ + const zend_op *orig_op = EG(opline_before_exception); + zend_op *exception_op = &EG(exception_op)[0]; + bool has_opdata = orig_op[1].opcode == ZEND_OP_DATA; + + /* The handler may still access opline. Make sure operands keep working. */ + const void *handler = exception_op->handler; + *exception_op = *orig_op; + exception_op->opcode = ZEND_HANDLE_EXCEPTION; + exception_op->handler = handler; + zend_copy_exception_consts(/* opnum */ 0); + if (has_opdata) { + exception_op[1] = orig_op[1]; + exception_op[1].opcode = ZEND_OP_DATA; + zend_copy_exception_consts(/* opnum */ 1); + } else { + exception_op[1] = exception_op[2]; + } +} +#endif + +ZEND_API void zend_rethrow_exception(zend_execute_data *execute_data) +{ +#ifdef ZEND_UNIVERSAL_IP + if (zend_universal_ip->opcode != ZEND_HANDLE_EXCEPTION) { + EG(opline_before_exception) = zend_universal_ip; + } +#else + if (EX(opline)->opcode != ZEND_HANDLE_EXCEPTION) { + EG(opline_before_exception) = EX(opline); + } +#endif + EX(opline) = EG(exception_op); +#ifdef ZEND_UNIVERSAL_IP + zend_universal_ip = EG(exception_op); + zend_copy_exception_ops(); +#endif } ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /* {{{ */ @@ -223,8 +286,16 @@ ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /* /* no need to rethrow the exception */ return; } +#ifdef ZEND_UNIVERSAL_IP + EG(opline_before_exception) = zend_universal_ip; +#else EG(opline_before_exception) = EG(current_execute_data)->opline; +#endif EG(current_execute_data)->opline = EG(exception_op); +#ifdef ZEND_UNIVERSAL_IP + zend_universal_ip = EG(exception_op); + zend_copy_exception_ops(); +#endif } /* }}} */ @@ -244,6 +315,9 @@ ZEND_API void zend_clear_exception(void) /* {{{ */ OBJ_RELEASE(exception); if (EG(current_execute_data)) { EG(current_execute_data)->opline = EG(opline_before_exception); + if (ZEND_USER_CODE(EG(current_execute_data)->func->type)) { + LOAD_CURRENT_OPLINE(); + } } #if ZEND_DEBUG EG(opline_before_exception) = NULL; @@ -1017,7 +1091,11 @@ ZEND_API ZEND_COLD void zend_throw_unwind_exit(void) { ZEND_ASSERT(!EG(exception)); EG(exception) = zend_create_unwind_exit(); +#ifdef ZEND_UNIVERSAL_IP + EG(opline_before_exception) = zend_universal_ip; +#else EG(opline_before_exception) = EG(current_execute_data)->opline; +#endif EG(current_execute_data)->opline = EG(exception_op); } @@ -1025,7 +1103,11 @@ ZEND_API ZEND_COLD void zend_throw_graceful_exit(void) { ZEND_ASSERT(!EG(exception)); EG(exception) = zend_create_graceful_exit(); +#ifdef ZEND_UNIVERSAL_IP + EG(opline_before_exception) = zend_universal_ip; +#else EG(opline_before_exception) = EG(current_execute_data)->opline; +#endif EG(current_execute_data)->opline = EG(exception_op); } diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h index f61b5ecb304e2..220b434b388cf 100644 --- a/Zend/zend_exceptions.h +++ b/Zend/zend_exceptions.h @@ -80,13 +80,7 @@ ZEND_API bool zend_is_graceful_exit(const zend_object *ex); #include "zend_globals.h" -static zend_always_inline void zend_rethrow_exception(zend_execute_data *execute_data) -{ - if (EX(opline)->opcode != ZEND_HANDLE_EXCEPTION) { - EG(opline_before_exception) = EX(opline); - EX(opline) = EG(exception_op); - } -} +ZEND_API void zend_rethrow_exception(zend_execute_data *execute_data); END_EXTERN_C() diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 1819d0b671dc8..0c4303dbc5ad4 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -106,7 +106,19 @@ # pragma GCC diagnostic ignored "-Wvolatile-register-var" register const zend_op* volatile opline __asm__(ZEND_VM_IP_GLOBAL_REG); # pragma GCC diagnostic warning "-Wvolatile-register-var" +#endif + +#ifdef ZEND_UNIVERSAL_IP +# define ZEND_CURRENT_OPLINE opline +# define LOAD_CURRENT_OPLINE() \ + do { \ + if (EG(current_execute_data)) { \ + opline = EG(current_execute_data)->opline; \ + } \ + } while (0) #else +# define ZEND_CURRENT_OPLINE EX(opline) +# define LOAD_CURRENT_OPLINE() #endif #define _CONST_CODE 0 @@ -279,12 +291,12 @@ static zend_never_inline ZEND_COLD zval* zval_undefined_cv(uint32_t var EXECUTE_ static zend_never_inline ZEND_COLD zval* ZEND_FASTCALL _zval_undefined_op1(EXECUTE_DATA_D) { - return zval_undefined_cv(EX(opline)->op1.var EXECUTE_DATA_CC); + return zval_undefined_cv(ZEND_CURRENT_OPLINE->op1.var EXECUTE_DATA_CC); } static zend_never_inline ZEND_COLD zval* ZEND_FASTCALL _zval_undefined_op2(EXECUTE_DATA_D) { - return zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC); + return zval_undefined_cv(ZEND_CURRENT_OPLINE->op2.var EXECUTE_DATA_CC); } #define ZVAL_UNDEFINED_OP1() _zval_undefined_op1(EXECUTE_DATA_C) @@ -1680,8 +1692,10 @@ static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type ZEND_API ZEND_COLD void zend_wrong_string_offset_error(void) { const char *msg = NULL; +#ifndef ZEND_UNIVERSAL_IP const zend_execute_data *execute_data = EG(current_execute_data); const zend_op *opline = execute_data->opline; +#endif if (UNEXPECTED(EG(exception) != NULL)) { return; @@ -5134,11 +5148,17 @@ zval * ZEND_FASTCALL zend_handle_named_arg( return arg; } -static zend_execute_data *start_fake_frame(zend_execute_data *call, const zend_op *opline) { +static zend_execute_data *start_fake_frame(zend_execute_data *call, const zend_op *op) { zend_execute_data *old_prev_execute_data = call->prev_execute_data; call->prev_execute_data = EG(current_execute_data); - call->opline = opline; + call->opline = op; EG(current_execute_data) = call; +#ifdef ZEND_UNIVERSAL_IP + if (call->prev_execute_data) { + call->prev_execute_data->opline = opline; + } + opline = op; +#endif return old_prev_execute_data; } @@ -5146,6 +5166,11 @@ static void end_fake_frame(zend_execute_data *call, zend_execute_data *old_prev_ zend_execute_data *prev_execute_data = call->prev_execute_data; EG(current_execute_data) = prev_execute_data; call->prev_execute_data = old_prev_execute_data; +#ifdef ZEND_UNIVERSAL_IP + if (prev_execute_data) { + opline = prev_execute_data->opline; + } +#endif if (UNEXPECTED(EG(exception)) && ZEND_USER_CODE(prev_execute_data->func->common.type)) { zend_rethrow_exception(prev_execute_data); } @@ -5306,7 +5331,7 @@ static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame(uint #define ZEND_VM_NEXT_OPCODE_EX(check_exception, skip) \ CHECK_SYMBOL_TABLES() \ if (check_exception) { \ - OPLINE = EX(opline) + (skip); \ + OPLINE = ZEND_CURRENT_OPLINE + (skip); \ } else { \ ZEND_ASSERT(!EG(exception)); \ OPLINE = opline + (skip); \ @@ -5354,7 +5379,7 @@ static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame(uint ZEND_VM_CONTINUE() #define ZEND_VM_SMART_BRANCH(_result, _check) do { \ if ((_check) && UNEXPECTED(EG(exception))) { \ - OPLINE = EX(opline); \ + OPLINE = ZEND_CURRENT_OPLINE; \ } else if (EXPECTED(opline->result_type == (IS_SMART_BRANCH_JMPZ|IS_TMP_VAR))) { \ if (_result) { \ ZEND_VM_SET_NEXT_OPCODE(opline + 2); \ @@ -5375,7 +5400,7 @@ static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame(uint } while (0) #define ZEND_VM_SMART_BRANCH_JMPZ(_result, _check) do { \ if ((_check) && UNEXPECTED(EG(exception))) { \ - OPLINE = EX(opline); \ + OPLINE = ZEND_CURRENT_OPLINE; \ } else if (_result) { \ ZEND_VM_SET_NEXT_OPCODE(opline + 2); \ } else { \ @@ -5385,7 +5410,7 @@ static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame(uint } while (0) #define ZEND_VM_SMART_BRANCH_JMPNZ(_result, _check) do { \ if ((_check) && UNEXPECTED(EG(exception))) { \ - OPLINE = EX(opline); \ + OPLINE = ZEND_CURRENT_OPLINE; \ } else if (!(_result)) { \ ZEND_VM_SET_NEXT_OPCODE(opline + 2); \ } else { \ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index feeff659bd5f4..c654e0bc1a1a4 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -38,6 +38,7 @@ #include "zend_inheritance.h" #include "zend_observer.h" #include "zend_call_stack.h" +#include "zend_universal_ip.h" #ifdef HAVE_SYS_TIME_H #include #endif @@ -644,15 +645,21 @@ ZEND_API uint32_t zend_get_executed_lineno(void) /* {{{ */ ex = ex->prev_execute_data; } if (ex) { - if (!ex->opline) { +#ifdef ZEND_UNIVERSAL_IP + const zend_op *opline = ex == EG(current_execute_data) ? zend_universal_ip : ex->opline; +#else + const zend_op *opline = ex->opline; +#endif + + if (!opline) { /* Missing SAVE_OPLINE()? Falling back to first line of function */ return ex->func->op_array.opcodes[0].lineno; } - if (EG(exception) && ex->opline->opcode == ZEND_HANDLE_EXCEPTION && - ex->opline->lineno == 0 && EG(opline_before_exception)) { + if (EG(exception) && opline->opcode == ZEND_HANDLE_EXCEPTION && + opline->lineno == 0 && EG(opline_before_exception)) { return EG(opline_before_exception)->lineno; } - return ex->opline->lineno; + return opline->lineno; } else { return 0; } @@ -810,6 +817,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ call = zend_vm_stack_push_call_frame(call_info, func, fci->param_count, object_or_called_scope); + SAVE_CURRENT_OPLINE(); if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_DEPRECATED)) { zend_deprecated_function(func); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 0ab0257f87700..bd6e8c636d24b 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -25,6 +25,7 @@ #include "zend_closures.h" #include "zend_generators_arginfo.h" #include "zend_observer.h" +#include "zend_universal_ip.h" ZEND_API zend_class_entry *zend_ce_generator; ZEND_API zend_class_entry *zend_ce_ClosedGeneratorException; @@ -456,8 +457,10 @@ static void zend_generator_throw_exception(zend_generator *generator, zval *exce /* Throw the exception in the context of the generator. Decrementing the opline * to pretend the exception happened during the YIELD opcode. */ + SAVE_CURRENT_OPLINE(); EG(current_execute_data) = generator->execute_data; generator->execute_data->opline--; + LOAD_CURRENT_OPLINE(); if (exception) { zend_throw_exception_object(exception); @@ -473,6 +476,7 @@ static void zend_generator_throw_exception(zend_generator *generator, zval *exce generator->execute_data->opline++; EG(current_execute_data) = original_execute_data; + LOAD_CURRENT_OPLINE(); } static void zend_generator_add_child(zend_generator *generator, zend_generator *child) @@ -564,7 +568,9 @@ ZEND_API zend_generator *zend_generator_update_current(zend_generator *generator if (Z_ISUNDEF(new_root_parent->retval)) { /* Throw the exception in the context of the generator */ zend_execute_data *original_execute_data = EG(current_execute_data); + SAVE_CURRENT_OPLINE(); EG(current_execute_data) = new_root->execute_data; + LOAD_CURRENT_OPLINE(); if (new_root == generator) { new_root->execute_data->prev_execute_data = original_execute_data; @@ -575,9 +581,11 @@ ZEND_API zend_generator *zend_generator_update_current(zend_generator *generator /* ZEND_YIELD(_FROM) already advance, so decrement opline to throw from correct place */ new_root->execute_data->opline--; + LOAD_CURRENT_OPLINE(); zend_throw_exception(zend_ce_ClosedGeneratorException, "Generator yielded from aborted, no return value available", 0); EG(current_execute_data) = original_execute_data; + LOAD_CURRENT_OPLINE(); if (!((old_root ? old_root : generator)->flags & ZEND_GENERATOR_CURRENTLY_RUNNING)) { new_root->node.parent = NULL; @@ -602,6 +610,7 @@ ZEND_API zend_generator *zend_generator_update_current(zend_generator *generator static zend_result zend_generator_get_next_delegated_value(zend_generator *generator) /* {{{ */ { --generator->execute_data->opline; + LOAD_CURRENT_OPLINE(); zval *value; if (Z_TYPE(generator->values) == IS_ARRAY) { @@ -685,6 +694,7 @@ static zend_result zend_generator_get_next_delegated_value(zend_generator *gener } ++generator->execute_data->opline; + LOAD_CURRENT_OPLINE(); return SUCCESS; failure: @@ -692,6 +702,7 @@ static zend_result zend_generator_get_next_delegated_value(zend_generator *gener ZVAL_UNDEF(&generator->values); ++generator->execute_data->opline; + LOAD_CURRENT_OPLINE(); return FAILURE; } /* }}} */ @@ -725,7 +736,9 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ uint32_t original_jit_trace_num = EG(jit_trace_num); /* Set executor globals */ + SAVE_CURRENT_OPLINE(); EG(current_execute_data) = generator->execute_data; + LOAD_CURRENT_OPLINE(); EG(jit_trace_num) = 0; /* We want the backtrace to look as if the generator function was @@ -745,6 +758,7 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ if (EXPECTED(zend_generator_get_next_delegated_value(generator) == SUCCESS)) { /* Restore executor globals */ EG(current_execute_data) = original_execute_data; + LOAD_CURRENT_OPLINE(); EG(jit_trace_num) = original_jit_trace_num; orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT; @@ -783,6 +797,7 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ /* Restore executor globals */ EG(current_execute_data) = original_execute_data; + LOAD_CURRENT_OPLINE(); EG(jit_trace_num) = original_jit_trace_num; /* If an exception was thrown in the generator we have to internally diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 8900a5f416f53..4b2067f725458 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -246,6 +246,11 @@ struct _zend_executor_globals { zend_object *exception, *prev_exception; const zend_op *opline_before_exception; zend_op exception_op[3]; +#ifdef ZEND_UNIVERSAL_IP + /* Consts copied over from opline during exceptions. Allows the throwing handler to access + * constants even after the opline register has been adjusted. */ + zval exception_consts[4]; +#endif struct _zend_module_entry *current_module; diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 573d9eb106df1..09620f891402c 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -30,6 +30,7 @@ #include "zend_closures.h" #include "zend_compile.h" #include "zend_hash.h" +#include "zend_universal_ip.h" #define DEBUG_OBJECT_HANDLERS 0 @@ -867,10 +868,10 @@ found:; if (execute_data && EX(func) && ZEND_USER_CODE(EX(func)->common.type) - && EX(opline) - && EX(opline)->opcode == ZEND_ASSIGN_OBJ - && EX(opline)->result_type) { - ZVAL_COPY_DEREF(EX_VAR(EX(opline)->result.var), variable_ptr); + && ZEND_CURRENT_OPLINE + && ZEND_CURRENT_OPLINE->opcode == ZEND_ASSIGN_OBJ + && ZEND_CURRENT_OPLINE->result_type) { + ZVAL_COPY_DEREF(EX_VAR(ZEND_CURRENT_OPLINE->result.var), variable_ptr); variable_ptr = NULL; } rc_dtor_func(garbage); diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 4c4b3cf30c13d..94d6911d2d5d5 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -174,6 +174,11 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) if (old_exception) { EG(opline_before_exception) = old_opline_before_exception; + if (EG(current_execute_data) + && EG(current_execute_data)->func + && ZEND_USER_CODE(EG(current_execute_data)->func->common.type)) { + zend_rethrow_exception(EG(current_execute_data)); + } if (EG(exception)) { zend_exception_set_previous(EG(exception), old_exception); } else { diff --git a/Zend/zend_universal_ip.h b/Zend/zend_universal_ip.h new file mode 100644 index 0000000000000..61e8e28eaca37 --- /dev/null +++ b/Zend/zend_universal_ip.h @@ -0,0 +1,66 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_GLOBAL_REGS_H +#define ZEND_GLOBAL_REGS_H + +#ifdef PHP_WIN32 +# include "../../../../main/config.w32.h" +#else +# include +#endif +#include "zend_vm_opcodes.h" + +#if defined(HAVE_GCC_GLOBAL_REGS) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) +# if defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(i386) +# define ZEND_VM_IP_GLOBAL_REG "%edi" +# elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__x86_64__) +# define ZEND_VM_IP_GLOBAL_REG "%r15" +# elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__powerpc64__) +# define ZEND_VM_IP_GLOBAL_REG "r15" +# elif defined(__IBMC__) && ZEND_GCC_VERSION >= 4002 && defined(__powerpc64__) +# define ZEND_VM_IP_GLOBAL_REG "r15" +# elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__aarch64__) +# define ZEND_VM_IP_GLOBAL_REG "x28" +# elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__riscv) && __riscv_xlen == 64 +# define ZEND_VM_IP_GLOBAL_REG "x19" +# endif +#endif + +#if defined(ZEND_UNIVERSAL_IP) && defined(ZEND_VM_IP_GLOBAL_REG) +# pragma GCC diagnostic ignored "-Wvolatile-register-var" +register const zend_op* volatile zend_universal_ip __asm__(ZEND_VM_IP_GLOBAL_REG); +# pragma GCC diagnostic warning "-Wvolatile-register-var" +# define ZEND_CURRENT_OPLINE zend_universal_ip +# define SAVE_CURRENT_OPLINE() \ + do { \ + if (EG(current_execute_data)) { \ + EG(current_execute_data)->opline = zend_universal_ip; \ + } \ + } while (0) +# define LOAD_CURRENT_OPLINE() \ + do { \ + if (EG(current_execute_data)) { \ + zend_universal_ip = EG(current_execute_data)->opline; \ + } \ + } while (0) +#else +# define ZEND_CURRENT_OPLINE EX(opline) +# define SAVE_CURRENT_OPLINE() +# define LOAD_CURRENT_OPLINE() +#endif + +#endif /* ZEND_GLOBAL_REGS_H */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 909e8e43ecbd7..c66d5c4ff3bbc 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2853,10 +2853,11 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) { zend_execute_data *old_execute_data; uint32_t call_info = EX_CALL_INFO(); - SAVE_OPLINE(); + SAVE_OPLINE_EX(); if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -2879,6 +2880,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -2923,6 +2925,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) efree_size(EX(func), sizeof(zend_op_array)); old_execute_data = execute_data; execute_data = EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); zend_vm_stack_free_call_frame_ex(call_info, old_execute_data); if (call_info & ZEND_CALL_NEEDS_REATTACH) { @@ -2942,6 +2945,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) } else { if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD call_info = EX_CALL_INFO(); @@ -2983,6 +2987,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) } } EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); ZEND_VM_RETURN(); } } @@ -3967,7 +3972,7 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) zval *ret; zval retval; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; call->prev_execute_data = execute_data; @@ -4015,6 +4020,7 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) } if (UNEXPECTED(EG(exception) != NULL)) { + /* FIXME: Restore opline? */ zend_rethrow_exception(execute_data); HANDLE_EXCEPTION(); } @@ -4030,7 +4036,7 @@ ZEND_VM_HOT_HANDLER(130, ZEND_DO_UCALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) zend_function *fbc = call->func; zval *ret; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; ret = NULL; @@ -4055,7 +4061,7 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER)) zend_function *fbc = call->func; zval *ret; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { @@ -4153,7 +4159,7 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) zend_function *fbc = call->func; zval *ret; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { @@ -6387,7 +6393,7 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMPVAR|CV, ANY, EVAL, SPEC(OBSER zend_op_array *new_op_array; zval *inc_filename; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); inc_filename = GET_OP1_ZVAL_PTR(BP_VAR_R); new_op_array = zend_include_or_eval(inc_filename, opline->extended_value); if (UNEXPECTED(EG(exception) != NULL)) { @@ -8330,6 +8336,9 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMPVAR|CV|UNUSED /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -8436,6 +8445,9 @@ ZEND_VM_C_LABEL(yield_from_try_again): /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -8782,7 +8794,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER)) uint32_t num_args = EX_NUM_ARGS(); zend_execute_data *call; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); if (num_args) { zval *p = ZEND_CALL_ARG(execute_data, 1); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index feeb1f6f6b090..0f2a74e9c222b 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -393,7 +393,15 @@ static const void *zend_vm_get_opcode_handler_func(uint8_t opcode, const zend_op typedef ZEND_OPCODE_HANDLER_RET (ZEND_FASTCALL *opcode_handler_t) (ZEND_OPCODE_HANDLER_ARGS); #define DCL_OPLINE -#ifdef ZEND_VM_IP_GLOBAL_REG +#ifdef ZEND_UNIVERSAL_IP +# define OPLINE opline +# define USE_OPLINE +# define LOAD_OPLINE() opline = EX(opline) +# define LOAD_OPLINE_EX() +# define LOAD_NEXT_OPLINE() opline = EX(opline) + 1 +# define SAVE_OPLINE() +# define SAVE_OPLINE_EX() EX(opline) = opline +#elif defined(ZEND_VM_IP_GLOBAL_REG) # define OPLINE opline # define USE_OPLINE # define LOAD_OPLINE() opline = EX(opline) @@ -1109,10 +1117,11 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper { zend_execute_data *old_execute_data; uint32_t call_info = EX_CALL_INFO(); - SAVE_OPLINE(); + SAVE_OPLINE_EX(); if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -1135,6 +1144,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -1179,6 +1189,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper efree_size(EX(func), sizeof(zend_op_array)); old_execute_data = execute_data; execute_data = EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); zend_vm_stack_free_call_frame_ex(call_info, old_execute_data); if (call_info & ZEND_CALL_NEEDS_REATTACH) { @@ -1198,6 +1209,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper } else { if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD call_info = EX_CALL_INFO(); @@ -1239,6 +1251,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper } } EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); ZEND_VM_RETURN(); } } @@ -1259,7 +1272,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV zval *ret; zval retval; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; call->prev_execute_data = execute_data; @@ -1305,6 +1318,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV } if (UNEXPECTED(EG(exception) != NULL)) { + /* FIXME: Restore opline? */ zend_rethrow_exception(execute_data); HANDLE_EXCEPTION(); } @@ -1321,7 +1335,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV zval *ret; zval retval; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; call->prev_execute_data = execute_data; @@ -1367,6 +1381,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV } if (UNEXPECTED(EG(exception) != NULL)) { + /* FIXME: Restore opline? */ zend_rethrow_exception(execute_data); HANDLE_EXCEPTION(); } @@ -1383,7 +1398,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_OBS zval *ret; zval retval; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; call->prev_execute_data = execute_data; @@ -1431,6 +1446,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_OBS } if (UNEXPECTED(EG(exception) != NULL)) { + /* FIXME: Restore opline? */ zend_rethrow_exception(execute_data); HANDLE_EXCEPTION(); } @@ -1446,7 +1462,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_UCALL_SPEC_RETV zend_function *fbc = call->func; zval *ret; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; ret = NULL; @@ -1470,7 +1486,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_UCALL_SPEC_RETV zend_function *fbc = call->func; zval *ret; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; ret = NULL; @@ -1494,7 +1510,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_UCALL_SPEC_OBS zend_function *fbc = call->func; zval *ret; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; ret = NULL; @@ -1519,7 +1535,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S zend_function *fbc = call->func; zval *ret; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { @@ -1614,7 +1630,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S zend_function *fbc = call->func; zval *ret; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { @@ -1709,7 +1725,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_ zend_function *fbc = call->func; zval *ret; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { @@ -1807,7 +1823,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV zend_function *fbc = call->func; zval *ret; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { @@ -1916,7 +1932,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV zend_function *fbc = call->func; zval *ret; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { @@ -2025,7 +2041,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_OBS zend_function *fbc = call->func; zval *ret; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); EX(call) = call->prev_execute_data; if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { @@ -3400,7 +3416,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z uint32_t num_args = EX_NUM_ARGS(); zend_execute_data *call; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); if (num_args) { zval *p = ZEND_CALL_ARG(execute_data, 1); @@ -3540,7 +3556,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_ uint32_t num_args = EX_NUM_ARGS(); zend_execute_data *call; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); if (num_args) { zval *p = ZEND_CALL_ARG(execute_data, 1); @@ -4974,7 +4990,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HAN zend_op_array *new_op_array; zval *inc_filename; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); inc_filename = RT_CONSTANT(opline, opline->op1); new_op_array = zend_include_or_eval(inc_filename, opline->extended_value); if (UNEXPECTED(EG(exception) != NULL)) { @@ -5057,7 +5073,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_OBSERVER_ zend_op_array *new_op_array; zval *inc_filename; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); inc_filename = get_zval_ptr(opline->op1_type, opline->op1, BP_VAR_R); new_op_array = zend_include_or_eval(inc_filename, opline->extended_value); if (UNEXPECTED(EG(exception) != NULL)) { @@ -5595,6 +5611,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CONST_HANDLER( /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -7755,6 +7774,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -10037,6 +10059,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMPVAR_HANDLE /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -10886,6 +10911,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLE /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -12421,6 +12449,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZE /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -14790,7 +14821,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HA zend_op_array *new_op_array; zval *inc_filename; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); inc_filename = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); new_op_array = zend_include_or_eval(inc_filename, opline->extended_value); if (UNEXPECTED(EG(exception) != NULL)) { @@ -14969,6 +15000,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_TMPVAR_HANDLER /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -20288,6 +20322,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(Z /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -20733,6 +20770,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMPVAR_HANDLER( /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -21193,6 +21233,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER( /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -21597,6 +21640,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -25561,6 +25607,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(Z /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -28014,6 +28063,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMPVAR_HANDLER( /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -29969,6 +30021,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER( /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -32399,6 +32454,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -34591,6 +34649,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLE /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -36475,6 +36536,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMPVAR_HANDL /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -37016,6 +37080,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDL /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -38968,6 +39035,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(Z /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -39825,7 +39895,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLE zend_op_array *new_op_array; zval *inc_filename; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); inc_filename = _get_zval_ptr_cv_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); new_op_array = zend_include_or_eval(inc_filename, opline->extended_value); if (UNEXPECTED(EG(exception) != NULL)) { @@ -40327,6 +40397,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CV_HANDLER(ZEN /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -44311,6 +44384,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZE /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -47910,6 +47986,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMPVAR_HANDLER(Z /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -49768,6 +49847,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(Z /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -53393,6 +53475,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_ /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); +#ifdef ZEND_UNIVERSAL_IP + EX(opline) = opline; +#endif ZEND_VM_RETURN(); } @@ -57072,10 +57157,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) { zend_execute_data *old_execute_data; uint32_t call_info = EX_CALL_INFO(); - SAVE_OPLINE(); + SAVE_OPLINE_EX(); if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -57098,6 +57184,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -57142,6 +57229,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) efree_size(EX(func), sizeof(zend_op_array)); old_execute_data = execute_data; execute_data = EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); zend_vm_stack_free_call_frame_ex(call_info, old_execute_data); if (call_info & ZEND_CALL_NEEDS_REATTACH) { @@ -57161,6 +57249,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) } else { if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD call_info = EX_CALL_INFO(); @@ -57202,6 +57291,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) } } EG(current_execute_data) = EX(prev_execute_data); + LOAD_CURRENT_OPLINE(); ZEND_VM_RETURN(); } } @@ -65809,13 +65899,13 @@ ZEND_API int ZEND_FASTCALL zend_vm_call_opcode_handler(zend_execute_data* ex) if (EXPECTED(opline)) { #endif ret = execute_data != ex ? (int)(execute_data->prev_execute_data != ex) + 1 : 0; - SAVE_OPLINE(); + SAVE_OPLINE_EX(); } else { ret = -1; } #else ret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); - SAVE_OPLINE(); + SAVE_OPLINE_EX(); #endif #ifdef ZEND_VM_FP_GLOBAL_REG execute_data = orig_execute_data; diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index f7b630bc5a048..6231a774df645 100755 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -1900,7 +1900,16 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) out($f,"typedef ZEND_OPCODE_HANDLER_RET (ZEND_FASTCALL *opcode_handler_t) (ZEND_OPCODE_HANDLER_ARGS);\n"); out($f,"\n"); out($f,"#define DCL_OPLINE\n"); - out($f,"#ifdef ZEND_VM_IP_GLOBAL_REG\n"); + out($f,"#ifdef ZEND_UNIVERSAL_IP\n"); + out($f,"# define OPLINE opline\n"); + out($f,"# define USE_OPLINE\n"); + out($f,"# define LOAD_OPLINE() opline = EX(opline)\n"); + out($f,"# define LOAD_OPLINE_EX()\n"); + /* Called on restored frames with a valid EX(opline). */ + out($f,"# define LOAD_NEXT_OPLINE() opline = EX(opline) + 1\n"); + out($f,"# define SAVE_OPLINE()\n"); + out($f,"# define SAVE_OPLINE_EX() EX(opline) = opline\n"); + out($f,"#elif defined(ZEND_VM_IP_GLOBAL_REG)\n"); out($f,"# define OPLINE opline\n"); out($f,"# define USE_OPLINE\n"); out($f,"# define LOAD_OPLINE() opline = EX(opline)\n"); @@ -1957,7 +1966,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) out($f,"# define LOAD_OPLINE_EX() LOAD_OPLINE()\n"); out($f,"#define LOAD_NEXT_OPLINE() opline = EX(opline) + 1\n"); out($f,"#define SAVE_OPLINE() EX(opline) = opline\n"); - out($f,"#define SAVE_OPLINE_EX()\n"); + out($f,"#define SAVE_OPLINE_EX() SAVE_OPLINE()\n"); out($f,"#define HANDLE_EXCEPTION() ZEND_ASSERT(EG(exception)); LOAD_OPLINE(); ZEND_VM_CONTINUE()\n"); out($f,"#define HANDLE_EXCEPTION_LEAVE() ZEND_ASSERT(EG(exception)); LOAD_OPLINE(); ZEND_VM_LEAVE()\n"); out($f,"#define ZEND_VM_CONTINUE() goto zend_vm_continue\n"); @@ -1983,7 +1992,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) out($f,"#define LOAD_OPLINE_EX() LOAD_OPLINE()\n"); out($f,"#define LOAD_NEXT_OPLINE() opline = EX(opline) + 1\n"); out($f,"#define SAVE_OPLINE() EX(opline) = opline\n"); - out($f,"#define SAVE_OPLINE_EX()\n"); + out($f,"#define SAVE_OPLINE_EX() SAVE_OPLINE()\n"); if (ZEND_VM_SPEC) { out($f,"#define HANDLE_EXCEPTION() ZEND_ASSERT(EG(exception)); goto ZEND_HANDLE_EXCEPTION_SPEC_LABEL\n"); out($f,"#define HANDLE_EXCEPTION_LEAVE() ZEND_ASSERT(EG(exception)); goto ZEND_HANDLE_EXCEPTION_SPEC_LABEL\n"); @@ -2975,13 +2984,13 @@ function gen_vm($def, $skel) { out($f, "\tif (EXPECTED(opline)) {\n"); } out($f, "\t\tret = execute_data != ex ? (int)(execute_data->prev_execute_data != ex) + 1 : 0;\n"); - out($f, "\t\tSAVE_OPLINE();\n"); + out($f, "\t\tSAVE_OPLINE_EX();\n"); out($f, "\t} else {\n"); out($f, "\t\tret = -1;\n"); out($f, "\t}\n"); out($f, "#else\n"); out($f, "\tret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); - out($f, "\tSAVE_OPLINE();\n"); + out($f, "\tSAVE_OPLINE_EX();\n"); out($f, "#endif\n"); out($f, "#ifdef ZEND_VM_FP_GLOBAL_REG\n"); out($f, "\texecute_data = orig_execute_data;\n"); diff --git a/benchmark/benchmark.php b/benchmark/benchmark.php index a0c01ca766233..e08b06cdedded 100644 --- a/benchmark/benchmark.php +++ b/benchmark/benchmark.php @@ -22,11 +22,11 @@ function main() { $data['branch'] = $branch; } $data['Zend/bench.php'] = runBench(false); - $data['Zend/bench.php JIT'] = runBench(true); + // $data['Zend/bench.php JIT'] = runBench(true); $data['Symfony Demo 2.2.3'] = runSymfonyDemo(false); - $data['Symfony Demo 2.2.3 JIT'] = runSymfonyDemo(true); + // $data['Symfony Demo 2.2.3 JIT'] = runSymfonyDemo(true); $data['Wordpress 6.2'] = runWordpress(false); - $data['Wordpress 6.2 JIT'] = runWordpress(true); + // $data['Wordpress 6.2 JIT'] = runWordpress(true); $result = json_encode($data, JSON_PRETTY_PRINT) . "\n"; fwrite(STDOUT, $result); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a725573aa9076..c27592663853d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -45,6 +45,7 @@ #include "zend_smart_str.h" #include "zend_enum.h" #include "zend_fibers.h" +#include "zend_universal_ip.h" #define REFLECTION_ATTRIBUTE_IS_INSTANCEOF (1 << 1) @@ -2250,6 +2251,8 @@ ZEND_METHOD(ReflectionGenerator, getTrace) RETURN_THROWS(); } + SAVE_CURRENT_OPLINE(); + REFLECTION_CHECK_VALID_GENERATOR(ex) root_generator = zend_generator_get_current(generator); @@ -2264,8 +2267,10 @@ ZEND_METHOD(ReflectionGenerator, getTrace) } EG(current_execute_data) = root_generator->execute_data; + LOAD_CURRENT_OPLINE(); zend_fetch_debug_backtrace(return_value, 0, options, 0); EG(current_execute_data) = ex_backup; + LOAD_CURRENT_OPLINE(); root_generator->execute_data->prev_execute_data = root_prev; generator->execute_data->prev_execute_data = cur_prev; @@ -6692,6 +6697,7 @@ static int call_attribute_constructor( call->call = NULL; call->return_value = NULL; call->func = (zend_function*)(call->opline + 1); + SAVE_CURRENT_OPLINE(); call->prev_execute_data = EG(current_execute_data); memset(call->func, 0, sizeof(zend_function)); @@ -6702,12 +6708,14 @@ static int call_attribute_constructor( call->func->op_array.filename = filename; EG(current_execute_data) = call; + LOAD_CURRENT_OPLINE(); } zend_call_known_function(ctor, obj, obj->ce, NULL, argc, args, named_params); if (filename) { EG(current_execute_data) = call->prev_execute_data; + LOAD_CURRENT_OPLINE(); zend_vm_stack_free_call_frame(call); } @@ -7093,6 +7101,7 @@ ZEND_METHOD(ReflectionFiber, getTrace) fiber->stack_bottom->prev_execute_data = NULL; if (EG(active_fiber) != fiber) { + SAVE_CURRENT_OPLINE(); // No need to replace current execute data if within the current fiber. EG(current_execute_data) = fiber->execute_data; } @@ -7100,6 +7109,7 @@ ZEND_METHOD(ReflectionFiber, getTrace) zend_fetch_debug_backtrace(return_value, 0, options, 0); EG(current_execute_data) = execute_data; // Restore original execute data. + LOAD_CURRENT_OPLINE(); fiber->stack_bottom->prev_execute_data = prev_execute_data; // Restore prev execute data on fiber stack. }