From d196e47eaf1dff3ef4c487c538b18ed919c320ce Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Fri, 16 Apr 2021 15:34:32 -0500 Subject: [PATCH 01/43] Implement Fibers --- Zend/asm/LICENSE | 23 + Zend/asm/jump_arm64_aapcs_elf_gas.S | 114 +++ Zend/asm/jump_arm64_aapcs_macho_gas.S | 109 +++ Zend/asm/jump_arm_aapcs_elf_gas.S | 88 ++ Zend/asm/jump_arm_aapcs_macho_gas.S | 95 +++ Zend/asm/jump_combined_sysv_macho_gas.S | 24 + Zend/asm/jump_i386_ms_pe_masm.asm | 116 +++ Zend/asm/jump_i386_sysv_elf_gas.S | 83 ++ Zend/asm/jump_i386_sysv_macho_gas.S | 74 ++ Zend/asm/jump_mips32_o32_elf_gas.S | 119 +++ Zend/asm/jump_mips64_n64_elf_gas.S | 124 +++ Zend/asm/jump_ppc32_sysv_elf_gas.S | 201 +++++ Zend/asm/jump_ppc32_sysv_macho_gas.S | 201 +++++ Zend/asm/jump_ppc64_sysv_elf_gas.S | 221 +++++ Zend/asm/jump_ppc64_sysv_macho_gas.S | 164 ++++ Zend/asm/jump_x86_64_ms_pe_masm.asm | 205 +++++ Zend/asm/jump_x86_64_sysv_elf_gas.S | 91 +++ Zend/asm/jump_x86_64_sysv_macho_gas.S | 75 ++ Zend/asm/make_arm64_aapcs_elf_gas.S | 85 ++ Zend/asm/make_arm64_aapcs_macho_gas.S | 83 ++ Zend/asm/make_arm_aapcs_elf_gas.S | 81 ++ Zend/asm/make_arm_aapcs_macho_gas.S | 71 ++ Zend/asm/make_combined_sysv_macho_gas.S | 24 + Zend/asm/make_i386_ms_pe_masm.asm | 140 ++++ Zend/asm/make_i386_sysv_elf_gas.S | 107 +++ Zend/asm/make_i386_sysv_macho_gas.S | 90 ++ Zend/asm/make_mips32_o32_elf_gas.S | 97 +++ Zend/asm/make_mips64_n64_elf_gas.S | 96 +++ Zend/asm/make_ppc32_sysv_elf_gas.S | 146 ++++ Zend/asm/make_ppc32_sysv_macho_gas.S | 137 ++++ Zend/asm/make_ppc64_sysv_elf_gas.S | 177 ++++ Zend/asm/make_ppc64_sysv_macho_gas.S | 126 +++ Zend/asm/make_x86_64_ms_pe_masm.asm | 163 ++++ Zend/asm/make_x86_64_sysv_elf_gas.S | 82 ++ Zend/asm/make_x86_64_sysv_macho_gas.S | 76 ++ Zend/tests/fibers/catch.phpt | 21 + Zend/tests/fibers/double-start.phpt | 19 + Zend/tests/fibers/exit-in-fiber.phpt | 19 + Zend/tests/fibers/failing-fiber.phpt | 23 + Zend/tests/fibers/fast-finish-fiber.phpt | 16 + Zend/tests/fibers/fatal-error-in-fiber.phpt | 13 + .../fibers/fatal-error-in-nested-fiber.phpt | 27 + .../fatal-error-with-multiple-fibers.phpt | 21 + .../fibers/fiber-created-in-destruct.phpt | 23 + Zend/tests/fibers/fiber-error-construct.phpt | 13 + Zend/tests/fibers/fiber-in-destruct.phpt | 34 + .../fibers/fiber-in-shutdown-function.phpt | 23 + Zend/tests/fibers/fiber-status.phpt | 50 ++ Zend/tests/fibers/fiber-this.phpt | 17 + .../tests/fibers/fiber-throw-in-destruct.phpt | 26 + .../fibers/get-return-after-throwing.phpt | 23 + .../get-return-from-unstarted-fiber.phpt | 15 + .../get-return-in-unfinished-fiber.phpt | 19 + Zend/tests/fibers/get-return.phpt | 19 + .../fibers/resume-non-running-fiber.phpt | 15 + Zend/tests/fibers/resume-running-fiber.phpt | 19 + .../tests/fibers/resume-terminated-fiber.phpt | 17 + Zend/tests/fibers/resume.phpt | 17 + Zend/tests/fibers/silence-operator.phpt | 27 + Zend/tests/fibers/start-arguments.phpt | 27 + ...d-in-force-close-fiber-after-shutdown.phpt | 26 + .../fibers/suspend-in-force-close-fiber.phpt | 24 + .../fibers/suspend-in-nested-function.phpt | 28 + Zend/tests/fibers/suspend-outside-fiber.phpt | 13 + .../fibers/throw-into-non-running-fiber.phpt | 15 + Zend/tests/fibers/throw.phpt | 21 + .../fibers/unfinished-fiber-with-finally.phpt | 29 + ...nfinished-fiber-with-nested-try-catch.phpt | 46 ++ ...inished-fiber-with-suspend-in-finally.phpt | 35 + ...nfinished-fiber-with-throw-in-finally.phpt | 47 ++ Zend/tests/fibers/unfinished-fiber.phpt | 24 + Zend/tests/fibers/unstarted-fiber.phpt | 11 + Zend/zend.c | 9 + Zend/zend_default_classes.c | 2 + Zend/zend_exceptions.c | 22 +- Zend/zend_exceptions.h | 4 +- Zend/zend_execute_API.c | 4 + Zend/zend_fibers.c | 767 ++++++++++++++++++ Zend/zend_fibers.h | 129 +++ Zend/zend_fibers.stub.php | 36 + Zend/zend_fibers_arginfo.h | 96 +++ Zend/zend_globals.h | 17 + Zend/zend_portability.h | 6 + configure.ac | 50 +- ext/reflection/php_reflection.c | 154 ++++ ext/reflection/php_reflection.h | 1 + ext/reflection/php_reflection.stub.php | 21 + ext/reflection/php_reflection_arginfo.h | 59 +- .../ReflectionExtension_getClasses_basic.phpt | 7 +- .../tests/ReflectionFiber_basic.phpt | 87 ++ .../tests/ReflectionFiber_errors.phpt | 63 ++ main/main.c | 67 +- win32/build/Makefile.frag.w32 | 7 + win32/build/config.w32 | 13 +- 94 files changed, 6369 insertions(+), 22 deletions(-) create mode 100644 Zend/asm/LICENSE create mode 100644 Zend/asm/jump_arm64_aapcs_elf_gas.S create mode 100644 Zend/asm/jump_arm64_aapcs_macho_gas.S create mode 100644 Zend/asm/jump_arm_aapcs_elf_gas.S create mode 100644 Zend/asm/jump_arm_aapcs_macho_gas.S create mode 100644 Zend/asm/jump_combined_sysv_macho_gas.S create mode 100644 Zend/asm/jump_i386_ms_pe_masm.asm create mode 100644 Zend/asm/jump_i386_sysv_elf_gas.S create mode 100644 Zend/asm/jump_i386_sysv_macho_gas.S create mode 100644 Zend/asm/jump_mips32_o32_elf_gas.S create mode 100644 Zend/asm/jump_mips64_n64_elf_gas.S create mode 100644 Zend/asm/jump_ppc32_sysv_elf_gas.S create mode 100644 Zend/asm/jump_ppc32_sysv_macho_gas.S create mode 100644 Zend/asm/jump_ppc64_sysv_elf_gas.S create mode 100644 Zend/asm/jump_ppc64_sysv_macho_gas.S create mode 100644 Zend/asm/jump_x86_64_ms_pe_masm.asm create mode 100644 Zend/asm/jump_x86_64_sysv_elf_gas.S create mode 100644 Zend/asm/jump_x86_64_sysv_macho_gas.S create mode 100644 Zend/asm/make_arm64_aapcs_elf_gas.S create mode 100644 Zend/asm/make_arm64_aapcs_macho_gas.S create mode 100644 Zend/asm/make_arm_aapcs_elf_gas.S create mode 100644 Zend/asm/make_arm_aapcs_macho_gas.S create mode 100644 Zend/asm/make_combined_sysv_macho_gas.S create mode 100644 Zend/asm/make_i386_ms_pe_masm.asm create mode 100644 Zend/asm/make_i386_sysv_elf_gas.S create mode 100644 Zend/asm/make_i386_sysv_macho_gas.S create mode 100644 Zend/asm/make_mips32_o32_elf_gas.S create mode 100644 Zend/asm/make_mips64_n64_elf_gas.S create mode 100644 Zend/asm/make_ppc32_sysv_elf_gas.S create mode 100644 Zend/asm/make_ppc32_sysv_macho_gas.S create mode 100644 Zend/asm/make_ppc64_sysv_elf_gas.S create mode 100644 Zend/asm/make_ppc64_sysv_macho_gas.S create mode 100644 Zend/asm/make_x86_64_ms_pe_masm.asm create mode 100644 Zend/asm/make_x86_64_sysv_elf_gas.S create mode 100644 Zend/asm/make_x86_64_sysv_macho_gas.S create mode 100644 Zend/tests/fibers/catch.phpt create mode 100644 Zend/tests/fibers/double-start.phpt create mode 100644 Zend/tests/fibers/exit-in-fiber.phpt create mode 100644 Zend/tests/fibers/failing-fiber.phpt create mode 100644 Zend/tests/fibers/fast-finish-fiber.phpt create mode 100644 Zend/tests/fibers/fatal-error-in-fiber.phpt create mode 100644 Zend/tests/fibers/fatal-error-in-nested-fiber.phpt create mode 100644 Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt create mode 100644 Zend/tests/fibers/fiber-created-in-destruct.phpt create mode 100644 Zend/tests/fibers/fiber-error-construct.phpt create mode 100644 Zend/tests/fibers/fiber-in-destruct.phpt create mode 100644 Zend/tests/fibers/fiber-in-shutdown-function.phpt create mode 100644 Zend/tests/fibers/fiber-status.phpt create mode 100644 Zend/tests/fibers/fiber-this.phpt create mode 100644 Zend/tests/fibers/fiber-throw-in-destruct.phpt create mode 100644 Zend/tests/fibers/get-return-after-throwing.phpt create mode 100644 Zend/tests/fibers/get-return-from-unstarted-fiber.phpt create mode 100644 Zend/tests/fibers/get-return-in-unfinished-fiber.phpt create mode 100644 Zend/tests/fibers/get-return.phpt create mode 100644 Zend/tests/fibers/resume-non-running-fiber.phpt create mode 100644 Zend/tests/fibers/resume-running-fiber.phpt create mode 100644 Zend/tests/fibers/resume-terminated-fiber.phpt create mode 100644 Zend/tests/fibers/resume.phpt create mode 100644 Zend/tests/fibers/silence-operator.phpt create mode 100644 Zend/tests/fibers/start-arguments.phpt create mode 100644 Zend/tests/fibers/suspend-in-force-close-fiber-after-shutdown.phpt create mode 100644 Zend/tests/fibers/suspend-in-force-close-fiber.phpt create mode 100644 Zend/tests/fibers/suspend-in-nested-function.phpt create mode 100644 Zend/tests/fibers/suspend-outside-fiber.phpt create mode 100644 Zend/tests/fibers/throw-into-non-running-fiber.phpt create mode 100644 Zend/tests/fibers/throw.phpt create mode 100644 Zend/tests/fibers/unfinished-fiber-with-finally.phpt create mode 100644 Zend/tests/fibers/unfinished-fiber-with-nested-try-catch.phpt create mode 100644 Zend/tests/fibers/unfinished-fiber-with-suspend-in-finally.phpt create mode 100644 Zend/tests/fibers/unfinished-fiber-with-throw-in-finally.phpt create mode 100644 Zend/tests/fibers/unfinished-fiber.phpt create mode 100644 Zend/tests/fibers/unstarted-fiber.phpt create mode 100644 Zend/zend_fibers.c create mode 100644 Zend/zend_fibers.h create mode 100644 Zend/zend_fibers.stub.php create mode 100644 Zend/zend_fibers_arginfo.h create mode 100644 ext/reflection/tests/ReflectionFiber_basic.phpt create mode 100644 ext/reflection/tests/ReflectionFiber_errors.phpt create mode 100644 win32/build/Makefile.frag.w32 diff --git a/Zend/asm/LICENSE b/Zend/asm/LICENSE new file mode 100644 index 0000000000000..36b7cd93cdfba --- /dev/null +++ b/Zend/asm/LICENSE @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Zend/asm/jump_arm64_aapcs_elf_gas.S b/Zend/asm/jump_arm64_aapcs_elf_gas.S new file mode 100644 index 0000000000000..cefd1830d718f --- /dev/null +++ b/Zend/asm/jump_arm64_aapcs_elf_gas.S @@ -0,0 +1,114 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | d8 | d9 | d10 | d11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | d12 | d13 | d14 | d15 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * | x19 | x20 | x21 | x22 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | x23 | x24 | x25 | x26 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| * + * ------------------------------------------------- * + * | x27 | x28 | FP | LR | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | | | * + * ------------------------------------------------- * + * | 0xa0| 0xa4| 0xa8| 0xac| | | * + * ------------------------------------------------- * + * | PC | align | | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.file "jump_arm64_aapcs_elf_gas.S" +.text +.align 2 +.global jump_fcontext +.type jump_fcontext, %function +jump_fcontext: + # prepare stack for GP + FPU + sub sp, sp, #0xb0 + + # save d8 - d15 + stp d8, d9, [sp, #0x00] + stp d10, d11, [sp, #0x10] + stp d12, d13, [sp, #0x20] + stp d14, d15, [sp, #0x30] + + # save x19-x30 + stp x19, x20, [sp, #0x40] + stp x21, x22, [sp, #0x50] + stp x23, x24, [sp, #0x60] + stp x25, x26, [sp, #0x70] + stp x27, x28, [sp, #0x80] + stp x29, x30, [sp, #0x90] + + # save LR as PC + str x30, [sp, #0xa0] + + # store RSP (pointing to context-data) in X0 + mov x4, sp + + # restore RSP (pointing to context-data) from X1 + mov sp, x0 + + # load d8 - d15 + ldp d8, d9, [sp, #0x00] + ldp d10, d11, [sp, #0x10] + ldp d12, d13, [sp, #0x20] + ldp d14, d15, [sp, #0x30] + + # load x19-x30 + ldp x19, x20, [sp, #0x40] + ldp x21, x22, [sp, #0x50] + ldp x23, x24, [sp, #0x60] + ldp x25, x26, [sp, #0x70] + ldp x27, x28, [sp, #0x80] + ldp x29, x30, [sp, #0x90] + + # return transfer_t from jump + # pass transfer_t as first arg in context function + # X0 == FCTX, X1 == DATA + mov x0, x4 + + # load pc + ldr x4, [sp, #0xa0] + + # restore stack from GP + FPU + add sp, sp, #0xb0 + + ret x4 +.size jump_fcontext,.-jump_fcontext +# Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/jump_arm64_aapcs_macho_gas.S b/Zend/asm/jump_arm64_aapcs_macho_gas.S new file mode 100644 index 0000000000000..31738f7453147 --- /dev/null +++ b/Zend/asm/jump_arm64_aapcs_macho_gas.S @@ -0,0 +1,109 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | d8 | d9 | d10 | d11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | d12 | d13 | d14 | d15 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * | x19 | x20 | x21 | x22 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | x23 | x24 | x25 | x26 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| * + * ------------------------------------------------- * + * | x27 | x28 | FP | LR | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | | | * + * ------------------------------------------------- * + * | 0xa0| 0xa4| 0xa8| 0xac| | | * + * ------------------------------------------------- * + * | PC | align | | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.text +.globl _jump_fcontext +.balign 16 +_jump_fcontext: + ; prepare stack for GP + FPU + sub sp, sp, #0xb0 + + ; save d8 - d15 + stp d8, d9, [sp, #0x00] + stp d10, d11, [sp, #0x10] + stp d12, d13, [sp, #0x20] + stp d14, d15, [sp, #0x30] + + ; save x19-x30 + stp x19, x20, [sp, #0x40] + stp x21, x22, [sp, #0x50] + stp x23, x24, [sp, #0x60] + stp x25, x26, [sp, #0x70] + stp x27, x28, [sp, #0x80] + stp fp, lr, [sp, #0x90] + + ; save LR as PC + str lr, [sp, #0xa0] + + ; store RSP (pointing to context-data) in X0 + mov x4, sp + + ; restore RSP (pointing to context-data) from X1 + mov sp, x0 + + ; load d8 - d15 + ldp d8, d9, [sp, #0x00] + ldp d10, d11, [sp, #0x10] + ldp d12, d13, [sp, #0x20] + ldp d14, d15, [sp, #0x30] + + ; load x19-x30 + ldp x19, x20, [sp, #0x40] + ldp x21, x22, [sp, #0x50] + ldp x23, x24, [sp, #0x60] + ldp x25, x26, [sp, #0x70] + ldp x27, x28, [sp, #0x80] + ldp fp, lr, [sp, #0x90] + + ; return transfer_t from jump + ; pass transfer_t as first arg in context function + ; X0 == FCTX, X1 == DATA + mov x0, x4 + + ; load pc + ldr x4, [sp, #0xa0] + + ; restore stack from GP + FPU + add sp, sp, #0xb0 + + ret x4 diff --git a/Zend/asm/jump_arm_aapcs_elf_gas.S b/Zend/asm/jump_arm_aapcs_elf_gas.S new file mode 100644 index 0000000000000..86efe9d821463 --- /dev/null +++ b/Zend/asm/jump_arm_aapcs_elf_gas.S @@ -0,0 +1,88 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * |hiddn| v1 | v2 | v3 | v4 | v5 | v6 | v7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | v8 | lr | pc | FCTX| DATA| | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.file "jump_arm_aapcs_elf_gas.S" +.text +.globl jump_fcontext +.align 2 +.type jump_fcontext,%function +.syntax unified +jump_fcontext: + @ save LR as PC + push {lr} + @ save hidden,V1-V8,LR + push {a1,v1-v8,lr} + + @ prepare stack for FPU + sub sp, sp, #64 +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) + @ save S16-S31 + vstmia sp, {d8-d15} +#endif + + @ store RSP (pointing to context-data) in A1 + mov a1, sp + + @ restore RSP (pointing to context-data) from A2 + mov sp, a2 + +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) + @ restore S16-S31 + vldmia sp, {d8-d15} +#endif + @ prepare stack for FPU + add sp, sp, #64 + + @ restore hidden,V1-V8,LR + pop {a4,v1-v8,lr} + + @ return transfer_t from jump + str a1, [a4, #0] + str a3, [a4, #4] + @ pass transfer_t as first arg in context function + @ A1 == FCTX, A2 == DATA + mov a2, a3 + + @ restore PC + pop {pc} +.size jump_fcontext,.-jump_fcontext + +@ Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/jump_arm_aapcs_macho_gas.S b/Zend/asm/jump_arm_aapcs_macho_gas.S new file mode 100644 index 0000000000000..8edd0d7de79de --- /dev/null +++ b/Zend/asm/jump_arm_aapcs_macho_gas.S @@ -0,0 +1,95 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | sjlj|hiddn| v1 | v2 | v3 | v4 | v5 | v6 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | v7 | v8 | lr | pc | FCTX| DATA| | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.text +.globl _jump_fcontext +.align 2 +_jump_fcontext: + @ save LR as PC + push {lr} + @ save hidden,V1-V8,LR + push {a1,v1-v8,lr} + + @ locate TLS to save/restore SjLj handler + mrc p15, 0, v2, c13, c0, #3 + bic v2, v2, #3 + + @ load TLS[__PTK_LIBC_DYLD_Unwind_SjLj_Key] + ldr v1, [v2, #8] + @ save SjLj handler + push {v1} + + @ prepare stack for FPU + sub sp, sp, #64 +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) + @ save S16-S31 + vstmia sp, {d8-d15} +#endif + + @ store RSP (pointing to context-data) in A1 + mov a1, sp + + @ restore RSP (pointing to context-data) from A2 + mov sp, a2 + +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) + @ restore S16-S31 + vldmia sp, {d8-d15} +#endif + @ prepare stack for FPU + add sp, sp, #64 + + @ r#estore SjLj handler + pop {v1} + @ store SjLj handler in TLS + str v1, [v2, #8] + + @ restore hidden,V1-V8,LR + pop {a4,v1-v8,lr} + + @ return transfer_t from jump + str a1, [a4, #0] + str a3, [a4, #4] + @ pass transfer_t as first arg in context function + @ A1 == FCTX, A2 == DATA + mov a2, a3 + + @ restore PC + pop {pc} diff --git a/Zend/asm/jump_combined_sysv_macho_gas.S b/Zend/asm/jump_combined_sysv_macho_gas.S new file mode 100644 index 0000000000000..34a32f785f281 --- /dev/null +++ b/Zend/asm/jump_combined_sysv_macho_gas.S @@ -0,0 +1,24 @@ +/* + Copyright Sergue E. Leontiev 2013. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +// Stub file for universal binary + +#if defined(__i386__) + #include "jump_i386_sysv_macho_gas.S" +#elif defined(__x86_64__) + #include "jump_x86_64_sysv_macho_gas.S" +#elif defined(__ppc__) + #include "jump_ppc32_sysv_macho_gas.S" +#elif defined(__ppc64__) + #include "jump_ppc64_sysv_macho_gas.S" +#elif defined(__arm__) + #include "jump_arm_aapcs_macho_gas.S" +#elif defined(__arm64__) + #include "jump_arm64_aapcs_macho_gas.S" +#else + #error "No arch's" +#endif diff --git a/Zend/asm/jump_i386_ms_pe_masm.asm b/Zend/asm/jump_i386_ms_pe_masm.asm new file mode 100644 index 0000000000000..7a9e848f1c3f4 --- /dev/null +++ b/Zend/asm/jump_i386_ms_pe_masm.asm @@ -0,0 +1,116 @@ + +; Copyright Oliver Kowalke 2009. +; Distributed under the Boost Software License, Version 1.0. +; (See accompanying file LICENSE_1_0.txt or copy at +; http://www.boost.org/LICENSE_1_0.txt) + +; --------------------------------------------------------------------------------- +; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +; --------------------------------------------------------------------------------- +; | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | +; --------------------------------------------------------------------------------- +; | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | +; --------------------------------------------------------------------------------- +; --------------------------------------------------------------------------------- +; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | +; --------------------------------------------------------------------------------- +; | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | +; --------------------------------------------------------------------------------- +; | ESI | EBX | EBP | EIP | to | data | EH NXT |SEH HNDLR| +; --------------------------------------------------------------------------------- + +.386 +.XMM +.model flat, c +.code + +jump_fcontext PROC BOOST_CONTEXT_EXPORT + ; prepare stack + lea esp, [esp-02ch] + +IFNDEF BOOST_USE_TSX + ; save MMX control- and status-word + stmxcsr [esp] + ; save x87 control-word + fnstcw [esp+04h] +ENDIF + + assume fs:nothing + ; load NT_TIB into ECX + mov edx, fs:[018h] + assume fs:error + ; load fiber local storage + mov eax, [edx+010h] + mov [esp+08h], eax + ; load current deallocation stack + mov eax, [edx+0e0ch] + mov [esp+0ch], eax + ; load current stack limit + mov eax, [edx+08h] + mov [esp+010h], eax + ; load current stack base + mov eax, [edx+04h] + mov [esp+014h], eax + ; load current SEH exception list + mov eax, [edx] + mov [esp+018h], eax + + mov [esp+01ch], edi ; save EDI + mov [esp+020h], esi ; save ESI + mov [esp+024h], ebx ; save EBX + mov [esp+028h], ebp ; save EBP + + ; store ESP (pointing to context-data) in EAX + mov eax, esp + + ; firstarg of jump_fcontext() == fcontext to jump to + mov ecx, [esp+030h] + + ; restore ESP (pointing to context-data) from ECX + mov esp, ecx + +IFNDEF BOOST_USE_TSX + ; restore MMX control- and status-word + ldmxcsr [esp] + ; restore x87 control-word + fldcw [esp+04h] +ENDIF + + assume fs:nothing + ; load NT_TIB into EDX + mov edx, fs:[018h] + assume fs:error + ; restore fiber local storage + mov ecx, [esp+08h] + mov [edx+010h], ecx + ; restore current deallocation stack + mov ecx, [esp+0ch] + mov [edx+0e0ch], ecx + ; restore current stack limit + mov ecx, [esp+010h] + mov [edx+08h], ecx + ; restore current stack base + mov ecx, [esp+014h] + mov [edx+04h], ecx + ; restore current SEH exception list + mov ecx, [esp+018h] + mov [edx], ecx + + mov ecx, [esp+02ch] ; restore EIP + + mov edi, [esp+01ch] ; restore EDI + mov esi, [esp+020h] ; restore ESI + mov ebx, [esp+024h] ; restore EBX + mov ebp, [esp+028h] ; restore EBP + + ; prepare stack + lea esp, [esp+030h] + + ; return transfer_t + ; FCTX == EAX, DATA == EDX + mov edx, [eax+034h] + + ; jump to context + jmp ecx +jump_fcontext ENDP +END diff --git a/Zend/asm/jump_i386_sysv_elf_gas.S b/Zend/asm/jump_i386_sysv_elf_gas.S new file mode 100644 index 0000000000000..b96d4b5c0e70e --- /dev/null +++ b/Zend/asm/jump_i386_sysv_elf_gas.S @@ -0,0 +1,83 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | hidden | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | | * + * ---------------------------------------------------------------------------------- * + * | to | data | | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.file "jump_i386_sysv_elf_gas.S" +.text +.globl jump_fcontext +.align 2 +.type jump_fcontext,@function +jump_fcontext: + leal -0x18(%esp), %esp /* prepare stack */ + +#if !defined(BOOST_USE_TSX) + stmxcsr (%esp) /* save MMX control- and status-word */ + fnstcw 0x4(%esp) /* save x87 control-word */ +#endif + + movl %edi, 0x8(%esp) /* save EDI */ + movl %esi, 0xc(%esp) /* save ESI */ + movl %ebx, 0x10(%esp) /* save EBX */ + movl %ebp, 0x14(%esp) /* save EBP */ + + /* store ESP (pointing to context-data) in ECX */ + movl %esp, %ecx + + /* first arg of jump_fcontext() == fcontext to jump to */ + movl 0x20(%esp), %eax + + /* second arg of jump_fcontext() == data to be transferred */ + movl 0x24(%esp), %edx + + /* restore ESP (pointing to context-data) from EAX */ + movl %eax, %esp + + /* address of returned transport_t */ + movl 0x1c(%esp), %eax + /* return parent fcontext_t */ + movl %ecx, (%eax) + /* return data */ + movl %edx, 0x4(%eax) + + movl 0x18(%esp), %ecx /* restore EIP */ + +#if !defined(BOOST_USE_TSX) + ldmxcsr (%esp) /* restore MMX control- and status-word */ + fldcw 0x4(%esp) /* restore x87 control-word */ +#endif + + movl 0x8(%esp), %edi /* restore EDI */ + movl 0xc(%esp), %esi /* restore ESI */ + movl 0x10(%esp), %ebx /* restore EBX */ + movl 0x14(%esp), %ebp /* restore EBP */ + + leal 0x20(%esp), %esp /* prepare stack */ + + /* jump to context */ + jmp *%ecx +.size jump_fcontext,.-jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/jump_i386_sysv_macho_gas.S b/Zend/asm/jump_i386_sysv_macho_gas.S new file mode 100644 index 0000000000000..8ab7c6f29c662 --- /dev/null +++ b/Zend/asm/jump_i386_sysv_macho_gas.S @@ -0,0 +1,74 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | to | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | | * + * ---------------------------------------------------------------------------------- * + * | data | | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.text +.globl _jump_fcontext +.align 2 +_jump_fcontext: + leal -0x18(%esp), %esp /* prepare stack */ + +#if !defined(BOOST_USE_TSX) + stmxcsr (%esp) /* save MMX control- and status-word */ + fnstcw 0x4(%esp) /* save x87 control-word */ +#endif + + movl %edi, 0x8(%esp) /* save EDI */ + movl %esi, 0xc(%esp) /* save ESI */ + movl %ebx, 0x10(%esp) /* save EBX */ + movl %ebp, 0x14(%esp) /* save EBP */ + + /* store ESP (pointing to context-data) in ECX */ + movl %esp, %ecx + + /* first arg of jump_fcontext() == fcontext to jump to */ + movl 0x1c(%esp), %eax + + /* second arg of jump_fcontext() == data to be transferred */ + movl 0x20(%esp), %edx + + /* restore ESP (pointing to context-data) from EAX */ + movl %eax, %esp + + /* return parent fcontext_t */ + movl %ecx, %eax + /* returned data is stored in EDX */ + + movl 0x18(%esp), %ecx /* restore EIP */ + +#if !defined(BOOST_USE_TSX) + ldmxcsr (%esp) /* restore MMX control- and status-word */ + fldcw 0x4(%esp) /* restore x87 control-word */ +#endif + + movl 0x8(%esp), %edi /* restore EDI */ + movl 0xc(%esp), %esi /* restore ESI */ + movl 0x10(%esp), %ebx /* restore EBX */ + movl 0x14(%esp), %ebp /* restore EBP */ + + leal 0x1c(%esp), %esp /* prepare stack */ + + /* jump to context */ + jmp *%ecx diff --git a/Zend/asm/jump_mips32_o32_elf_gas.S b/Zend/asm/jump_mips32_o32_elf_gas.S new file mode 100644 index 0000000000000..f2b8034d8c5b3 --- /dev/null +++ b/Zend/asm/jump_mips32_o32_elf_gas.S @@ -0,0 +1,119 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | F20 | F22 | F24 | F26 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | F28 | F30 | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | FP |hiddn| RA | PC | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | ABI ARGS | GP | FCTX| DATA| | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "jump_mips32_o32_elf_gas.S" +.text +.globl jump_fcontext +.align 2 +.type jump_fcontext,@function +.ent jump_fcontext +jump_fcontext: + # reserve space on stack + addiu $sp, $sp, -96 + + sw $s0, 48($sp) # save S0 + sw $s1, 52($sp) # save S1 + sw $s2, 56($sp) # save S2 + sw $s3, 60($sp) # save S3 + sw $s4, 64($sp) # save S4 + sw $s5, 68($sp) # save S5 + sw $s6, 72($sp) # save S6 + sw $s7, 76($sp) # save S7 + sw $fp, 80($sp) # save FP + sw $a0, 84($sp) # save hidden, address of returned transfer_t + sw $ra, 88($sp) # save RA + sw $ra, 92($sp) # save RA as PC + +#if defined(__mips_hard_float) + s.d $f20, ($sp) # save F20 + s.d $f22, 8($sp) # save F22 + s.d $f24, 16($sp) # save F24 + s.d $f26, 24($sp) # save F26 + s.d $f28, 32($sp) # save F28 + s.d $f30, 40($sp) # save F30 +#endif + + # store SP (pointing to context-data) in A0 + move $a0, $sp + + # restore SP (pointing to context-data) from A1 + move $sp, $a1 + +#if defined(__mips_hard_float) + l.d $f20, ($sp) # restore F20 + l.d $f22, 8($sp) # restore F22 + l.d $f24, 16($sp) # restore F24 + l.d $f26, 24($sp) # restore F26 + l.d $f28, 32($sp) # restore F28 + l.d $f30, 40($sp) # restore F30 +#endif + + lw $s0, 48($sp) # restore S0 + lw $s1, 52($sp) # restore S1 + lw $s2, 56($sp) # restore S2 + lw $s3, 60($sp) # restore S3 + lw $s4, 64($sp) # restore S4 + lw $s5, 68($sp) # restore S5 + lw $s6, 72($sp) # restore S6 + lw $s7, 76($sp) # restore S7 + lw $fp, 80($sp) # restore FP + lw $v0, 84($sp) # restore hidden, address of returned transfer_t + lw $ra, 88($sp) # restore RA + + # load PC + lw $t9, 92($sp) + + # adjust stack + addiu $sp, $sp, 96 + + # return transfer_t from jump + sw $a0, ($v0) # fctx of transfer_t + sw $a2, 4($v0) # data of transfer_t + # pass transfer_t as first arg in context function + # A0 == fctx, A1 == data + move $a1, $a2 + + # jump to context + jr $t9 +.end jump_fcontext +.size jump_fcontext, .-jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/jump_mips64_n64_elf_gas.S b/Zend/asm/jump_mips64_n64_elf_gas.S new file mode 100644 index 0000000000000..e338912bb6e86 --- /dev/null +++ b/Zend/asm/jump_mips64_n64_elf_gas.S @@ -0,0 +1,124 @@ +/* + Copyright Jiaxun Yang 2018. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | F24 | F25 | F26 | F27 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | F28 | F29 | F30 | F31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | FP | GP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "jump_mips64_n64_elf_gas.S" +.text +.globl jump_fcontext +.align 3 +.type jump_fcontext,@function +.ent jump_fcontext +jump_fcontext: + # reserve space on stack + daddiu $sp, $sp, -160 + + sd $s0, 64($sp) # save S0 + sd $s1, 72($sp) # save S1 + sd $s2, 80($sp) # save S2 + sd $s3, 88($sp) # save S3 + sd $s4, 96($sp) # save S4 + sd $s5, 104($sp) # save S5 + sd $s6, 112($sp) # save S6 + sd $s7, 120($sp) # save S7 + sd $fp, 128($sp) # save FP + sd $ra, 144($sp) # save RA + sd $ra, 152($sp) # save RA as PC + +#if defined(__mips_hard_float) + s.d $f24, 0($sp) # save F24 + s.d $f25, 8($sp) # save F25 + s.d $f26, 16($sp) # save F26 + s.d $f27, 24($sp) # save F27 + s.d $f28, 32($sp) # save F28 + s.d $f29, 40($sp) # save F29 + s.d $f30, 48($sp) # save F30 + s.d $f31, 56($sp) # save F31 +#endif + + # store SP (pointing to old context-data) in v0 as return + move $v0, $sp + + # get SP (pointing to new context-data) from a0 param + move $sp, $a0 + +#if defined(__mips_hard_float) + l.d $f24, 0($sp) # restore F24 + l.d $f25, 8($sp) # restore F25 + l.d $f26, 16($sp) # restore F26 + l.d $f27, 24($sp) # restore F27 + l.d $f28, 32($sp) # restore F28 + l.d $f29, 40($sp) # restore F29 + l.d $f30, 48($sp) # restore F30 + l.d $f31, 56($sp) # restore F31 +#endif + + ld $s0, 64($sp) # restore S0 + ld $s1, 72($sp) # restore S1 + ld $s2, 80($sp) # restore S2 + ld $s3, 88($sp) # restore S3 + ld $s4, 96($sp) # restore S4 + ld $s5, 104($sp) # restore S5 + ld $s6, 112($sp) # restore S6 + ld $s7, 120($sp) # restore S7 + ld $fp, 128($sp) # restore FP + ld $ra, 144($sp) # restore RAa + + # load PC + ld $t9, 152($sp) + + # adjust stack + daddiu $sp, $sp, 160 + + move $a0, $v0 # move old sp from v0 to a0 as param + move $v1, $a1 # move *data from a1 to v1 as return + + # jump to context + jr $t9 +.end jump_fcontext +.size jump_fcontext, .-jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/jump_ppc32_sysv_elf_gas.S b/Zend/asm/jump_ppc32_sysv_elf_gas.S new file mode 100644 index 0000000000000..48e09c935e096 --- /dev/null +++ b/Zend/asm/jump_ppc32_sysv_elf_gas.S @@ -0,0 +1,201 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * |bchai|hiddn| fpscr | PC | CR | R14 | R15 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R16 | R17 | R18 | R19 | R20 | R21 | R22 | R23 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R24 | R25 | R26 | R27 | R28 | R29 | R30 | R31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | F14 | F15 | F16 | F17 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | F18 | F19 | F20 | F21 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | F22 | F23 | F24 | F25 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | F26 | F27 | F28 | F29 | * + * ------------------------------------------------- * + * ------------------------|------------ * + * | 224 | 228 | 232 | 236 | 240 | 244 | * + * ------------------------|------------ * + * | F30 | F31 |bchai| LR | * + * ------------------------|------------ * + * * + *******************************************************/ + +.file "jump_ppc32_sysv_elf_gas.S" +.text +.globl jump_fcontext +.align 2 +.type jump_fcontext,@function +jump_fcontext: + # Linux: jump_fcontext( hidden transfer_t * R3, R4, R5) + # Other: transfer_t R3:R4 = jump_fcontext( R3, R4) + + mflr %r0 # return address from LR + mffs %f0 # FPSCR + mfcr %r8 # condition register + + stwu %r1, -240(%r1) # allocate stack space, R1 % 16 == 0 + stw %r0, 244(%r1) # save LR in caller's frame + +#ifdef __linux__ + stw %r3, 4(%r1) # hidden pointer +#endif + + stfd %f0, 8(%r1) # FPSCR + stw %r0, 16(%r1) # LR as PC + stw %r8, 20(%r1) # CR + + # Save registers R14 to R31. + # Don't change R2, the thread-local storage pointer. + # Don't change R13, the small data pointer. + stw %r14, 24(%r1) + stw %r15, 28(%r1) + stw %r16, 32(%r1) + stw %r17, 36(%r1) + stw %r18, 40(%r1) + stw %r19, 44(%r1) + stw %r20, 48(%r1) + stw %r21, 52(%r1) + stw %r22, 56(%r1) + stw %r23, 60(%r1) + stw %r24, 64(%r1) + stw %r25, 68(%r1) + stw %r26, 72(%r1) + stw %r27, 76(%r1) + stw %r28, 80(%r1) + stw %r29, 84(%r1) + stw %r30, 88(%r1) + stw %r31, 92(%r1) + + # Save registers F14 to F31 in slots with 8-byte alignment. + # 4-byte alignment may stall the pipeline of some processors. + # Less than 4 may cause alignment traps. + stfd %f14, 96(%r1) + stfd %f15, 104(%r1) + stfd %f16, 112(%r1) + stfd %f17, 120(%r1) + stfd %f18, 128(%r1) + stfd %f19, 136(%r1) + stfd %f20, 144(%r1) + stfd %f21, 152(%r1) + stfd %f22, 160(%r1) + stfd %f23, 168(%r1) + stfd %f24, 176(%r1) + stfd %f25, 184(%r1) + stfd %f26, 192(%r1) + stfd %f27, 200(%r1) + stfd %f28, 208(%r1) + stfd %f29, 216(%r1) + stfd %f30, 224(%r1) + stfd %f31, 232(%r1) + + # store RSP (pointing to context-data) in R7/R6 + # restore RSP (pointing to context-data) from R4/R3 +#ifdef __linux__ + mr %r7, %r1 + mr %r1, %r4 + lwz %r3, 4(%r1) # hidden pointer +#else + mr %r6, %r1 + mr %r1, %r3 +#endif + + lfd %f0, 8(%r1) # FPSCR + lwz %r0, 16(%r1) # PC + lwz %r8, 20(%r1) # CR + + mtfsf 0xff, %f0 # restore FPSCR + mtctr %r0 # load CTR with PC + mtcr %r8 # restore CR + + # restore R14 to R31 + lwz %r14, 24(%r1) + lwz %r15, 28(%r1) + lwz %r16, 32(%r1) + lwz %r17, 36(%r1) + lwz %r18, 40(%r1) + lwz %r19, 44(%r1) + lwz %r20, 48(%r1) + lwz %r21, 52(%r1) + lwz %r22, 56(%r1) + lwz %r23, 60(%r1) + lwz %r24, 64(%r1) + lwz %r25, 68(%r1) + lwz %r26, 72(%r1) + lwz %r27, 76(%r1) + lwz %r28, 80(%r1) + lwz %r29, 84(%r1) + lwz %r30, 88(%r1) + lwz %r31, 92(%r1) + + # restore F14 to F31 + lfd %f14, 96(%r1) + lfd %f15, 104(%r1) + lfd %f16, 112(%r1) + lfd %f17, 120(%r1) + lfd %f18, 128(%r1) + lfd %f19, 136(%r1) + lfd %f20, 144(%r1) + lfd %f21, 152(%r1) + lfd %f22, 160(%r1) + lfd %f23, 168(%r1) + lfd %f24, 176(%r1) + lfd %f25, 184(%r1) + lfd %f26, 192(%r1) + lfd %f27, 200(%r1) + lfd %f28, 208(%r1) + lfd %f29, 216(%r1) + lfd %f30, 224(%r1) + lfd %f31, 232(%r1) + + # restore LR from caller's frame + lwz %r0, 244(%r1) + mtlr %r0 + + # adjust stack + addi %r1, %r1, 240 + + # return transfer_t +#ifdef __linux__ + stw %r7, 0(%r3) + stw %r5, 4(%r3) +#else + mr %r3, %r6 + # %r4, %r4 +#endif + + # jump to context + bctr +.size jump_fcontext, .-jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/jump_ppc32_sysv_macho_gas.S b/Zend/asm/jump_ppc32_sysv_macho_gas.S new file mode 100644 index 0000000000000..c555237afa2e9 --- /dev/null +++ b/Zend/asm/jump_ppc32_sysv_macho_gas.S @@ -0,0 +1,201 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/****************************************************** + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | F14 | F15 | F16 | F17 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | F18 | F19 | F20 | F21 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | F22 | F23 | F24 | F25 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | F26 | F27 | F28 | F29 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | F30 | F31 | fpscr | R13 | R14 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | R31 |hiddn| CR | LR | PC |bchai|linkr| FCTX| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 64 | | * + * ------------------------------------------------- * + * | 256 | | * + * ------------------------------------------------- * + * | DATA| | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.text +.globl _jump_fcontext +.align 2 +_jump_fcontext: + ; reserve space on stack + subi r1, r1, 244 + + stfd f14, 0(r1) # save F14 + stfd f15, 8(r1) # save F15 + stfd f16, 16(r1) # save F16 + stfd f17, 24(r1) # save F17 + stfd f18, 32(r1) # save F18 + stfd f19, 40(r1) # save F19 + stfd f20, 48(r1) # save F20 + stfd f21, 56(r1) # save F21 + stfd f22, 64(r1) # save F22 + stfd f23, 72(r1) # save F23 + stfd f24, 80(r1) # save F24 + stfd f25, 88(r1) # save F25 + stfd f26, 96(r1) # save F26 + stfd f27, 104(r1) # save F27 + stfd f28, 112(r1) # save F28 + stfd f29, 120(r1) # save F29 + stfd f30, 128(r1) # save F30 + stfd f31, 136(r1) # save F31 + mffs f0 # load FPSCR + stfd f0, 144(r1) # save FPSCR + + stw r13, 152(r1) # save R13 + stw r14, 156(r1) # save R14 + stw r15, 160(r1) # save R15 + stw r16, 164(r1) # save R16 + stw r17, 168(r1) # save R17 + stw r18, 172(r1) # save R18 + stw r19, 176(r1) # save R19 + stw r20, 180(r1) # save R20 + stw r21, 184(r1) # save R21 + stw r22, 188(r1) # save R22 + stw r23, 192(r1) # save R23 + stw r24, 196(r1) # save R24 + stw r25, 200(r1) # save R25 + stw r26, 204(r1) # save R26 + stw r27, 208(r1) # save R27 + stw r28, 212(r1) # save R28 + stw r29, 216(r1) # save R29 + stw r30, 220(r1) # save R30 + stw r31, 224(r1) # save R31 + stw r3, 228(r1) # save hidden + + # save CR + mfcr r0 + stw r0, 232(r1) + # save LR + mflr r0 + stw r0, 236(r1) + # save LR as PC + stw r0, 240(r1) + + # store RSP (pointing to context-data) in R6 + mr r6, r1 + + # restore RSP (pointing to context-data) from R4 + mr r1, r4 + + lfd f14, 0(r1) # restore F14 + lfd f15, 8(r1) # restore F15 + lfd f16, 16(r1) # restore F16 + lfd f17, 24(r1) # restore F17 + lfd f18, 32(r1) # restore F18 + lfd f19, 40(r1) # restore F19 + lfd f20, 48(r1) # restore F20 + lfd f21, 56(r1) # restore F21 + lfd f22, 64(r1) # restore F22 + lfd f23, 72(r1) # restore F23 + lfd f24, 80(r1) # restore F24 + lfd f25, 88(r1) # restore F25 + lfd f26, 96(r1) # restore F26 + lfd f27, 104(r1) # restore F27 + lfd f28, 112(r1) # restore F28 + lfd f29, 120(r1) # restore F29 + lfd f30, 128(r1) # restore F30 + lfd f31, 136(r1) # restore F31 + lfd f0, 144(r1) # load FPSCR + mtfsf 0xff, f0 # restore FPSCR + + lwz r13, 152(r1) # restore R13 + lwz r14, 156(r1) # restore R14 + lwz r15, 160(r1) # restore R15 + lwz r16, 164(r1) # restore R16 + lwz r17, 168(r1) # restore R17 + lwz r18, 172(r1) # restore R18 + lwz r19, 176(r1) # restore R19 + lwz r20, 180(r1) # restore R20 + lwz r21, 184(r1) # restore R21 + lwz r22, 188(r1) # restore R22 + lwz r23, 192(r1) # restore R23 + lwz r24, 196(r1) # restore R24 + lwz r25, 200(r1) # restore R25 + lwz r26, 204(r1) # restore R26 + lwz r27, 208(r1) # restore R27 + lwz r28, 212(r1) # restore R28 + lwz r29, 216(r1) # restore R29 + lwz r30, 220(r1) # restore R30 + lwz r31, 224(r1) # restore R31 + lwz r3, 228(r1) # restore hidden + + # restore CR + lwz r0, 232(r1) + mtcr r0 + # restore LR + lwz r0, 236(r1) + mtlr r0 + # load PC + lwz r0, 240(r1) + # restore CTR + mtctr r0 + + # adjust stack + addi r1, r1, 244 + + # return transfer_t + stw r6, 0(r3) + stw r5, 4(r3) + + # jump to context + bctr diff --git a/Zend/asm/jump_ppc64_sysv_elf_gas.S b/Zend/asm/jump_ppc64_sysv_elf_gas.S new file mode 100644 index 0000000000000..28907db32b8c3 --- /dev/null +++ b/Zend/asm/jump_ppc64_sysv_elf_gas.S @@ -0,0 +1,221 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | TOC | R14 | R15 | R16 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R17 | R18 | R19 | R20 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R21 | R22 | R23 | R24 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | R25 | R26 | R27 | R28 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | R29 | R30 | R31 | hidden | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | CR | LR | PC | back-chain| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | cr saved | lr saved | compiler | linker | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | TOC saved | FCTX | DATA | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.file "jump_ppc64_sysv_elf_gas.S" +.globl jump_fcontext +#if _CALL_ELF == 2 + .text + .align 2 +jump_fcontext: + addis %r2, %r12, .TOC.-jump_fcontext@ha + addi %r2, %r2, .TOC.-jump_fcontext@l + .localentry jump_fcontext, . - jump_fcontext +#else + .section ".opd","aw" + .align 3 +jump_fcontext: +# ifdef _CALL_LINUX + .quad .L.jump_fcontext,.TOC.@tocbase,0 + .type jump_fcontext,@function + .text + .align 2 +.L.jump_fcontext: +# else + .hidden .jump_fcontext + .globl .jump_fcontext + .quad .jump_fcontext,.TOC.@tocbase,0 + .size jump_fcontext,24 + .type .jump_fcontext,@function + .text + .align 2 +.jump_fcontext: +# endif +#endif + # reserve space on stack + subi %r1, %r1, 184 + +#if _CALL_ELF != 2 + std %r2, 0(%r1) # save TOC +#endif + std %r14, 8(%r1) # save R14 + std %r15, 16(%r1) # save R15 + std %r16, 24(%r1) # save R16 + std %r17, 32(%r1) # save R17 + std %r18, 40(%r1) # save R18 + std %r19, 48(%r1) # save R19 + std %r20, 56(%r1) # save R20 + std %r21, 64(%r1) # save R21 + std %r22, 72(%r1) # save R22 + std %r23, 80(%r1) # save R23 + std %r24, 88(%r1) # save R24 + std %r25, 96(%r1) # save R25 + std %r26, 104(%r1) # save R26 + std %r27, 112(%r1) # save R27 + std %r28, 120(%r1) # save R28 + std %r29, 128(%r1) # save R29 + std %r30, 136(%r1) # save R30 + std %r31, 144(%r1) # save R31 +#if _CALL_ELF != 2 + std %r3, 152(%r1) # save hidden +#endif + + # save CR + mfcr %r0 + std %r0, 160(%r1) + # save LR + mflr %r0 + std %r0, 168(%r1) + # save LR as PC + std %r0, 176(%r1) + + # store RSP (pointing to context-data) in R6 + mr %r6, %r1 + +#if _CALL_ELF == 2 + # restore RSP (pointing to context-data) from R3 + mr %r1, %r3 +#else + # restore RSP (pointing to context-data) from R4 + mr %r1, %r4 + + ld %r2, 0(%r1) # restore TOC +#endif + ld %r14, 8(%r1) # restore R14 + ld %r15, 16(%r1) # restore R15 + ld %r16, 24(%r1) # restore R16 + ld %r17, 32(%r1) # restore R17 + ld %r18, 40(%r1) # restore R18 + ld %r19, 48(%r1) # restore R19 + ld %r20, 56(%r1) # restore R20 + ld %r21, 64(%r1) # restore R21 + ld %r22, 72(%r1) # restore R22 + ld %r23, 80(%r1) # restore R23 + ld %r24, 88(%r1) # restore R24 + ld %r25, 96(%r1) # restore R25 + ld %r26, 104(%r1) # restore R26 + ld %r27, 112(%r1) # restore R27 + ld %r28, 120(%r1) # restore R28 + ld %r29, 128(%r1) # restore R29 + ld %r30, 136(%r1) # restore R30 + ld %r31, 144(%r1) # restore R31 +#if _CALL_ELF != 2 + ld %r3, 152(%r1) # restore hidden +#endif + + # restore CR + ld %r0, 160(%r1) + mtcr %r0 + # restore LR + ld %r0, 168(%r1) + mtlr %r0 + + # load PC + ld %r12, 176(%r1) + # restore CTR + mtctr %r12 + + # adjust stack + addi %r1, %r1, 184 + +#if _CALL_ELF == 2 + # copy transfer_t into transfer_fn arg registers + mr %r3, %r6 + # arg pointer already in %r4 + + # jump to context + bctr + .size jump_fcontext, .-jump_fcontext +#else + # zero in r3 indicates first jump to context-function + cmpdi %r3, 0 + beq use_entry_arg + + # return transfer_t + std %r6, 0(%r3) + std %r5, 8(%r3) + + # jump to context + bctr + +use_entry_arg: + # copy transfer_t into transfer_fn arg registers + mr %r3, %r6 + mr %r4, %r5 + + # jump to context + bctr +# ifdef _CALL_LINUX + .size .jump_fcontext, .-.L.jump_fcontext +# else + .size .jump_fcontext, .-.jump_fcontext +# endif +#endif + + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/jump_ppc64_sysv_macho_gas.S b/Zend/asm/jump_ppc64_sysv_macho_gas.S new file mode 100644 index 0000000000000..74fcb2ab3528f --- /dev/null +++ b/Zend/asm/jump_ppc64_sysv_macho_gas.S @@ -0,0 +1,164 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | TOC | R14 | R15 | R16 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R17 | R18 | R19 | R20 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R21 | R22 | R23 | R24 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | R25 | R26 | R27 | R28 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | R29 | R30 | R31 | hidden | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | CR | LR | PC | back-chain| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | cr saved | lr saved | compiler | linker | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | TOC saved | FCTX | DATA | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.text +.align 2 +.globl _jump_fcontext + +_jump_fcontext: + ; reserve space on stack + subi r1, r1, 184 + + std r14, 8(r1) ; save R14 + std r15, 16(r1) ; save R15 + std r16, 24(r1) ; save R16 + std r17, 32(r1) ; save R17 + std r18, 40(r1) ; save R18 + std r19, 48(r1) ; save R19 + std r20, 56(r1) ; save R20 + std r21, 64(r1) ; save R21 + std r22, 72(r1) ; save R22 + std r23, 80(r1) ; save R23 + std r24, 88(r1) ; save R24 + std r25, 96(r1) ; save R25 + std r26, 104(r1) ; save R26 + std r27, 112(r1) ; save R27 + std r28, 120(r1) ; save R28 + std r29, 128(r1) ; save R29 + std r30, 136(r1) ; save R30 + std r31, 144(r1) ; save R31 + std r3, 152(r1) ; save hidden + + ; save CR + mfcr r0 + std r0, 160(r1) + ; save LR + mflr r0 + std r0, 168(r1) + ; save LR as PC + std r0, 176(r1) + + ; store RSP (pointing to context-data) in R6 + mr r6, r1 + + ; restore RSP (pointing to context-data) from R4 + mr r1, r4 + + ld r14, 8(r1) ; restore R14 + ld r15, 16(r1) ; restore R15 + ld r16, 24(r1) ; restore R16 + ld r17, 32(r1) ; restore R17 + ld r18, 40(r1) ; restore R18 + ld r19, 48(r1) ; restore R19 + ld r20, 56(r1) ; restore R20 + ld r21, 64(r1) ; restore R21 + ld r22, 72(r1) ; restore R22 + ld r23, 80(r1) ; restore R23 + ld r24, 88(r1) ; restore R24 + ld r25, 96(r1) ; restore R25 + ld r26, 104(r1) ; restore R26 + ld r27, 112(r1) ; restore R27 + ld r28, 120(r1) ; restore R28 + ld r29, 128(r1) ; restore R29 + ld r30, 136(r1) ; restore R30 + ld r31, 144(r1) ; restore R31 + ld r3, 152(r1) ; restore hidden + + ; restore CR + ld r0, 160(r1) + mtcr r0 + ; restore LR + ld r0, 168(r1) + mtlr r0 + + ; load PC + ld r12, 176(r1) + # restore CTR + mtctr r12 + + # adjust stack + addi r1, r1, 184 + + # zero in r3 indicates first jump to context-function + cmpdi r3, 0 + beq use_entry_arg + + # return transfer_t + std r6, 0(r3) + std r5, 8(r3) + + # jump to context + bctr + +use_entry_arg: + # copy transfer_t into transfer_fn arg registers + mr r3, r6 + mr r4, r5 + + # jump to context + bctr diff --git a/Zend/asm/jump_x86_64_ms_pe_masm.asm b/Zend/asm/jump_x86_64_ms_pe_masm.asm new file mode 100644 index 0000000000000..c8a28a558e8bd --- /dev/null +++ b/Zend/asm/jump_x86_64_ms_pe_masm.asm @@ -0,0 +1,205 @@ + +; Copyright Oliver Kowalke 2009. +; Distributed under the Boost Software License, Version 1.0. +; (See accompanying file LICENSE_1_0.txt or copy at +; http://www.boost.org/LICENSE_1_0.txt) + +; ---------------------------------------------------------------------------------- +; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +; ---------------------------------------------------------------------------------- +; | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | +; ---------------------------------------------------------------------------------- +; | SEE registers (XMM6-XMM15) | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | +; ---------------------------------------------------------------------------------- +; | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | +; ---------------------------------------------------------------------------------- +; | SEE registers (XMM6-XMM15) | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | +; ---------------------------------------------------------------------------------- +; | 0xe40 | 0x44 | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | +; ---------------------------------------------------------------------------------- +; | SEE registers (XMM6-XMM15) | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | +; ---------------------------------------------------------------------------------- +; | 0x60 | 0x64 | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | +; ---------------------------------------------------------------------------------- +; | SEE registers (XMM6-XMM15) | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 32 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | +; ---------------------------------------------------------------------------------- +; | 0x80 | 0x84 | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | +; ---------------------------------------------------------------------------------- +; | SEE registers (XMM6-XMM15) | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | +; ---------------------------------------------------------------------------------- +; | 0xa0 | 0xa4 | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | +; ---------------------------------------------------------------------------------- +; | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | +; ---------------------------------------------------------------------------------- +; | 0xc0 | 0xc4 | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | +; ---------------------------------------------------------------------------------- +; | limit | base | R12 | R13 | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | +; ---------------------------------------------------------------------------------- +; | 0xe0 | 0xe4 | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | +; ---------------------------------------------------------------------------------- +; | R14 | R15 | RDI | RSI | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | +; ---------------------------------------------------------------------------------- +; | 0x100 | 0x104 | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | +; ---------------------------------------------------------------------------------- +; | RBX | RBP | hidden | RIP | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | +; ---------------------------------------------------------------------------------- +; | 0x120 | 0x124 | 0x128 | 0x12c | 0x130 | 0x134 | 0x138 | 0x13c | +; ---------------------------------------------------------------------------------- +; | parameter area | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | +; ---------------------------------------------------------------------------------- +; | 0x140 | 0x144 | 0x148 | 0x14c | 0x150 | 0x154 | 0x158 | 0x15c | +; ---------------------------------------------------------------------------------- +; | FCTX | DATA | | +; ---------------------------------------------------------------------------------- + +.code + +jump_fcontext PROC BOOST_CONTEXT_EXPORT FRAME + .endprolog + + ; prepare stack + lea rsp, [rsp-0118h] + +IFNDEF BOOST_USE_TSX + ; save XMM storage + movaps [rsp], xmm6 + movaps [rsp+010h], xmm7 + movaps [rsp+020h], xmm8 + movaps [rsp+030h], xmm9 + movaps [rsp+040h], xmm10 + movaps [rsp+050h], xmm11 + movaps [rsp+060h], xmm12 + movaps [rsp+070h], xmm13 + movaps [rsp+080h], xmm14 + movaps [rsp+090h], xmm15 + ; save MMX control- and status-word + stmxcsr [rsp+0a0h] + ; save x87 control-word + fnstcw [rsp+0a4h] +ENDIF + + ; load NT_TIB + mov r10, gs:[030h] + ; save fiber local storage + mov rax, [r10+020h] + mov [rsp+0b0h], rax + ; save current deallocation stack + mov rax, [r10+01478h] + mov [rsp+0b8h], rax + ; save current stack limit + mov rax, [r10+010h] + mov [rsp+0c0h], rax + ; save current stack base + mov rax, [r10+08h] + mov [rsp+0c8h], rax + + mov [rsp+0d0h], r12 ; save R12 + mov [rsp+0d8h], r13 ; save R13 + mov [rsp+0e0h], r14 ; save R14 + mov [rsp+0e8h], r15 ; save R15 + mov [rsp+0f0h], rdi ; save RDI + mov [rsp+0f8h], rsi ; save RSI + mov [rsp+0100h], rbx ; save RBX + mov [rsp+0108h], rbp ; save RBP + + mov [rsp+0110h], rcx ; save hidden address of transport_t + + ; preserve RSP (pointing to context-data) in R9 + mov r9, rsp + + ; restore RSP (pointing to context-data) from RDX + mov rsp, rdx + +IFNDEF BOOST_USE_TSX + ; restore XMM storage + movaps xmm6, [rsp] + movaps xmm7, [rsp+010h] + movaps xmm8, [rsp+020h] + movaps xmm9, [rsp+030h] + movaps xmm10, [rsp+040h] + movaps xmm11, [rsp+050h] + movaps xmm12, [rsp+060h] + movaps xmm13, [rsp+070h] + movaps xmm14, [rsp+080h] + movaps xmm15, [rsp+090h] + ; restore MMX control- and status-word + ldmxcsr [rsp+0a0h] + ; save x87 control-word + fldcw [rsp+0a4h] +ENDIF + + ; load NT_TIB + mov r10, gs:[030h] + ; restore fiber local storage + mov rax, [rsp+0b0h] + mov [r10+020h], rax + ; restore current deallocation stack + mov rax, [rsp+0b8h] + mov [r10+01478h], rax + ; restore current stack limit + mov rax, [rsp+0c0h] + mov [r10+010h], rax + ; restore current stack base + mov rax, [rsp+0c8h] + mov [r10+08h], rax + + mov r12, [rsp+0d0h] ; restore R12 + mov r13, [rsp+0d8h] ; restore R13 + mov r14, [rsp+0e0h] ; restore R14 + mov r15, [rsp+0e8h] ; restore R15 + mov rdi, [rsp+0f0h] ; restore RDI + mov rsi, [rsp+0f8h] ; restore RSI + mov rbx, [rsp+0100h] ; restore RBX + mov rbp, [rsp+0108h] ; restore RBP + + mov rax, [rsp+0110h] ; restore hidden address of transport_t + + ; prepare stack + lea rsp, [rsp+0118h] + + ; load return-address + pop r10 + + ; transport_t returned in RAX + ; return parent fcontext_t + mov [rax], r9 + ; return data + mov [rax+08h], r8 + + ; transport_t as 1.arg of context-function + mov rcx, rax + + ; indirect jump to context + jmp r10 +jump_fcontext ENDP +END diff --git a/Zend/asm/jump_x86_64_sysv_elf_gas.S b/Zend/asm/jump_x86_64_sysv_elf_gas.S new file mode 100644 index 0000000000000..0c4a1a39eb8b0 --- /dev/null +++ b/Zend/asm/jump_x86_64_sysv_elf_gas.S @@ -0,0 +1,91 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * + * ---------------------------------------------------------------------------------- * + * | R15 | RBX | RBP | RIP | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.file "jump_x86_64_sysv_elf_gas.S" +.text +.globl jump_fcontext +.type jump_fcontext,@function +.align 16 +jump_fcontext: + leaq -0x38(%rsp), %rsp /* prepare stack */ + +#if !defined(BOOST_USE_TSX) + stmxcsr (%rsp) /* save MMX control- and status-word */ + fnstcw 0x4(%rsp) /* save x87 control-word */ +#endif + + movq %r12, 0x8(%rsp) /* save R12 */ + movq %r13, 0x10(%rsp) /* save R13 */ + movq %r14, 0x18(%rsp) /* save R14 */ + movq %r15, 0x20(%rsp) /* save R15 */ + movq %rbx, 0x28(%rsp) /* save RBX */ + movq %rbp, 0x30(%rsp) /* save RBP */ + + /* store RSP (pointing to context-data) in RAX */ + movq %rsp, %rax + + /* restore RSP (pointing to context-data) from RDI */ + movq %rdi, %rsp + + movq 0x38(%rsp), %r8 /* restore return-address */ + +#if !defined(BOOST_USE_TSX) + ldmxcsr (%rsp) /* restore MMX control- and status-word */ + fldcw 0x4(%rsp) /* restore x87 control-word */ +#endif + + movq 0x8(%rsp), %r12 /* restore R12 */ + movq 0x10(%rsp), %r13 /* restore R13 */ + movq 0x18(%rsp), %r14 /* restore R14 */ + movq 0x20(%rsp), %r15 /* restore R15 */ + movq 0x28(%rsp), %rbx /* restore RBX */ + movq 0x30(%rsp), %rbp /* restore RBP */ + + leaq 0x40(%rsp), %rsp /* prepare stack */ + + /* return transfer_t from jump */ +#if !defined(_ILP32) + /* RAX == fctx, RDX == data */ + movq %rsi, %rdx +#else + /* RAX == data:fctx */ + salq $32, %rsi + orq %rsi, %rax +#endif + /* pass transfer_t as first arg in context function */ +#if !defined(_ILP32) + /* RDI == fctx, RSI == data */ +#else + /* RDI == data:fctx */ +#endif + movq %rax, %rdi + + /* indirect jump to context */ + jmp *%r8 +.size jump_fcontext,.-jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/jump_x86_64_sysv_macho_gas.S b/Zend/asm/jump_x86_64_sysv_macho_gas.S new file mode 100644 index 0000000000000..afc3e5c126f9c --- /dev/null +++ b/Zend/asm/jump_x86_64_sysv_macho_gas.S @@ -0,0 +1,75 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * + * ---------------------------------------------------------------------------------- * + * | R15 | RBX | RBP | RIP | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.text +.globl _jump_fcontext +.align 8 +_jump_fcontext: + leaq -0x38(%rsp), %rsp /* prepare stack */ + +#if !defined(BOOST_USE_TSX) + stmxcsr (%rsp) /* save MMX control- and status-word */ + fnstcw 0x4(%rsp) /* save x87 control-word */ +#endif + + movq %r12, 0x8(%rsp) /* save R12 */ + movq %r13, 0x10(%rsp) /* save R13 */ + movq %r14, 0x18(%rsp) /* save R14 */ + movq %r15, 0x20(%rsp) /* save R15 */ + movq %rbx, 0x28(%rsp) /* save RBX */ + movq %rbp, 0x30(%rsp) /* save RBP */ + + /* store RSP (pointing to context-data) in RAX */ + movq %rsp, %rax + + /* restore RSP (pointing to context-data) from RDI */ + movq %rdi, %rsp + + movq 0x38(%rsp), %r8 /* restore return-address */ + +#if !defined(BOOST_USE_TSX) + ldmxcsr (%rsp) /* restore MMX control- and status-word */ + fldcw 0x4(%rsp) /* restore x87 control-word */ +#endif + + movq 0x8(%rsp), %r12 /* restore R12 */ + movq 0x10(%rsp), %r13 /* restore R13 */ + movq 0x18(%rsp), %r14 /* restore R14 */ + movq 0x20(%rsp), %r15 /* restore R15 */ + movq 0x28(%rsp), %rbx /* restore RBX */ + movq 0x30(%rsp), %rbp /* restore RBP */ + + leaq 0x40(%rsp), %rsp /* prepare stack */ + + /* return transfer_t from jump */ + /* RAX == fctx, RDX == data */ + movq %rsi, %rdx + /* pass transfer_t as first arg in context function */ + /* RDI == fctx, RSI == data */ + movq %rax, %rdi + + /* indirect jump to context */ + jmp *%r8 diff --git a/Zend/asm/make_arm64_aapcs_elf_gas.S b/Zend/asm/make_arm64_aapcs_elf_gas.S new file mode 100644 index 0000000000000..66cfb2da17ea8 --- /dev/null +++ b/Zend/asm/make_arm64_aapcs_elf_gas.S @@ -0,0 +1,85 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | d8 | d9 | d10 | d11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | d12 | d13 | d14 | d15 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * | x19 | x20 | x21 | x22 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | x23 | x24 | x25 | x26 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| * + * ------------------------------------------------- * + * | x27 | x28 | FP | LR | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | | | * + * ------------------------------------------------- * + * | 0xa0| 0xa4| 0xa8| 0xac| | | * + * ------------------------------------------------- * + * | PC | align | | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.file "make_arm64_aapcs_elf_gas.S" +.text +.align 2 +.global make_fcontext +.type make_fcontext, %function +make_fcontext: + # shift address in x0 (allocated stack) to lower 16 byte boundary + and x0, x0, ~0xF + + # reserve space for context-data on context-stack + sub x0, x0, #0xb0 + + # third arg of make_fcontext() == address of context-function + # store address as a PC to jump in + str x2, [x0, #0xa0] + + # save address of finish as return-address for context-function + # will be entered after context-function returns (LR register) + adr x1, finish + str x1, [x0, #0x98] + + ret x30 // return pointer to context-data (x0) + +finish: + # exit code is zero + mov x0, #0 + # exit application + bl _exit + +.size make_fcontext,.-make_fcontext +# Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/make_arm64_aapcs_macho_gas.S b/Zend/asm/make_arm64_aapcs_macho_gas.S new file mode 100644 index 0000000000000..b30b1e3e5bbb0 --- /dev/null +++ b/Zend/asm/make_arm64_aapcs_macho_gas.S @@ -0,0 +1,83 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | d8 | d9 | d10 | d11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | d12 | d13 | d14 | d15 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * | x19 | x20 | x21 | x22 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | x23 | x24 | x25 | x26 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| * + * ------------------------------------------------- * + * | x27 | x28 | FP | LR | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | | | * + * ------------------------------------------------- * + * | 0xa0| 0xa4| 0xa8| 0xac| | | * + * ------------------------------------------------- * + * | PC | align | | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.text +.globl _make_fcontext +.balign 16 + +_make_fcontext: + ; shift address in x0 (allocated stack) to lower 16 byte boundary + and x0, x0, ~0xF + + ; reserve space for context-data on context-stack + sub x0, x0, #0xb0 + + ; third arg of make_fcontext() == address of context-function + ; store address as a PC to jump in + str x2, [x0, #0xa0] + + adr x1, finish + + ; save address of finish as return-address for context-function + ; will be entered after context-function returns (LR register) + str x1, [x0, #0x98] + + ret lr ; return pointer to context-data (x0) + +finish: + ; exit code is zero + mov x0, #0 + ; exit application + bl __exit + + diff --git a/Zend/asm/make_arm_aapcs_elf_gas.S b/Zend/asm/make_arm_aapcs_elf_gas.S new file mode 100644 index 0000000000000..98ae64b43f9db --- /dev/null +++ b/Zend/asm/make_arm_aapcs_elf_gas.S @@ -0,0 +1,81 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * |hiddn| v1 | v2 | v3 | v4 | v5 | v6 | v7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | v8 | lr | pc | FCTX| DATA| | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.file "make_arm_aapcs_elf_gas.S" +.text +.globl make_fcontext +.align 2 +.type make_fcontext,%function +.syntax unified +make_fcontext: + @ shift address in A1 to lower 16 byte boundary + bic a1, a1, #15 + + @ reserve space for context-data on context-stack + sub a1, a1, #124 + + @ third arg of make_fcontext() == address of context-function + str a3, [a1, #104] + + @ compute address of returned transfer_t + add a2, a1, #108 + mov a3, a2 + str a3, [a1, #64] + + @ compute abs address of label finish + adr a2, finish + @ save address of finish as return-address for context-function + @ will be entered after context-function returns + str a2, [a1, #100] + +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) +#endif + + bx lr @ return pointer to context-data + +finish: + @ exit code is zero + mov a1, #0 + @ exit application + bl _exit@PLT +.size make_fcontext,.-make_fcontext + +@ Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/make_arm_aapcs_macho_gas.S b/Zend/asm/make_arm_aapcs_macho_gas.S new file mode 100644 index 0000000000000..c909ae9d43a25 --- /dev/null +++ b/Zend/asm/make_arm_aapcs_macho_gas.S @@ -0,0 +1,71 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | sjlj|hiddn| v1 | v2 | v3 | v4 | v5 | v6 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | v7 | v8 | lr | pc | FCTX| DATA| | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.text +.globl _make_fcontext +.align 2 +_make_fcontext: + @ shift address in A1 to lower 16 byte boundary + bic a1, a1, #15 + + @ reserve space for context-data on context-stack + sub a1, a1, #124 + + @ third arg of make_fcontext() == address of context-function + str a3, [a1, #108] + + @ compute address of returned transfer_t + add a2, a1, #112 + mov a3, a2 + str a3, [a1, #68] + + @ compute abs address of label finish + adr a2, finish + @ save address of finish as return-address for context-function + @ will be entered after context-function returns + str a2, [a1, #104] + + bx lr @ return pointer to context-data + +finish: + @ exit code is zero + mov a1, #0 + @ exit application + bl __exit diff --git a/Zend/asm/make_combined_sysv_macho_gas.S b/Zend/asm/make_combined_sysv_macho_gas.S new file mode 100644 index 0000000000000..b22fa7ebe9297 --- /dev/null +++ b/Zend/asm/make_combined_sysv_macho_gas.S @@ -0,0 +1,24 @@ +/* + Copyright Sergue E. Leontiev 2013. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +// Stub file for universal binary + +#if defined(__i386__) + #include "make_i386_sysv_macho_gas.S" +#elif defined(__x86_64__) + #include "make_x86_64_sysv_macho_gas.S" +#elif defined(__ppc__) + #include "make_ppc32_sysv_macho_gas.S" +#elif defined(__ppc64__) + #include "make_ppc64_sysv_macho_gas.S" +#elif defined(__arm__) + #include "make_arm_aapcs_macho_gas.S" +#elif defined(__arm64__) + #include "make_arm64_aapcs_macho_gas.S" +#else + #error "No arch's" +#endif diff --git a/Zend/asm/make_i386_ms_pe_masm.asm b/Zend/asm/make_i386_ms_pe_masm.asm new file mode 100644 index 0000000000000..5246465cb975b --- /dev/null +++ b/Zend/asm/make_i386_ms_pe_masm.asm @@ -0,0 +1,140 @@ + +; Copyright Oliver Kowalke 2009. +; Distributed under the Boost Software License, Version 1.0. +; (See accompanying file LICENSE_1_0.txt or copy at +; http://www.boost.org/LICENSE_1_0.txt) + +; --------------------------------------------------------------------------------- +; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +; --------------------------------------------------------------------------------- +; | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | +; --------------------------------------------------------------------------------- +; | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | +; --------------------------------------------------------------------------------- +; --------------------------------------------------------------------------------- +; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | +; --------------------------------------------------------------------------------- +; | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | +; --------------------------------------------------------------------------------- +; | ESI | EBX | EBP | EIP | to | data | EH NXT |SEH HNDLR| +; --------------------------------------------------------------------------------- + +.386 +.XMM +.model flat, c +; standard C library function +_exit PROTO, value:SDWORD +.code + +make_fcontext PROC BOOST_CONTEXT_EXPORT + ; first arg of make_fcontext() == top of context-stack + mov eax, [esp+04h] + + ; reserve space for first argument of context-function + ; EAX might already point to a 16byte border + lea eax, [eax-08h] + + ; shift address in EAX to lower 16 byte boundary + and eax, -16 + + ; reserve space for context-data on context-stack + ; on context-function entry: (ESP -0x4) % 8 == 0 + ; additional space is required for SEH + lea eax, [eax-040h] + + ; save MMX control- and status-word + stmxcsr [eax] + ; save x87 control-word + fnstcw [eax+04h] + + ; first arg of make_fcontext() == top of context-stack + mov ecx, [esp+04h] + ; save top address of context stack as 'base' + mov [eax+014h], ecx + ; second arg of make_fcontext() == size of context-stack + mov edx, [esp+08h] + ; negate stack size for LEA instruction (== substraction) + neg edx + ; compute bottom address of context stack (limit) + lea ecx, [ecx+edx] + ; save bottom address of context-stack as 'limit' + mov [eax+010h], ecx + ; save bottom address of context-stack as 'dealloction stack' + mov [eax+0ch], ecx + ; set fiber-storage to zero + xor ecx, ecx + mov [eax+08h], ecx + + ; third arg of make_fcontext() == address of context-function + ; stored in EBX + mov ecx, [esp+0ch] + mov [eax+024h], ecx + + ; compute abs address of label trampoline + mov ecx, trampoline + ; save address of trampoline as return-address for context-function + ; will be entered after calling jump_fcontext() first time + mov [eax+02ch], ecx + + ; compute abs address of label finish + mov ecx, finish + ; save address of finish as return-address for context-function in EBP + ; will be entered after context-function returns + mov [eax+028h], ecx + + ; traverse current seh chain to get the last exception handler installed by Windows + ; note that on Windows Server 2008 and 2008 R2, SEHOP is activated by default + ; the exception handler chain is tested for the presence of ntdll.dll!FinalExceptionHandler + ; at its end by RaiseException all seh-handlers are disregarded if not present and the + ; program is aborted + assume fs:nothing + ; load NT_TIB into ECX + mov ecx, fs:[0h] + assume fs:error + +walk: + ; load 'next' member of current SEH into EDX + mov edx, [ecx] + ; test if 'next' of current SEH is last (== 0xffffffff) + inc edx + jz found + dec edx + ; exchange content; ECX contains address of next SEH + xchg edx, ecx + ; inspect next SEH + jmp walk + +found: + ; load 'handler' member of SEH == address of last SEH handler installed by Windows + mov ecx, [ecx+04h] + ; save address in ECX as SEH handler for context + mov [eax+03ch], ecx + ; set ECX to -1 + mov ecx, 0ffffffffh + ; save ECX as next SEH item + mov [eax+038h], ecx + ; load address of next SEH item + lea ecx, [eax+038h] + ; save next SEH + mov [eax+018h], ecx + + ret ; return pointer to context-data + +trampoline: + ; move transport_t for entering context-function + ; FCTX == EAX, DATA == EDX + mov [esp], eax + mov [esp+04h], edx + push ebp + ; jump to context-function + jmp ebx + +finish: + ; exit code is zero + xor eax, eax + mov [esp], eax + ; exit application + call _exit + hlt +make_fcontext ENDP +END diff --git a/Zend/asm/make_i386_sysv_elf_gas.S b/Zend/asm/make_i386_sysv_elf_gas.S new file mode 100644 index 0000000000000..b76de260d211f --- /dev/null +++ b/Zend/asm/make_i386_sysv_elf_gas.S @@ -0,0 +1,107 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | hidden | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | | * + * ---------------------------------------------------------------------------------- * + * | to | data | | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.file "make_i386_sysv_elf_gas.S" +.text +.globl make_fcontext +.align 2 +.type make_fcontext,@function +make_fcontext: + /* first arg of make_fcontext() == top of context-stack */ + movl 0x4(%esp), %eax + + /* reserve space for first argument of context-function + eax might already point to a 16byte border */ + leal -0x8(%eax), %eax + + /* shift address in EAX to lower 16 byte boundary */ + andl $-16, %eax + + /* reserve space for context-data on context-stack */ + leal -0x28(%eax), %eax + + /* third arg of make_fcontext() == address of context-function */ + /* stored in EBX */ + movl 0xc(%esp), %ecx + movl %ecx, 0x10(%eax) + + /* save MMX control- and status-word */ + stmxcsr (%eax) + /* save x87 control-word */ + fnstcw 0x4(%eax) + + /* return transport_t */ + /* FCTX == EDI, DATA == ESI */ + leal 0x8(%eax), %ecx + movl %ecx, 0x1c(%eax) + + /* compute abs address of label trampoline */ + call 1f + /* address of trampoline 1 */ +1: popl %ecx + /* compute abs address of label trampoline */ + addl $trampoline-1b, %ecx + /* save address of trampoline as return address */ + /* will be entered after calling jump_fcontext() first time */ + movl %ecx, 0x18(%eax) + + /* compute abs address of label finish */ + call 2f + /* address of label 2 */ +2: popl %ecx + /* compute abs address of label finish */ + addl $finish-2b, %ecx + /* save address of finish as return-address for context-function */ + /* will be entered after context-function returns */ + movl %ecx, 0x14(%eax) + + ret /* return pointer to context-data */ + +trampoline: + /* move transport_t for entering context-function */ + movl %edi, (%esp) + movl %esi, 0x4(%esp) + pushl %ebp + /* jump to context-function */ + jmp *%ebx + +finish: + call 3f + /* address of label 3 */ +3: popl %ebx + /* compute address of GOT and store it in EBX */ + addl $_GLOBAL_OFFSET_TABLE_+[.-3b], %ebx + + /* exit code is zero */ + xorl %eax, %eax + movl %eax, (%esp) + /* exit application */ + call _exit@PLT + hlt +.size make_fcontext,.-make_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/make_i386_sysv_macho_gas.S b/Zend/asm/make_i386_sysv_macho_gas.S new file mode 100644 index 0000000000000..fdcdb7c80fbff --- /dev/null +++ b/Zend/asm/make_i386_sysv_macho_gas.S @@ -0,0 +1,90 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | to | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | | * + * ---------------------------------------------------------------------------------- * + * | data | | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.text +.globl _make_fcontext +.align 2 +_make_fcontext: + /* first arg of make_fcontext() == top of context-stack */ + movl 0x4(%esp), %eax + + /* reserve space for first argument of context-function + eax might already point to a 16byte border */ + leal -0x8(%eax), %eax + + /* shift address in EAX to lower 16 byte boundary */ + andl $-16, %eax + + /* reserve space for context-data on context-stack */ + leal -0x2c(%eax), %eax + + /* third arg of make_fcontext() == address of context-function */ + /* stored in EBX */ + movl 0xc(%esp), %ecx + movl %ecx, 0x10(%eax) + + /* save MMX control- and status-word */ + stmxcsr (%eax) + /* save x87 control-word */ + fnstcw 0x4(%eax) + + /* compute abs address of label trampoline */ + call 1f + /* address of trampoline 1 */ +1: popl %ecx + /* compute abs address of label trampoline */ + addl $trampoline-1b, %ecx + /* save address of trampoline as return address */ + /* will be entered after calling jump_fcontext() first time */ + movl %ecx, 0x18(%eax) + + /* compute abs address of label finish */ + call 2f + /* address of label 2 */ +2: popl %ecx + /* compute abs address of label finish */ + addl $finish-2b, %ecx + /* save address of finish as return-address for context-function */ + /* will be entered after context-function returns */ + movl %ecx, 0x14(%eax) + + ret /* return pointer to context-data */ + +trampoline: + /* move transport_t for entering context-function */ + movl %eax, (%esp) + movl %edx, 0x4(%esp) + pushl %ebp + /* jump to context-function */ + jmp *%ebx + +finish: + /* exit code is zero */ + xorl %eax, %eax + movl %eax, (%esp) + /* exit application */ + call __exit + hlt diff --git a/Zend/asm/make_mips32_o32_elf_gas.S b/Zend/asm/make_mips32_o32_elf_gas.S new file mode 100644 index 0000000000000..4e11e3d058250 --- /dev/null +++ b/Zend/asm/make_mips32_o32_elf_gas.S @@ -0,0 +1,97 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | F20 | F22 | F24 | F26 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | F28 | F30 | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | FP |hiddn| RA | PC | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | ABI ARGS | GP | FCTX| DATA| | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "make_mips32_o32_elf_gas.S" +.text +.globl make_fcontext +.align 2 +.type make_fcontext,@function +.ent make_fcontext +make_fcontext: +#ifdef __PIC__ +.set noreorder +.cpload $t9 +.set reorder +#endif + # shift address in A0 to lower 16 byte boundary + li $v1, -16 # 0xfffffffffffffff0 + and $v0, $v1, $a0 + + # reserve space for context-data on context-stack + # includes an extra 32 bytes for: + # - 16-byte incoming argument area required by mips ABI used when + # jump_context calls the initial function + # - 4 bytes to save our GP register used in finish + # - 8 bytes to as space for transfer_t returned to finish + # - 4 bytes for alignment + addiu $v0, $v0, -128 + + # third arg of make_fcontext() == address of context-function + sw $a2, 92($v0) + # save global pointer in context-data + sw $gp, 112($v0) + + # compute address of returned transfer_t + addiu $t0, $v0, 116 + sw $t0, 84($v0) + + # compute abs address of label finish + la $t9, finish + # save address of finish as return-address for context-function + # will be entered after context-function returns + sw $t9, 88($v0) + + jr $ra # return pointer to context-data + +finish: + # reload our gp register (needed for la) + lw $gp, 16($sp) + + # call _exit(0) + # the previous function should have left the 16 bytes incoming argument + # area on the stack which we reuse for calling _exit + la $t9, _exit + move $a0, $zero + jr $t9 +.end make_fcontext +.size make_fcontext, .-make_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/make_mips64_n64_elf_gas.S b/Zend/asm/make_mips64_n64_elf_gas.S new file mode 100644 index 0000000000000..7bb30b14dee79 --- /dev/null +++ b/Zend/asm/make_mips64_n64_elf_gas.S @@ -0,0 +1,96 @@ +/* + Copyright Jiaxun Yang 2018. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | F24 | F25 | F26 | F27 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | F28 | F29 | F30 | F31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | FP | GP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "make_mips64_n64_elf_gas.S" +.text +.globl make_fcontext +.align 3 +.type make_fcontext,@function +.ent make_fcontext +make_fcontext: +#ifdef __PIC__ +.set noreorder +.cpload $t9 +.set reorder +#endif + # shift address in A0 to lower 16 byte boundary + li $v1, 0xfffffffffffffff0 + and $v0, $v1, $a0 + + # reserve space for context-data on context-stack + daddiu $v0, $v0, -160 + + # third arg of make_fcontext() == address of context-function + sd $a2, 152($v0) + # save global pointer in context-data + sd $gp, 136($v0) + + # psudo instruction compute abs address of label finish based on GP + dla $t9, finish + + # save address of finish as return-address for context-function + # will be entered after context-function returns + sd $t9, 144($v0) + + jr $ra # return pointer to context-data + +finish: + # reload our gp register (needed for la) + daddiu $t0, $sp, -160 + ld $gp, 136($t0) + + # call _exit(0) + # the previous function should have left the 16 bytes incoming argument + # area on the stack which we reuse for calling _exit + dla $t9, _exit + move $a0, $zero + jr $t9 +.end make_fcontext +.size make_fcontext, .-make_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/make_ppc32_sysv_elf_gas.S b/Zend/asm/make_ppc32_sysv_elf_gas.S new file mode 100644 index 0000000000000..9616c4ca9b468 --- /dev/null +++ b/Zend/asm/make_ppc32_sysv_elf_gas.S @@ -0,0 +1,146 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * |bchai|hiddn| fpscr | PC | CR | R14 | R15 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R16 | R17 | R18 | R19 | R20 | R21 | R22 | R23 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R24 | R25 | R26 | R27 | R28 | R29 | R30 | R31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | F14 | F15 | F16 | F17 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | F18 | F19 | F20 | F21 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | F22 | F23 | F24 | F25 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | F26 | F27 | F28 | F29 | * + * ------------------------------------------------- * + * ------------------------|------------ * + * | 224 | 228 | 232 | 236 | 240 | 244 | * + * ------------------------|------------ * + * | F30 | F31 |bchai| LR | * + * ------------------------|------------ * + * * + *******************************************************/ + +.file "make_ppc32_sysv_elf_gas.S" +.text +.globl make_fcontext +.align 2 +.type make_fcontext,@function +make_fcontext: + # save return address into R6 + mflr %r6 + + # first arg of make_fcontext() == top address of context-function + # shift address in R3 to lower 16 byte boundary + clrrwi %r3, %r3, 4 + + # reserve space on context-stack, including 16 bytes of linkage + # and parameter area + 240 bytes of context-data (R1 % 16 == 0) + subi %r3, %r3, 16 + 240 + + # third arg of make_fcontext() == address of context-function +#ifdef __linux__ + # save context-function as PC + stw %r5, 16(%r3) +#else + # save context-function for trampoline + stw %r5, 248(%r3) +#endif + + # set back-chain to zero + li %r0, 0 + stw %r0, 240(%r3) + + # copy FPSCR to new context + mffs %f0 + stfd %f0, 8(%r3) + +#ifdef __linux__ + # set hidden pointer for returning transfer_t + la %r0, 248(%r3) + stw %r0, 4(%r3) +#endif + + # load address of label 1 into R4 + bl 1f +1: mflr %r4 +#ifndef __linux__ + # compute abs address of trampoline, use as PC + addi %r7, %r4, trampoline - 1b + stw %r7, 16(%r3) +#endif + # compute abs address of label finish + addi %r4, %r4, finish - 1b + # save address of finish as return-address for context-function + # will be entered after context-function returns + stw %r4, 244(%r3) + + # restore return address from R6 + mtlr %r6 + + blr # return pointer to context-data + +#ifndef __linux__ +trampoline: + # On systems other than Linux, jump_fcontext is returning the + # transfer_t in R3:R4, but we need to pass transfer_t * R3 to + # our context-function. + lwz %r0, 8(%r1) # address of context-function + mtctr %r0 + stw %r3, 8(%r1) + stw %r4, 12(%r1) + la %r3, 8(%r1) # address of transfer_t + bctr +#endif + +finish: + # Use the secure PLT for _exit(0). If we use the insecure BSS PLT + # here, then the linker may use the insecure BSS PLT even if the + # C++ compiler wanted the secure PLT. + + # set R30 for secure PLT, large model + bl 2f +2: mflr %r30 + addis %r30, %r30, .Ltoc - 2b@ha + addi %r30, %r30, .Ltoc - 2b@l + + # call _exit(0) with special addend 0x8000 for large model + li %r3, 0 + bl _exit + 0x8000@plt +.size make_fcontext, .-make_fcontext + +/* Provide the GOT pointer for secure PLT, large model. */ +.section .got2,"aw" +.Ltoc = . + 0x8000 + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/make_ppc32_sysv_macho_gas.S b/Zend/asm/make_ppc32_sysv_macho_gas.S new file mode 100644 index 0000000000000..8f35eff9abbff --- /dev/null +++ b/Zend/asm/make_ppc32_sysv_macho_gas.S @@ -0,0 +1,137 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/****************************************************** + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | F14 | F15 | F16 | F17 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | F18 | F19 | F20 | F21 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | F22 | F23 | F24 | F25 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | F26 | F27 | F28 | F29 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | F30 | F31 | fpscr | R13 | R14 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | R31 |hiddn| CR | LR | PC |bchai|linkr| FCTX| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 64 | | * + * ------------------------------------------------- * + * | 256 | | * + * ------------------------------------------------- * + * | DATA| | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.text +.globl _make_fcontext +.align 2 +_make_fcontext: + # save return address into R6 + mflr r6 + + # first arg of make_fcontext() == top address of context-function + # shift address in R3 to lower 16 byte boundary + clrrwi r3, r3, 4 + + # reserve space for context-data on context-stack + # including 64 byte of linkage + parameter area (R1 16 == 0) + subi r3, r3, 336 + + # third arg of make_fcontext() == address of context-function + stw r5, 240(r3) + + # set back-chain to zero + li r0, 0 + stw r0, 244(r3) + + mffs f0 # load FPSCR + stfd f0, 144(r3) # save FPSCR + + # compute address of returned transfer_t + addi r0, r3, 252 + mr r4, r0 + stw r4, 228(r3) + + # load LR + mflr r0 + # jump to label 1 + bl 1f +1: + # load LR into R4 + mflr r4 + # compute abs address of label finish + addi r4, r4, finish - 1b + # restore LR + mtlr r0 + # save address of finish as return-address for context-function + # will be entered after context-function returns + stw r4, 236(r3) + + # restore return address from R6 + mtlr r6 + + blr # return pointer to context-data + +finish: + # save return address into R0 + mflr r0 + # save return address on stack, set up stack frame + stw r0, 4(r1) + # allocate stack space, R1 16 == 0 + stwu r1, -16(r1) + + # exit code is zero + li r3, 0 + # exit application + bl _exit@plt diff --git a/Zend/asm/make_ppc64_sysv_elf_gas.S b/Zend/asm/make_ppc64_sysv_elf_gas.S new file mode 100644 index 0000000000000..c4d7ee59826af --- /dev/null +++ b/Zend/asm/make_ppc64_sysv_elf_gas.S @@ -0,0 +1,177 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | TOC | R14 | R15 | R16 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R17 | R18 | R19 | R20 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R21 | R22 | R23 | R24 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | R25 | R26 | R27 | R28 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | R29 | R30 | R31 | hidden | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | CR | LR | PC | back-chain| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | cr saved | lr saved | compiler | linker | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | TOC saved | FCTX | DATA | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.file "make_ppc64_sysv_elf_gas.S" +.globl make_fcontext +#if _CALL_ELF == 2 + .text + .align 2 +make_fcontext: + addis %r2, %r12, .TOC.-make_fcontext@ha + addi %r2, %r2, .TOC.-make_fcontext@l + .localentry make_fcontext, . - make_fcontext +#else + .section ".opd","aw" + .align 3 +make_fcontext: +# ifdef _CALL_LINUX + .quad .L.make_fcontext,.TOC.@tocbase,0 + .type make_fcontext,@function + .text + .align 2 +.L.make_fcontext: +# else + .hidden .make_fcontext + .globl .make_fcontext + .quad .make_fcontext,.TOC.@tocbase,0 + .size make_fcontext,24 + .type .make_fcontext,@function + .text + .align 2 +.make_fcontext: +# endif +#endif + # save return address into R6 + mflr %r6 + + # first arg of make_fcontext() == top address of context-stack + # shift address in R3 to lower 16 byte boundary + clrrdi %r3, %r3, 4 + + # reserve space for context-data on context-stack + # including 64 byte of linkage + parameter area (R1 % 16 == 0) + subi %r3, %r3, 248 + + # third arg of make_fcontext() == address of context-function + # entry point (ELFv2) or descriptor (ELFv1) +#if _CALL_ELF == 2 + # save address of context-function entry point + std %r5, 176(%r3) +#else + # save address of context-function entry point + ld %r4, 0(%r5) + std %r4, 176(%r3) + # save TOC of context-function + ld %r4, 8(%r5) + std %r4, 0(%r3) +#endif + + # set back-chain to zero + li %r0, 0 + std %r0, 184(%r3) + +#if _CALL_ELF != 2 + # zero in r3 indicates first jump to context-function + std %r0, 152(%r3) +#endif + + # load LR + mflr %r0 + # jump to label 1 + bl 1f +1: + # load LR into R4 + mflr %r4 + # compute abs address of label finish + addi %r4, %r4, finish - 1b + # restore LR + mtlr %r0 + # save address of finish as return-address for context-function + # will be entered after context-function returns + std %r4, 168(%r3) + + # restore return address from R6 + mtlr %r6 + + blr # return pointer to context-data + +finish: + # save return address into R0 + mflr %r0 + # save return address on stack, set up stack frame + std %r0, 8(%r1) + # allocate stack space, R1 % 16 == 0 + stdu %r1, -32(%r1) + + # exit code is zero + li %r3, 0 + # exit application + bl _exit + nop +#if _CALL_ELF == 2 + .size make_fcontext, .-make_fcontext +#else +# ifdef _CALL_LINUX + .size .make_fcontext, .-.L.make_fcontext +# else + .size .make_fcontext, .-.make_fcontext +# endif +#endif + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/make_ppc64_sysv_macho_gas.S b/Zend/asm/make_ppc64_sysv_macho_gas.S new file mode 100644 index 0000000000000..7b947bb6b030b --- /dev/null +++ b/Zend/asm/make_ppc64_sysv_macho_gas.S @@ -0,0 +1,126 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | TOC | R14 | R15 | R16 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R17 | R18 | R19 | R20 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R21 | R22 | R23 | R24 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | R25 | R26 | R27 | R28 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | R29 | R30 | R31 | hidden | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | CR | LR | PC | back-chain| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | cr saved | lr saved | compiler | linker | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | TOC saved | FCTX | DATA | | * + * ------------------------------------------------- * + * * + +.text +.globl _make_fcontext +_make_fcontext: + ; save return address into R6 + mflr r6 + + ; first arg of make_fcontext() == top address of context-function + ; shift address in R3 to lower 16 byte boundary + clrrwi r3, r3, 4 + + ; reserve space for context-data on context-stack + ; including 64 byte of linkage + parameter area (R1 16 == 0) + subi r3, r3, 248 + + ; third arg of make_fcontext() == address of context-function + stw r5, 176(r3) + + ; set back-chain to zero + li %r0, 0 + std %r0, 184(%r3) + + ; compute address of returned transfer_t + addi %r0, %r3, 232 + mr %r4, %r0 + std %r4, 152(%r3) + + ; load LR + mflr r0 + ; jump to label 1 + bl l1 +l1: + ; load LR into R4 + mflr r4 + ; compute abs address of label finish + addi r4, r4, lo16((finish - .) + 4) + ; restore LR + mtlr r0 + ; save address of finish as return-address for context-function + ; will be entered after context-function returns + std r4, 168(r3) + + ; restore return address from R6 + mtlr r6 + + blr ; return pointer to context-data + +finish: + ; save return address into R0 + mflr r0 + ; save return address on stack, set up stack frame + stw r0, 8(r1) + ; allocate stack space, R1 16 == 0 + stwu r1, -32(r1) + + ; set return value to zero + li r3, 0 + ; exit application + bl __exit + nop diff --git a/Zend/asm/make_x86_64_ms_pe_masm.asm b/Zend/asm/make_x86_64_ms_pe_masm.asm new file mode 100644 index 0000000000000..8f6c959a83763 --- /dev/null +++ b/Zend/asm/make_x86_64_ms_pe_masm.asm @@ -0,0 +1,163 @@ + +; Copyright Oliver Kowalke 2009. +; Distributed under the Boost Software License, Version 1.0. +; (See accompanying file LICENSE_1_0.txt or copy at +; http://www.boost.org/LICENSE_1_0.txt) + +; ---------------------------------------------------------------------------------- +; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +; ---------------------------------------------------------------------------------- +; | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | +; ---------------------------------------------------------------------------------- +; | SEE registers (XMM6-XMM15) | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | +; ---------------------------------------------------------------------------------- +; | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | +; ---------------------------------------------------------------------------------- +; | SEE registers (XMM6-XMM15) | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | +; ---------------------------------------------------------------------------------- +; | 0xe40 | 0x44 | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | +; ---------------------------------------------------------------------------------- +; | SEE registers (XMM6-XMM15) | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | +; ---------------------------------------------------------------------------------- +; | 0x60 | 0x64 | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | +; ---------------------------------------------------------------------------------- +; | SEE registers (XMM6-XMM15) | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 32 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | +; ---------------------------------------------------------------------------------- +; | 0x80 | 0x84 | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | +; ---------------------------------------------------------------------------------- +; | SEE registers (XMM6-XMM15) | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | +; ---------------------------------------------------------------------------------- +; | 0xa0 | 0xa4 | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | +; ---------------------------------------------------------------------------------- +; | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | +; ---------------------------------------------------------------------------------- +; | 0xc0 | 0xc4 | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | +; ---------------------------------------------------------------------------------- +; | limit | base | R12 | R13 | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | +; ---------------------------------------------------------------------------------- +; | 0xe0 | 0xe4 | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | +; ---------------------------------------------------------------------------------- +; | R14 | R15 | RDI | RSI | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | +; ---------------------------------------------------------------------------------- +; | 0x100 | 0x104 | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | +; ---------------------------------------------------------------------------------- +; | RBX | RBP | hidden | RIP | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | +; ---------------------------------------------------------------------------------- +; | 0x120 | 0x124 | 0x128 | 0x12c | 0x130 | 0x134 | 0x138 | 0x13c | +; ---------------------------------------------------------------------------------- +; | parameter area | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | +; ---------------------------------------------------------------------------------- +; | 0x140 | 0x144 | 0x148 | 0x14c | 0x150 | 0x154 | 0x158 | 0x15c | +; ---------------------------------------------------------------------------------- +; | FCTX | DATA | | +; ---------------------------------------------------------------------------------- + +; standard C library function +EXTERN _exit:PROC +.code + +; generate function table entry in .pdata and unwind information in +make_fcontext PROC BOOST_CONTEXT_EXPORT FRAME + ; .xdata for a function's structured exception handling unwind behavior + .endprolog + + ; first arg of make_fcontext() == top of context-stack + mov rax, rcx + + ; shift address in RAX to lower 16 byte boundary + ; == pointer to fcontext_t and address of context stack + and rax, -16 + + ; reserve space for context-data on context-stack + ; on context-function entry: (RSP -0x8) % 16 == 0 + sub rax, 0150h + + ; third arg of make_fcontext() == address of context-function + ; stored in RBX + mov [rax+0100h], r8 + + ; first arg of make_fcontext() == top of context-stack + ; save top address of context stack as 'base' + mov [rax+0c8h], rcx + ; second arg of make_fcontext() == size of context-stack + ; negate stack size for LEA instruction (== substraction) + neg rdx + ; compute bottom address of context stack (limit) + lea rcx, [rcx+rdx] + ; save bottom address of context stack as 'limit' + mov [rax+0c0h], rcx + ; save address of context stack limit as 'dealloction stack' + mov [rax+0b8h], rcx + ; set fiber-storage to zero + xor rcx, rcx + mov [rax+0b0h], rcx + + ; save MMX control- and status-word + stmxcsr [rax+0a0h] + ; save x87 control-word + fnstcw [rax+0a4h] + + ; compute address of transport_t + lea rcx, [rax+0140h] + ; store address of transport_t in hidden field + mov [rax+0110h], rcx + + ; compute abs address of label trampoline + lea rcx, trampoline + ; save address of trampoline as return-address for context-function + ; will be entered after calling jump_fcontext() first time + mov [rax+0118h], rcx + + ; compute abs address of label finish + lea rcx, finish + ; save address of finish as return-address for context-function in RBP + ; will be entered after context-function returns + mov [rax+0108h], rcx + + ret ; return pointer to context-data + +trampoline: + ; store return address on stack + ; fix stack alignment + push rbp + ; jump to context-function + jmp rbx + +finish: + ; exit code is zero + xor rcx, rcx + ; exit application + call _exit + hlt +make_fcontext ENDP +END diff --git a/Zend/asm/make_x86_64_sysv_elf_gas.S b/Zend/asm/make_x86_64_sysv_elf_gas.S new file mode 100644 index 0000000000000..0ef37569a0299 --- /dev/null +++ b/Zend/asm/make_x86_64_sysv_elf_gas.S @@ -0,0 +1,82 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * + * ---------------------------------------------------------------------------------- * + * | R15 | RBX | RBP | RIP | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.file "make_x86_64_sysv_elf_gas.S" +.text +.globl make_fcontext +.type make_fcontext,@function +.align 16 +make_fcontext: + /* first arg of make_fcontext() == top of context-stack */ + movq %rdi, %rax + + /* shift address in RAX to lower 16 byte boundary */ + andq $-16, %rax + + /* reserve space for context-data on context-stack */ + /* on context-function entry: (RSP -0x8) % 16 == 0 */ + leaq -0x40(%rax), %rax + + /* third arg of make_fcontext() == address of context-function */ + /* stored in RBX */ + movq %rdx, 0x28(%rax) + + /* save MMX control- and status-word */ + stmxcsr (%rax) + /* save x87 control-word */ + fnstcw 0x4(%rax) + + /* compute abs address of label trampoline */ + leaq trampoline(%rip), %rcx + /* save address of trampoline as return-address for context-function */ + /* will be entered after calling jump_fcontext() first time */ + movq %rcx, 0x38(%rax) + + /* compute abs address of label finish */ + leaq finish(%rip), %rcx + /* save address of finish as return-address for context-function */ + /* will be entered after context-function returns */ + movq %rcx, 0x30(%rax) + + ret /* return pointer to context-data */ + +trampoline: + /* store return address on stack */ + /* fix stack alignment */ + push %rbp + /* jump to context-function */ + jmp *%rbx + +finish: + /* exit code is zero */ + xorq %rdi, %rdi + /* exit application */ + call _exit@PLT + hlt +.size make_fcontext,.-make_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/make_x86_64_sysv_macho_gas.S b/Zend/asm/make_x86_64_sysv_macho_gas.S new file mode 100644 index 0000000000000..5d6c5431c5985 --- /dev/null +++ b/Zend/asm/make_x86_64_sysv_macho_gas.S @@ -0,0 +1,76 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * + * ---------------------------------------------------------------------------------- * + * | R15 | RBX | RBP | RIP | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.text +.globl _make_fcontext +.align 8 +_make_fcontext: + /* first arg of make_fcontext() == top of context-stack */ + movq %rdi, %rax + + /* shift address in RAX to lower 16 byte boundary */ + andq $-16, %rax + + /* reserve space for context-data on context-stack */ + /* on context-function entry: (RSP -0x8) % 16 == 0 */ + leaq -0x40(%rax), %rax + + /* third arg of make_fcontext() == address of context-function */ + /* stored in RBX */ + movq %rdx, 0x28(%rax) + + /* save MMX control- and status-word */ + stmxcsr (%rax) + /* save x87 control-word */ + fnstcw 0x4(%rax) + + /* compute abs address of label trampoline */ + leaq trampoline(%rip), %rcx + /* save address of trampoline as return-address for context-function */ + /* will be entered after calling jump_fcontext() first time */ + movq %rcx, 0x38(%rax) + + /* compute abs address of label finish */ + leaq finish(%rip), %rcx + /* save address of finish as return-address for context-function */ + /* will be entered after context-function returns */ + movq %rcx, 0x30(%rax) + + ret /* return pointer to context-data */ + +trampoline: + /* store return address on stack */ + /* fix stack alignment */ + push %rbp + /* jump to context-function */ + jmp *%rbx + +finish: + /* exit code is zero */ + xorq %rdi, %rdi + /* exit application */ + call __exit + hlt diff --git a/Zend/tests/fibers/catch.phpt b/Zend/tests/fibers/catch.phpt new file mode 100644 index 0000000000000..acbf69a5d07be --- /dev/null +++ b/Zend/tests/fibers/catch.phpt @@ -0,0 +1,21 @@ +--TEST-- +Catch exception thrown into fiber +--FILE-- +getMessage()); + } +}); + +$value = $fiber->start(); +var_dump($value); + +$fiber->throw(new Exception('test')); + +--EXPECT-- +string(4) "test" +string(4) "test" diff --git a/Zend/tests/fibers/double-start.phpt b/Zend/tests/fibers/double-start.phpt new file mode 100644 index 0000000000000..2617c0cd570dd --- /dev/null +++ b/Zend/tests/fibers/double-start.phpt @@ -0,0 +1,19 @@ +--TEST-- +Start on already running fiber +--FILE-- +start(); + +$fiber->start(); + +--EXPECTF-- +Fatal error: Uncaught FiberError: Cannot start a fiber that has already been started in %sdouble-start.php:%d +Stack trace: +#0 %sdouble-start.php(%d): Fiber->start() +#1 {main} + thrown in %sdouble-start.php on line %d diff --git a/Zend/tests/fibers/exit-in-fiber.phpt b/Zend/tests/fibers/exit-in-fiber.phpt new file mode 100644 index 0000000000000..5cdc62d0ec69d --- /dev/null +++ b/Zend/tests/fibers/exit-in-fiber.phpt @@ -0,0 +1,19 @@ +--TEST-- +Exit from fiber +--FILE-- +start(); + +$fiber->resume(); + +echo "unreachable\n"; + +--EXPECT-- +resumed diff --git a/Zend/tests/fibers/failing-fiber.phpt b/Zend/tests/fibers/failing-fiber.phpt new file mode 100644 index 0000000000000..df2b25e38ed2b --- /dev/null +++ b/Zend/tests/fibers/failing-fiber.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test throwing into fiber +--FILE-- +start(); +var_dump($value); + +$fiber->resume($value); + +--EXPECTF-- +string(4) "test" + +Fatal error: Uncaught Exception: test in %sfailing-fiber.php:%d +Stack trace: +#0 [internal function]: {closure}() +#1 {main} + thrown in %sfailing-fiber.php on line %d diff --git a/Zend/tests/fibers/fast-finish-fiber.phpt b/Zend/tests/fibers/fast-finish-fiber.phpt new file mode 100644 index 0000000000000..8f2eb8c3fe212 --- /dev/null +++ b/Zend/tests/fibers/fast-finish-fiber.phpt @@ -0,0 +1,16 @@ +--TEST-- +Fast finishing fiber does not leak +--FILE-- + 'test'); +var_dump($fiber->isStarted()); +var_dump($fiber->start()); +var_dump($fiber->getReturn()); +var_dump($fiber->isTerminated()); + +--EXPECTF-- +bool(false) +NULL +string(4) "test" +bool(true) diff --git a/Zend/tests/fibers/fatal-error-in-fiber.phpt b/Zend/tests/fibers/fatal-error-in-fiber.phpt new file mode 100644 index 0000000000000..0a6e18bb1ab3d --- /dev/null +++ b/Zend/tests/fibers/fatal-error-in-fiber.phpt @@ -0,0 +1,13 @@ +--TEST-- +Fatal error in new fiber +--FILE-- +start(); + +--EXPECTF-- +Fatal error: Fatal error in fiber in %sfatal-error-in-fiber.php on line %d diff --git a/Zend/tests/fibers/fatal-error-in-nested-fiber.phpt b/Zend/tests/fibers/fatal-error-in-nested-fiber.phpt new file mode 100644 index 0000000000000..7d5988b8d099a --- /dev/null +++ b/Zend/tests/fibers/fatal-error-in-nested-fiber.phpt @@ -0,0 +1,27 @@ +--TEST-- +Fatal error within a nested fiber +--FILE-- +start()); + + \Fiber::suspend(1); + + $fiber->resume(); +}); + +var_dump($fiber->start()); + +$fiber->resume(); + +--EXPECTF-- +int(2) +int(1) + +Fatal error: Fatal error in nested fiber in %sfatal-error-in-nested-fiber.php on line %d diff --git a/Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt b/Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt new file mode 100644 index 0000000000000..e82b2ec9f180b --- /dev/null +++ b/Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt @@ -0,0 +1,21 @@ +--TEST-- +Fatal error in a fiber with other active fibers +--FILE-- + Fiber::suspend(1)); + +$fiber2 = new Fiber(function (): void { + \Fiber::suspend(2); + trigger_error("Fatal error in fiber", E_USER_ERROR); +}); + +var_dump($fiber1->start()); +var_dump($fiber2->start()); +$fiber2->resume(); + +--EXPECTF-- +int(1) +int(2) + +Fatal error: Fatal error in fiber in %sfatal-error-with-multiple-fibers.php on line %d diff --git a/Zend/tests/fibers/fiber-created-in-destruct.phpt b/Zend/tests/fibers/fiber-created-in-destruct.phpt new file mode 100644 index 0000000000000..f78b5b88d9b18 --- /dev/null +++ b/Zend/tests/fibers/fiber-created-in-destruct.phpt @@ -0,0 +1,23 @@ +--TEST-- +Fiber created in destructor +--FILE-- +start()); + var_dump($fiber->resume()); + var_dump($fiber->getReturn()); + } +}; + +--EXPECT-- +int(1) +NULL +int(2) diff --git a/Zend/tests/fibers/fiber-error-construct.phpt b/Zend/tests/fibers/fiber-error-construct.phpt new file mode 100644 index 0000000000000..bfff39394d720 --- /dev/null +++ b/Zend/tests/fibers/fiber-error-construct.phpt @@ -0,0 +1,13 @@ +--TEST-- +FiberError cannot be constructed in user code +--FILE-- +getMessage(), "\n"; +} + +--EXPECT-- +The "FiberError" class is reserved for internal use and cannot be manually instantiated diff --git a/Zend/tests/fibers/fiber-in-destruct.phpt b/Zend/tests/fibers/fiber-in-destruct.phpt new file mode 100644 index 0000000000000..7301521ecfe25 --- /dev/null +++ b/Zend/tests/fibers/fiber-in-destruct.phpt @@ -0,0 +1,34 @@ +--TEST-- +Pause fiber in destruct +--FILE-- +start()); +var_dump($fiber->resume()); +var_dump($fiber->resume()); +var_dump($fiber->resume()); +var_dump($fiber->getReturn()); + +--EXPECT-- +int(1) +int(2) +int(3) +NULL +int(4) diff --git a/Zend/tests/fibers/fiber-in-shutdown-function.phpt b/Zend/tests/fibers/fiber-in-shutdown-function.phpt new file mode 100644 index 0000000000000..ba7afeda43865 --- /dev/null +++ b/Zend/tests/fibers/fiber-in-shutdown-function.phpt @@ -0,0 +1,23 @@ +--TEST-- +Fiber in shutdown function +--FILE-- +start()); + var_dump($fiber->resume()); + var_dump($fiber->resume()); + var_dump($fiber->getReturn()); +}); + +--EXPECT-- +int(1) +int(2) +NULL +int(3) diff --git a/Zend/tests/fibers/fiber-status.phpt b/Zend/tests/fibers/fiber-status.phpt new file mode 100644 index 0000000000000..c0b60e2e92a6a --- /dev/null +++ b/Zend/tests/fibers/fiber-status.phpt @@ -0,0 +1,50 @@ +--TEST-- +Fiber status methods +--FILE-- +isStarted()); + var_dump($fiber->isRunning()); + var_dump($fiber->isSuspended()); + var_dump($fiber->isTerminated()); + Fiber::suspend(); +}); + +var_dump($fiber->isStarted()); +var_dump($fiber->isRunning()); +var_dump($fiber->isSuspended()); +var_dump($fiber->isTerminated()); + +$fiber->start(); + +var_dump($fiber->isStarted()); +var_dump($fiber->isRunning()); +var_dump($fiber->isSuspended()); +var_dump($fiber->isTerminated()); + +$fiber->resume(); + +var_dump($fiber->isStarted()); +var_dump($fiber->isRunning()); +var_dump($fiber->isSuspended()); +var_dump($fiber->isTerminated()); + +--EXPECT-- +bool(false) +bool(false) +bool(false) +bool(false) +bool(true) +bool(true) +bool(false) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(false) +bool(true) \ No newline at end of file diff --git a/Zend/tests/fibers/fiber-this.phpt b/Zend/tests/fibers/fiber-this.phpt new file mode 100644 index 0000000000000..4bc3e7f4a2920 --- /dev/null +++ b/Zend/tests/fibers/fiber-this.phpt @@ -0,0 +1,17 @@ +--TEST-- +Fiber::this() +--FILE-- +start(); + +--EXPECTF-- +NULL +object(Fiber)#%d (0) { +} diff --git a/Zend/tests/fibers/fiber-throw-in-destruct.phpt b/Zend/tests/fibers/fiber-throw-in-destruct.phpt new file mode 100644 index 0000000000000..34a3d5e71c63f --- /dev/null +++ b/Zend/tests/fibers/fiber-throw-in-destruct.phpt @@ -0,0 +1,26 @@ +--TEST-- +Fiber throwing from destructor +--FILE-- +start()); + var_dump($fiber->resume()); + } +}; + +--EXPECTF-- +int(1) + +Fatal error: Uncaught Exception: test in %sfiber-throw-in-destruct.php:%d +Stack trace: +#0 [internal function]: class@anonymous::{closure}() +#1 {main} + thrown in %sfiber-throw-in-destruct.php on line %d diff --git a/Zend/tests/fibers/get-return-after-throwing.phpt b/Zend/tests/fibers/get-return-after-throwing.phpt new file mode 100644 index 0000000000000..f1b912e5fb579 --- /dev/null +++ b/Zend/tests/fibers/get-return-after-throwing.phpt @@ -0,0 +1,23 @@ +--TEST-- +Fiber::getReturn() after a fiber throws +--FILE-- + throw new Exception('test')); + +try { + $fiber->start(); +} catch (Exception $exception) { + echo $exception->getMessage(), "\n"; +} + +$fiber->getReturn(); + +--EXPECTF-- +test + +Fatal error: Uncaught FiberError: Cannot get fiber return value: The fiber threw an exception in %sget-return-after-throwing.php:%d +Stack trace: +#0 %sget-return-after-throwing.php(%d): Fiber->getReturn() +#1 {main} + thrown in %sget-return-after-throwing.php on line %d diff --git a/Zend/tests/fibers/get-return-from-unstarted-fiber.phpt b/Zend/tests/fibers/get-return-from-unstarted-fiber.phpt new file mode 100644 index 0000000000000..a88000b172fd9 --- /dev/null +++ b/Zend/tests/fibers/get-return-from-unstarted-fiber.phpt @@ -0,0 +1,15 @@ +--TEST-- +Fiber::getReturn() from unstarted fiber +--FILE-- + Fiber::suspend(1)); + +$fiber->getReturn(); + +--EXPECTF-- +Fatal error: Uncaught FiberError: Cannot get fiber return value: The fiber has not been started in %sget-return-from-unstarted-fiber.php:%d +Stack trace: +#0 %sget-return-from-unstarted-fiber.php(%d): Fiber->getReturn() +#1 {main} + thrown in %sget-return-from-unstarted-fiber.php on line %d diff --git a/Zend/tests/fibers/get-return-in-unfinished-fiber.phpt b/Zend/tests/fibers/get-return-in-unfinished-fiber.phpt new file mode 100644 index 0000000000000..130bc44fb2601 --- /dev/null +++ b/Zend/tests/fibers/get-return-in-unfinished-fiber.phpt @@ -0,0 +1,19 @@ +--TEST-- +Fiber::getReturn() in unfinished fiber +--FILE-- + Fiber::suspend(1)); + +var_dump($fiber->start()); + +$fiber->getReturn(); + +--EXPECTF-- +int(1) + +Fatal error: Uncaught FiberError: Cannot get fiber return value: The fiber has not returned in %sget-return-in-unfinished-fiber.php:%d +Stack trace: +#0 %sget-return-in-unfinished-fiber.php(%d): Fiber->getReturn() +#1 {main} + thrown in %sget-return-in-unfinished-fiber.php on line %d diff --git a/Zend/tests/fibers/get-return.phpt b/Zend/tests/fibers/get-return.phpt new file mode 100644 index 0000000000000..c7c87f6bc7f53 --- /dev/null +++ b/Zend/tests/fibers/get-return.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test fiber return value +--FILE-- +start(); +var_dump($value); +var_dump($fiber->resume($value + 1)); +var_dump($fiber->getReturn()); + +--EXPECT-- +int(1) +NULL +int(2) diff --git a/Zend/tests/fibers/resume-non-running-fiber.phpt b/Zend/tests/fibers/resume-non-running-fiber.phpt new file mode 100644 index 0000000000000..79b5d9c5014d0 --- /dev/null +++ b/Zend/tests/fibers/resume-non-running-fiber.phpt @@ -0,0 +1,15 @@ +--TEST-- +Resume non-running fiber +--FILE-- + null); + +$fiber->resume(); + +--EXPECTF-- +Fatal error: Uncaught FiberError: Cannot resume a fiber that is not suspended in %sresume-non-running-fiber.php:%d +Stack trace: +#0 %sresume-non-running-fiber.php(%d): Fiber->resume() +#1 {main} + thrown in %sresume-non-running-fiber.php on line %d diff --git a/Zend/tests/fibers/resume-running-fiber.phpt b/Zend/tests/fibers/resume-running-fiber.phpt new file mode 100644 index 0000000000000..d5bc2e50c0f28 --- /dev/null +++ b/Zend/tests/fibers/resume-running-fiber.phpt @@ -0,0 +1,19 @@ +--TEST-- +Resume running fiber +--FILE-- +resume(); +}); + +$fiber->start(); + +--EXPECTF-- +Fatal error: Uncaught FiberError: Cannot resume a fiber that is not suspended in %sresume-running-fiber.php:%d +Stack trace: +#0 %sresume-running-fiber.php(%d): Fiber->resume() +#1 [internal function]: {closure}() +#2 {main} + thrown in %sresume-running-fiber.php on line %d diff --git a/Zend/tests/fibers/resume-terminated-fiber.phpt b/Zend/tests/fibers/resume-terminated-fiber.phpt new file mode 100644 index 0000000000000..5d4c66e77ebb8 --- /dev/null +++ b/Zend/tests/fibers/resume-terminated-fiber.phpt @@ -0,0 +1,17 @@ +--TEST-- +Resume terminated fiber +--FILE-- + null); + +$fiber->start(); + +$fiber->resume(); + +--EXPECTF-- +Fatal error: Uncaught FiberError: Cannot resume a fiber that is not suspended in %sresume-terminated-fiber.php:%d +Stack trace: +#0 %sresume-terminated-fiber.php(%d): Fiber->resume() +#1 {main} + thrown in %sresume-terminated-fiber.php on line %d diff --git a/Zend/tests/fibers/resume.phpt b/Zend/tests/fibers/resume.phpt new file mode 100644 index 0000000000000..195f0e3cb0eb3 --- /dev/null +++ b/Zend/tests/fibers/resume.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test resume +--FILE-- +start(); +var_dump($value); +$fiber->resume($value + 1); + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/fibers/silence-operator.phpt b/Zend/tests/fibers/silence-operator.phpt new file mode 100644 index 0000000000000..203fcc484eaf9 --- /dev/null +++ b/Zend/tests/fibers/silence-operator.phpt @@ -0,0 +1,27 @@ +--TEST-- +Silence operator does not leak out of fiber +--FILE-- +start(); + +trigger_error("Warning C", E_USER_WARNING); + +$fiber->resume(); + +trigger_error("Warning D", E_USER_WARNING); + +--EXPECTF-- +Warning: Warning C in %ssilence-operator.php on line %d + +Warning: Warning D in %ssilence-operator.php on line %d diff --git a/Zend/tests/fibers/start-arguments.phpt b/Zend/tests/fibers/start-arguments.phpt new file mode 100644 index 0000000000000..308267abe73a1 --- /dev/null +++ b/Zend/tests/fibers/start-arguments.phpt @@ -0,0 +1,27 @@ +--TEST-- +Arguments to fiber callback +--FILE-- +start(1); +$fiber->resume(0); +var_dump($fiber->getReturn()); + +$fiber = new Fiber(function (int $x): int { + return $x + Fiber::suspend($x); +}); + +$fiber->start('test'); + +--EXPECTF-- +int(1) + +Fatal error: Uncaught TypeError: {closure}(): Argument #1 ($x) must be of type int, string given in %sstart-arguments.php:%d +Stack trace: +#0 [internal function]: {closure}('test') +#1 {main} + thrown in %sstart-arguments.php on line %d diff --git a/Zend/tests/fibers/suspend-in-force-close-fiber-after-shutdown.phpt b/Zend/tests/fibers/suspend-in-force-close-fiber-after-shutdown.phpt new file mode 100644 index 0000000000000..a214e4839bfb5 --- /dev/null +++ b/Zend/tests/fibers/suspend-in-force-close-fiber-after-shutdown.phpt @@ -0,0 +1,26 @@ +--TEST-- +Suspend in force closed fiber after shutdown +--FILE-- +start(); + +echo "done\n"; + +--EXPECTF-- +done + +Fatal error: Uncaught FiberError: Cannot suspend in a force closed fiber in %ssuspend-in-force-close-fiber-after-shutdown.php:%d +Stack trace: +#0 %ssuspend-in-force-close-fiber-after-shutdown.php(%d): Fiber::suspend() +#1 [internal function]: {closure}() +#2 {main} + thrown in %ssuspend-in-force-close-fiber-after-shutdown.php on line %d diff --git a/Zend/tests/fibers/suspend-in-force-close-fiber.phpt b/Zend/tests/fibers/suspend-in-force-close-fiber.phpt new file mode 100644 index 0000000000000..afef1d5a68f0c --- /dev/null +++ b/Zend/tests/fibers/suspend-in-force-close-fiber.phpt @@ -0,0 +1,24 @@ +--TEST-- +Suspend in force closed fiber +--FILE-- +start(); + +unset($fiber); + +--EXPECTF-- +Fatal error: Uncaught FiberError: Cannot suspend in a force closed fiber in %ssuspend-in-force-close-fiber.php:%d +Stack trace: +#0 %ssuspend-in-force-close-fiber.php(%d): Fiber::suspend() +#1 [internal function]: {closure}() +#2 {main} + thrown in %ssuspend-in-force-close-fiber.php on line %d diff --git a/Zend/tests/fibers/suspend-in-nested-function.phpt b/Zend/tests/fibers/suspend-in-nested-function.phpt new file mode 100644 index 0000000000000..af6577dd912cd --- /dev/null +++ b/Zend/tests/fibers/suspend-in-nested-function.phpt @@ -0,0 +1,28 @@ +--TEST-- +Suspend within nested function call +--FILE-- +start()); +var_dump($fiber->resume(2)); +var_dump($fiber->resume(3)); +var_dump($fiber->getReturn()); + +echo "done\n"; + +--EXPECT-- +int(1) +int(2) +NULL +int(3) +done diff --git a/Zend/tests/fibers/suspend-outside-fiber.phpt b/Zend/tests/fibers/suspend-outside-fiber.phpt new file mode 100644 index 0000000000000..21ec150eb9080 --- /dev/null +++ b/Zend/tests/fibers/suspend-outside-fiber.phpt @@ -0,0 +1,13 @@ +--TEST-- +Suspend outside fiber +--FILE-- + null); + +$fiber->throw(new Exception('test')); + +--EXPECTF-- +Fatal error: Uncaught FiberError: Cannot resume a fiber that is not suspended in %sthrow-into-non-running-fiber.php:%d +Stack trace: +#0 %sthrow-into-non-running-fiber.php(%d): Fiber->throw(Object(Exception)) +#1 {main} + thrown in %sthrow-into-non-running-fiber.php on line %d diff --git a/Zend/tests/fibers/throw.phpt b/Zend/tests/fibers/throw.phpt new file mode 100644 index 0000000000000..bdd67cb61fb7c --- /dev/null +++ b/Zend/tests/fibers/throw.phpt @@ -0,0 +1,21 @@ +--TEST-- +Test throwing into fiber +--FILE-- +start(); +var_dump($value); + +$fiber->throw(new Exception('test')); + +--EXPECTF-- +string(4) "test" + +Fatal error: Uncaught Exception: test in %sthrow.php:%d +Stack trace: +#0 {main} + thrown in %sthrow.php on line %d diff --git a/Zend/tests/fibers/unfinished-fiber-with-finally.phpt b/Zend/tests/fibers/unfinished-fiber-with-finally.phpt new file mode 100644 index 0000000000000..140a5fc09f0d4 --- /dev/null +++ b/Zend/tests/fibers/unfinished-fiber-with-finally.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test unfinished fiber with finally block +--FILE-- +start(); + +unset($fiber); // Destroy fiber object, executing finally block. + +echo "done\n"; + +--EXPECT-- +fiber +finally +done diff --git a/Zend/tests/fibers/unfinished-fiber-with-nested-try-catch.phpt b/Zend/tests/fibers/unfinished-fiber-with-nested-try-catch.phpt new file mode 100644 index 0000000000000..f13fe06ec5ac5 --- /dev/null +++ b/Zend/tests/fibers/unfinished-fiber-with-nested-try-catch.phpt @@ -0,0 +1,46 @@ +--TEST-- +Test unfinished fiber with nested try/catch blocks +--FILE-- +start(); + +unset($fiber); // Destroy fiber object, executing finally block. + +echo "done\n"; + +--EXPECT-- +fiber +inner finally +outer finally +done diff --git a/Zend/tests/fibers/unfinished-fiber-with-suspend-in-finally.phpt b/Zend/tests/fibers/unfinished-fiber-with-suspend-in-finally.phpt new file mode 100644 index 0000000000000..3c10f3fdb54e2 --- /dev/null +++ b/Zend/tests/fibers/unfinished-fiber-with-suspend-in-finally.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test unfinished fiber with suspend in finally +--FILE-- +start(); + +unset($fiber); // Destroy fiber object, executing finally block. + +echo "done\n"; + +--EXPECTF-- +fiber +inner finally +outer finally +done diff --git a/Zend/tests/fibers/unfinished-fiber-with-throw-in-finally.phpt b/Zend/tests/fibers/unfinished-fiber-with-throw-in-finally.phpt new file mode 100644 index 0000000000000..06306794bd7e8 --- /dev/null +++ b/Zend/tests/fibers/unfinished-fiber-with-throw-in-finally.phpt @@ -0,0 +1,47 @@ +--TEST-- +Test unfinished fiber with suspend in finally +--FILE-- +getMessage(), "\n"; + } finally { + echo "outer finally\n"; + } + + try { + echo Fiber::suspend(); + } catch (FiberError $exception) { + echo $exception->getMessage(), "\n"; + } +}); + +$fiber->start(); + +unset($fiber); // Destroy fiber object, executing finally block. + +echo "done\n"; + +--EXPECT-- +fiber +inner finally +finally exception +outer finally +Cannot suspend in a force closed fiber +done diff --git a/Zend/tests/fibers/unfinished-fiber.phpt b/Zend/tests/fibers/unfinished-fiber.phpt new file mode 100644 index 0000000000000..7dd9a7095f856 --- /dev/null +++ b/Zend/tests/fibers/unfinished-fiber.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test unfinished fiber +--FILE-- +start(); + +echo "done\n"; + +--EXPECT-- +fiber +done diff --git a/Zend/tests/fibers/unstarted-fiber.phpt b/Zend/tests/fibers/unstarted-fiber.phpt new file mode 100644 index 0000000000000..7724d7f3ad0f0 --- /dev/null +++ b/Zend/tests/fibers/unstarted-fiber.phpt @@ -0,0 +1,11 @@ +--TEST-- +Not starting a fiber does not leak +--FILE-- + null); + +echo "done"; + +--EXPECT-- +done diff --git a/Zend/zend.c b/Zend/zend.c index ce7d358b6d98e..b6cc668180c6c 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -34,6 +34,7 @@ #include "zend_cpuinfo.h" #include "zend_attributes.h" #include "zend_observer.h" +#include "zend_fibers.h" #include "Optimizer/zend_optimizer.h" static size_t global_map_ptr_last = 0; @@ -759,6 +760,9 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{ executor_globals->exception_class = NULL; executor_globals->exception = NULL; executor_globals->objects_store.object_buckets = NULL; + executor_globals->current_fiber = NULL; + executor_globals->next_fiber_id = 0; + executor_globals->fiber_error = NULL; #ifdef ZEND_WIN32 zend_get_windows_version_info(&executor_globals->windows_version_info); #endif @@ -1327,6 +1331,11 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_stack delayed_oplines_stack; int type = orig_type & E_ALL; + /* Fatal errors must be handled in {main} */ + if (type & E_FATAL_ERRORS && EG(current_fiber)) { + zend_error_suspend_fiber(orig_type, error_filename, error_lineno, message); + } + /* If we're executing a function during SCCP, count any warnings that may be emitted, * but don't perform any other error handling. */ if (EG(capture_warnings_during_sccp)) { diff --git a/Zend/zend_default_classes.c b/Zend/zend_default_classes.c index c0fa6f5a67b1f..7ab9b8325a39a 100644 --- a/Zend/zend_default_classes.c +++ b/Zend/zend_default_classes.c @@ -27,6 +27,7 @@ #include "zend_generators.h" #include "zend_weakrefs.h" #include "zend_enum.h" +#include "zend_fibers.h" ZEND_API void zend_register_default_classes(void) { @@ -38,4 +39,5 @@ ZEND_API void zend_register_default_classes(void) zend_register_weakref_ce(); zend_register_attribute_ce(); zend_register_enum_ce(); + zend_register_fiber_ce(); } diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index af3eacdf2888c..b49a10b621a1a 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -46,6 +46,9 @@ ZEND_API zend_class_entry *zend_ce_unhandled_match_error; /* Internal pseudo-exception that is not exposed to userland. */ static zend_class_entry zend_ce_unwind_exit; +/* Internal pseudo-exception used in destroyed fibers that is not exposed to userland. */ +static zend_class_entry zend_ce_fiber_exit; + ZEND_API void (*zend_throw_exception_hook)(zend_object *ex); static zend_object_handlers default_exception_handlers; @@ -94,7 +97,7 @@ void zend_exception_set_previous(zend_object *exception, zend_object *add_previo return; } - if (exception == add_previous || zend_is_unwind_exit(add_previous)) { + if (exception == add_previous || zend_is_unwind_exit(add_previous) || zend_is_fiber_exit(add_previous)) { OBJ_RELEASE(add_previous); return; } @@ -791,6 +794,8 @@ void zend_register_default_exception(void) /* {{{ */ zend_ce_unhandled_match_error->create_object = zend_default_exception_new; INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL); + + INIT_CLASS_ENTRY(zend_ce_fiber_exit, "FiberExit", NULL); } /* }}} */ @@ -987,7 +992,20 @@ ZEND_API ZEND_COLD void zend_throw_unwind_exit(void) EG(current_execute_data)->opline = EG(exception_op); } -ZEND_API bool zend_is_unwind_exit(zend_object *ex) +ZEND_API ZEND_COLD void zend_throw_fiber_exit(void) +{ + ZEND_ASSERT(!EG(exception)); + EG(exception) = zend_objects_new(&zend_ce_fiber_exit); + EG(opline_before_exception) = EG(current_execute_data)->opline; + EG(current_execute_data)->opline = EG(exception_op); +} + +ZEND_API bool zend_is_unwind_exit(const zend_object *ex) { return ex->ce == &zend_ce_unwind_exit; } + +ZEND_API bool zend_is_fiber_exit(const zend_object *ex) +{ + return ex->ce == &zend_ce_fiber_exit; +} diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h index 74724c24245a2..7dbc0a1e9ca3d 100644 --- a/Zend/zend_exceptions.h +++ b/Zend/zend_exceptions.h @@ -70,7 +70,9 @@ extern ZEND_API void (*zend_throw_exception_hook)(zend_object *ex); ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int severity); ZEND_API ZEND_COLD void zend_throw_unwind_exit(void); -ZEND_API bool zend_is_unwind_exit(zend_object *ex); +ZEND_API ZEND_COLD void zend_throw_fiber_exit(void); +ZEND_API bool zend_is_unwind_exit(const zend_object *ex); +ZEND_API bool zend_is_fiber_exit(const zend_object *ex); #include "zend_globals.h" diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 7117e769cc706..7fd8624af94d7 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -33,6 +33,7 @@ #include "zend_generators.h" #include "zend_vm.h" #include "zend_float.h" +#include "zend_fibers.h" #include "zend_weakrefs.h" #include "zend_inheritance.h" #include "zend_observer.h" @@ -187,6 +188,7 @@ void init_executor(void) /* {{{ */ EG(get_gc_buffer).start = EG(get_gc_buffer).end = EG(get_gc_buffer).cur = NULL; + zend_fiber_init(); zend_weakrefs_init(); EG(active) = 1; @@ -373,6 +375,8 @@ void shutdown_executor(void) /* {{{ */ zend_objects_store_free_object_storage(&EG(objects_store), fast_shutdown); + zend_fiber_shutdown(); + zend_weakrefs_shutdown(); zend_try { diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c new file mode 100644 index 0000000000000..68641b0256e89 --- /dev/null +++ b/Zend/zend_fibers.c @@ -0,0 +1,767 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Authors: Aaron Piotrowski | + | Martin Schröder | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "zend.h" +#include "zend_API.h" +#include "zend_vm.h" +#include "zend_interfaces.h" +#include "zend_exceptions.h" +#include "zend_builtin_functions.h" + +#include "zend_fibers.h" +#include "zend_fibers_arginfo.h" + +#ifdef HAVE_VALGRIND +#include "valgrind/valgrind.h" +#endif + +ZEND_API zend_class_entry *zend_ce_fiber; +static zend_class_entry *zend_ce_fiber_error; + +static zend_object_handlers zend_fiber_handlers; + +static zend_object *zend_fiber_object_create(zend_class_entry *ce); +static void zend_fiber_object_destroy(zend_object *object); + +static zend_llist zend_fiber_observers_list; + +typedef void *fcontext_t; + +typedef struct _transfer_t { + fcontext_t context; + void *data; +} transfer_t; + +extern fcontext_t make_fcontext(void *sp, size_t size, void (*fn)(transfer_t)); +extern transfer_t jump_fcontext(fcontext_t to, void *vp); + +#define ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, trace_num) do { \ + stack = EG(vm_stack); \ + stack->top = EG(vm_stack_top); \ + stack->end = EG(vm_stack_end); \ + stack_page_size = EG(vm_stack_page_size); \ + execute_data = EG(current_execute_data); \ + error_reporting = EG(error_reporting); \ + trace_num = EG(jit_trace_num); \ +} while (0) + +#define ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, trace_num) do { \ + EG(vm_stack) = stack; \ + EG(vm_stack_top) = stack->top; \ + EG(vm_stack_end) = stack->end; \ + EG(vm_stack_page_size) = stack_page_size; \ + EG(current_execute_data) = execute_data; \ + EG(error_reporting) = error_reporting; \ + EG(jit_trace_num) = trace_num; \ +} while (0) + +#if defined(MAP_STACK) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) +# define ZEND_FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON | MAP_STACK) +#else +# define ZEND_FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON) +#endif + + + +ZEND_API void zend_observer_fiber_switch_register(zend_observer_fiber_switch_handler handler) /* {{{ */ +{ + zend_llist_add_element(&zend_fiber_observers_list, &handler); +} +/* }}} */ + +zend_always_inline static void zend_observer_fiber_switch_notify(zend_fiber *from, zend_fiber *to) /* {{{ */ +{ + zend_llist_element *element; + zend_observer_fiber_switch_handler callback; + + for (element = zend_fiber_observers_list.head; element; element = element->next) { + callback = *(zend_observer_fiber_switch_handler *) element->data; + callback(from, to); + } +} +/* }}} */ + +static zend_bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) /* {{{ */ +{ + ZEND_ASSERT(size >= ZEND_FIBER_PAGESIZE + ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE); + + void *pointer; + + stack->size = (size + ZEND_FIBER_PAGESIZE - 1) / ZEND_FIBER_PAGESIZE * ZEND_FIBER_PAGESIZE; + size_t msize = stack->size + ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE; + +#ifdef PHP_WIN32 + pointer = VirtualAlloc(0, msize, MEM_COMMIT, PAGE_READWRITE); + + if (!pointer) { + return 0; + } + +# if ZEND_FIBER_GUARD_PAGES + DWORD protect; + + if (!VirtualProtect(pointer, ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE, PAGE_READWRITE | PAGE_GUARD, &protect)) { + VirtualFree(pointer, 0, MEM_RELEASE); + return 0; + } +# endif +#else + pointer = mmap(NULL, msize, PROT_READ | PROT_WRITE, ZEND_FIBER_STACK_FLAGS, -1, 0); + + if (pointer == MAP_FAILED) { + return 0; + } + +# if ZEND_FIBER_GUARD_PAGES + if (mprotect(pointer, ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE, PROT_NONE) < 0) { + munmap(pointer, msize); + return 0; + } +# endif +#endif + + stack->pointer = (void *) ((uintptr_t) pointer + ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE); + +#ifdef VALGRIND_STACK_REGISTER + uintptr_t base = (uintptr_t) stack->pointer; + stack->valgrind = VALGRIND_STACK_REGISTER(base, base + msize - ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE); +#endif + + return 1; +} +/* }}} */ + +void zend_fiber_stack_free(zend_fiber_stack *stack) /* {{{ */ +{ + if (!stack->pointer) { + return; + } + +#ifdef VALGRIND_STACK_DEREGISTER + VALGRIND_STACK_DEREGISTER(stack->valgrind); +#endif + + void *pointer = (void *) ((uintptr_t) stack->pointer - ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE); + +#ifdef PHP_WIN32 + VirtualFree(pointer, 0, MEM_RELEASE); +#else + size_t length = stack->size + ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE; + + munmap(pointer, length); +#endif + + stack->pointer = NULL; +} +/* }}} */ + +const char *zend_fiber_backend_info(void) /* {{{ */ +{ + return "assembler (boost.context v1.76.0)"; +} +/* }}} */ + +static ZEND_NORETURN void zend_fiber_trampoline(transfer_t transfer) /* {{{ */ +{ + zend_fiber_context *context = transfer.data; + + context->caller = transfer.context; + + context->function(context); + + context->self = NULL; + + zend_fiber_suspend_context(context); + + abort(); +} +/* }}} */ + +ZEND_API zend_bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_coroutine coroutine, size_t stack_size) /* {{{ */ +{ + if (UNEXPECTED(!zend_fiber_stack_allocate(&context->stack, stack_size))) { + return 0; + } + + // Stack grows down, calculate the top of the stack. make_fcontext then shifts pointer to lower 16-byte boundary. + void *stack = (void *) ((uintptr_t) context->stack.pointer + context->stack.size); + + context->self = make_fcontext(stack, context->stack.size, zend_fiber_trampoline); + + if (UNEXPECTED(!context->self)) { + zend_fiber_stack_free(&context->stack); + return 0; + } + + context->function = coroutine; + context->caller = NULL; + + return 1; +} +/* }}} */ + +ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context) /* {{{ */ +{ + if (!context) { + return; + } + + zend_fiber_stack_free(&context->stack); +} +/* }}} */ + +ZEND_API void zend_fiber_switch_context(zend_fiber_context *to) /* {{{ */ +{ + ZEND_ASSERT(to && to->self && to->stack.pointer && "Invalid fiber context"); + + transfer_t transfer = jump_fcontext(to->self, to); + + to->self = transfer.context; +} +/* }}} */ + +ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current) /* {{{ */ +{ + ZEND_ASSERT(current && current->caller && current->stack.pointer && "Invalid fiber context"); + + transfer_t transfer = jump_fcontext(current->caller, NULL); + + current->caller = transfer.context; +} +/* }}} */ + +static void zend_fiber_suspend(zend_fiber *fiber) /* {{{ */ +{ + zend_vm_stack stack; + size_t stack_page_size; + zend_execute_data *execute_data; + int error_reporting; + uint32_t jit_trace_num; + + ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num); + + zend_fiber_suspend_context(&fiber->context); + + ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num); +} +/* }}} */ + +static void zend_fiber_switch_to(zend_fiber *fiber) /* {{{ */ +{ + zend_fiber *previous; + zend_vm_stack stack; + size_t stack_page_size; + zend_execute_data *execute_data; + int error_reporting; + uint32_t jit_trace_num; + + previous = EG(current_fiber); + + zend_observer_fiber_switch_notify(previous, fiber); + + ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num); + + EG(current_fiber) = fiber; + + zend_fiber_switch_context(&fiber->context); + + EG(current_fiber) = previous; + + ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num); + + zend_observer_fiber_switch_notify(fiber, previous); + + if (UNEXPECTED(EG(fiber_error))) { + if (previous) { + zend_fiber_suspend(previous); // Still in fiber, suspend again until in {main}. + abort(); // This fiber should never be resumed. + } + + zend_fiber_error *error = EG(fiber_error); + EG(fiber_error) = NULL; // Null error reference so suspended fibers do not issue error again during shutdown. + zend_error_at_noreturn(error->type, error->filename, error->lineno, "%s", ZSTR_VAL(error->message)); + } +} +/* }}} */ + +/* {{{ */ +ZEND_COLD ZEND_NORETURN void zend_error_suspend_fiber( + int orig_type, const char *error_filename, uint32_t error_lineno, zend_string *message) +{ + ZEND_ASSERT(EG(current_fiber) && "Must be within an active fiber!"); + + zend_fiber_error error; + + error.type = orig_type; + error.filename = error_filename; + error.lineno = error_lineno; + error.message = message; + + EG(fiber_error) = &error; + + zend_fiber_suspend(EG(current_fiber)); + + abort(); // This fiber should never be resumed. +} +/* }}} */ + +static zend_always_inline zend_vm_stack zend_fiber_vm_stack_alloc(size_t size) /* {{{ */ +{ + zend_vm_stack page = emalloc(size); + + page->top = ZEND_VM_STACK_ELEMENTS(page); + page->end = (zval *) ((uintptr_t) page + size); + page->prev = NULL; + + return page; +} +/* }}} */ + +static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context) /* {{{ */ +{ + zend_fiber *fiber = EG(current_fiber); + ZEND_ASSERT(fiber); + + zend_long error_reporting = INI_INT("error_reporting"); + if (!error_reporting && !INI_STR("error_reporting")) { + error_reporting = E_ALL; + } + + zend_vm_stack stack = zend_fiber_vm_stack_alloc(ZEND_FIBER_VM_STACK_SIZE); + EG(vm_stack) = stack; + EG(vm_stack_top) = stack->top + ZEND_CALL_FRAME_SLOT; + EG(vm_stack_end) = stack->end; + EG(vm_stack_page_size) = ZEND_FIBER_VM_STACK_SIZE; + + fiber->execute_data = (zend_execute_data *) stack->top; + + ZEND_SECURE_ZERO(fiber->execute_data, sizeof(zend_execute_data)); + + EG(current_execute_data) = fiber->execute_data; + EG(jit_trace_num) = 0; + EG(error_reporting) = error_reporting; + + fiber->fci.retval = &fiber->value; + + // Reference added while running, removed when suspended, and added again once resumed. + GC_ADDREF(&fiber->std); + + zend_call_function(&fiber->fci, &fiber->fci_cache); + + if (EG(exception)) { + if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { + if (EXPECTED(zend_is_fiber_exit(EG(exception)))) { + zend_clear_exception(); + } else { + zend_exception_error(EG(exception), E_ERROR); + } + } else { + fiber->status = ZEND_FIBER_STATUS_THREW; + } + } else { + fiber->status = ZEND_FIBER_STATUS_RETURNED; + } + + zend_vm_stack_destroy(); + fiber->execute_data = NULL; + + // Remove reference added at last resume. + GC_DELREF(&fiber->std); +} +/* }}} */ + +static zend_object *zend_fiber_object_create(zend_class_entry *ce) /* {{{ */ +{ + zend_fiber *fiber; + + fiber = emalloc(sizeof(zend_fiber)); + ZEND_SECURE_ZERO(fiber, sizeof(zend_fiber)); + + fiber->id = EG(next_fiber_id)++; + + zend_object_std_init(&fiber->std, ce); + fiber->std.handlers = &zend_fiber_handlers; + + ZVAL_UNDEF(&fiber->value); + + zend_hash_index_add_ptr(&EG(fibers), fiber->id, fiber); + + return &fiber->std; +} +/* }}} */ + +static void zend_fiber_object_destroy(zend_object *object) /* {{{ */ +{ + zend_fiber *fiber = (zend_fiber *) object; + + if (UNEXPECTED(fiber->status == ZEND_FIBER_STATUS_INIT)) { + // Fiber was never started, so we need to release the reference to the callback. + zval_ptr_dtor(&fiber->fci.function_name); + } else { + if (fiber->status == ZEND_FIBER_STATUS_SUSPENDED) { + fiber->status = ZEND_FIBER_STATUS_SHUTDOWN; + zend_fiber_switch_to(fiber); + } + + zval_ptr_dtor(&fiber->value); + } + + zend_hash_index_del(&EG(fibers), fiber->id); + + zend_fiber_destroy_context(&fiber->context); + + zend_object_std_dtor(&fiber->std); +} +/* }}} */ + +void zend_fiber_cleanup(void) /* {{{ */ +{ + zend_fiber *fiber; + + ZEND_HASH_REVERSE_FOREACH_PTR(&EG(fibers), fiber) { + if (fiber->status == ZEND_FIBER_STATUS_SUSPENDED) { + fiber->status = ZEND_FIBER_STATUS_SHUTDOWN; + GC_ADDREF(&fiber->std); + zend_fiber_switch_to(fiber); + } + } ZEND_HASH_FOREACH_END(); +} +/* }}} */ + +/* {{{ proto Fiber::__construct(callable $callback) */ +ZEND_METHOD(Fiber, __construct) +{ + zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis()); + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) + Z_PARAM_FUNC_EX(fiber->fci, fiber->fci_cache, 0, 0) + ZEND_PARSE_PARAMETERS_END(); + + // Keep a reference to closures or callable objects until the fiber is started. + Z_TRY_ADDREF(fiber->fci.function_name); +} +/* }}} */ + +/* {{{ proto mixed Fiber::start(mixed ...$args) */ +ZEND_METHOD(Fiber, start) +{ + zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis()); + + if (fiber->status != ZEND_FIBER_STATUS_INIT) { + zend_throw_error(zend_ce_fiber_error, "Cannot start a fiber that has already been started"); + return; + } + + fiber->fci.params = ZEND_CALL_ARG(execute_data, 1); + fiber->fci.param_count = ZEND_CALL_NUM_ARGS(execute_data); + + if (!zend_fiber_init_context(&fiber->context, zend_fiber_execute, EG(fiber_stack_size))) { + zend_throw_error(NULL, "Could not create fiber context"); + return; + } + + fiber->status = ZEND_FIBER_STATUS_RUNNING; + + zend_fiber_switch_to(fiber); + + zval_ptr_dtor(&fiber->fci.function_name); + + if (fiber->status & ZEND_FIBER_STATUS_FINISHED) { + RETURN_NULL(); + } + + RETVAL_COPY_VALUE(&fiber->value); + ZVAL_UNDEF(&fiber->value); +} +/* }}} */ + +/* {{{ proto mixed Fiber::suspend(mixed $value) */ +ZEND_METHOD(Fiber, suspend) +{ + zend_fiber *fiber = EG(current_fiber); + zval *error, *value = NULL; + + if (UNEXPECTED(!fiber)) { + zend_throw_error(zend_ce_fiber_error, "Cannot suspend outside of a fiber"); + return; + } + + if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_RUNNING)) { + if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { + zend_throw_error(zend_ce_fiber_error, "Cannot suspend in a force closed fiber"); + } else { + zend_throw_error(zend_ce_fiber_error, "Cannot suspend in a fiber that is not running"); + } + return; + } + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(value); + ZEND_PARSE_PARAMETERS_END(); + + if (value) { + ZVAL_COPY(&fiber->value, value); + } else { + ZVAL_NULL(&fiber->value); + } + + fiber->execute_data = execute_data; + fiber->status = ZEND_FIBER_STATUS_SUSPENDED; + + // Remove running reference while suspended. + GC_DELREF(&fiber->std); + + zend_fiber_suspend(fiber); + + if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { + // This occurs when the fiber is GC'ed while suspended, do not add a ref. + zend_throw_fiber_exit(); + return; + } + + fiber->status = ZEND_FIBER_STATUS_RUNNING; + // Add reference while fiber is running. + GC_ADDREF(&fiber->std); + + if (!fiber->error) { + RETVAL_COPY_VALUE(&fiber->value); + ZVAL_UNDEF(&fiber->value); + return; + } + + error = fiber->error; + fiber->error = NULL; + + execute_data->opline--; + zend_throw_exception_object(error); + execute_data->opline++; +} +/* }}} */ + +/* {{{ proto mixed Fiber::resume(mixed $value = null) */ +ZEND_METHOD(Fiber, resume) +{ + zend_fiber *fiber; + zval *value = NULL; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(value); + ZEND_PARSE_PARAMETERS_END(); + + fiber = (zend_fiber *) Z_OBJ_P(getThis()); + + if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_SUSPENDED)) { + zend_throw_error(zend_ce_fiber_error, "Cannot resume a fiber that is not suspended"); + return; + } + + if (value) { + ZVAL_COPY(&fiber->value, value); + } else { + ZVAL_NULL(&fiber->value); + } + + fiber->status = ZEND_FIBER_STATUS_RUNNING; + + zend_fiber_switch_to(fiber); + + if (fiber->status & ZEND_FIBER_STATUS_FINISHED) { + RETURN_NULL(); + } + + RETVAL_COPY_VALUE(&fiber->value); + ZVAL_UNDEF(&fiber->value); +} +/* }}} */ + +/* {{{ proto mixed Fiber::throw(Throwable $exception) */ +ZEND_METHOD(Fiber, throw) +{ + zend_fiber *fiber; + zval *exception; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) + Z_PARAM_OBJECT_OF_CLASS_EX(exception, zend_ce_throwable, 0, 0) + ZEND_PARSE_PARAMETERS_END(); + + fiber = (zend_fiber *) Z_OBJ_P(getThis()); + + if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_SUSPENDED)) { + zend_throw_error(zend_ce_fiber_error, "Cannot resume a fiber that is not suspended"); + return; + } + + Z_ADDREF_P(exception); + fiber->error = exception; + + fiber->status = ZEND_FIBER_STATUS_RUNNING; + + zend_fiber_switch_to(fiber); + + if (fiber->status & ZEND_FIBER_STATUS_FINISHED) { + RETURN_NULL(); + } + + RETVAL_COPY_VALUE(&fiber->value); + ZVAL_UNDEF(&fiber->value); +} +/* }}} */ + +/* {{{ proto bool Fiber::isStarted() */ +ZEND_METHOD(Fiber, isStarted) +{ + zend_fiber *fiber; + + ZEND_PARSE_PARAMETERS_NONE(); + + fiber = (zend_fiber *) Z_OBJ_P(getThis()); + + RETURN_BOOL(fiber->status != ZEND_FIBER_STATUS_INIT); +} +/* }}} */ + +/* {{{ proto bool Fiber::isSuspended() */ +ZEND_METHOD(Fiber, isSuspended) +{ + zend_fiber *fiber; + + ZEND_PARSE_PARAMETERS_NONE(); + + fiber = (zend_fiber *) Z_OBJ_P(getThis()); + + RETURN_BOOL(fiber->status == ZEND_FIBER_STATUS_SUSPENDED); +} +/* }}} */ + +/* {{{ proto bool Fiber::isRunning() */ +ZEND_METHOD(Fiber, isRunning) +{ + zend_fiber *fiber; + + ZEND_PARSE_PARAMETERS_NONE(); + + fiber = (zend_fiber *) Z_OBJ_P(getThis()); + + RETURN_BOOL(fiber->status == ZEND_FIBER_STATUS_RUNNING); +} +/* }}} */ + +/* {{{ proto bool Fiber::isTerminated() */ +ZEND_METHOD(Fiber, isTerminated) +{ + zend_fiber *fiber; + + ZEND_PARSE_PARAMETERS_NONE(); + + fiber = (zend_fiber *) Z_OBJ_P(getThis()); + + RETURN_BOOL(fiber->status & ZEND_FIBER_STATUS_FINISHED); +} +/* }}} */ + +/* {{{ proto mixed Fiber::getReturn() */ +ZEND_METHOD(Fiber, getReturn) +{ + zend_fiber *fiber; + + ZEND_PARSE_PARAMETERS_NONE(); + + fiber = (zend_fiber *) Z_OBJ_P(getThis()); + + if (fiber->status != ZEND_FIBER_STATUS_RETURNED) { + const char *message; + + if (fiber->status == ZEND_FIBER_STATUS_INIT) { + message = "The fiber has not been started"; + } else if (fiber->status == ZEND_FIBER_STATUS_THREW) { + message = "The fiber threw an exception"; + } else { + message = "The fiber has not returned"; + } + + zend_throw_error(zend_ce_fiber_error, "Cannot get fiber return value: %s", message); + return; + } + + RETURN_COPY(&fiber->value); +} +/* }}} */ + +/* {{{ proto Fiber|null Fiber::this() */ +ZEND_METHOD(Fiber, this) +{ + zend_fiber *fiber; + + ZEND_PARSE_PARAMETERS_NONE(); + + fiber = EG(current_fiber); + + if (!fiber) { + RETURN_NULL(); + } + + RETURN_OBJ_COPY(&fiber->std); +} +/* }}} */ + +/* {{{ proto FiberError::__construct(string $message) */ +ZEND_METHOD(FiberError, __construct) +{ + zend_throw_error( + NULL, + "The \"%s\" class is reserved for internal use and cannot be manually instantiated", + ZSTR_VAL(Z_OBJCE_P(getThis())->name) + ); +} +/* }}} */ + + +void zend_register_fiber_ce(void) +{ + zend_ce_fiber = register_class_Fiber(); + zend_ce_fiber->create_object = zend_fiber_object_create; + zend_ce_fiber->serialize = zend_class_serialize_deny; + zend_ce_fiber->unserialize = zend_class_unserialize_deny; + + zend_fiber_handlers = std_object_handlers; + zend_fiber_handlers.free_obj = zend_fiber_object_destroy; + zend_fiber_handlers.clone_obj = NULL; + + zend_ce_fiber_error = register_class_FiberError(zend_ce_error); + zend_ce_fiber_error->create_object = zend_ce_error->create_object; +} + +void zend_fiber_init(void) +{ + EG(current_fiber) = NULL; + EG(next_fiber_id) = 0; + EG(fiber_error) = NULL; + + zend_hash_init(&EG(fibers), 0, NULL, NULL, 0); + zend_llist_init(&zend_fiber_observers_list, sizeof(zend_observer_fiber_switch_handler), NULL, 0); +} + +void zend_fiber_shutdown(void) +{ + zend_hash_destroy(&EG(fibers)); + zend_llist_destroy(&zend_fiber_observers_list); +} diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h new file mode 100644 index 0000000000000..69cd8de7ff291 --- /dev/null +++ b/Zend/zend_fibers.h @@ -0,0 +1,129 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Authors: Aaron Piotrowski | + | Martin Schröder | + +----------------------------------------------------------------------+ +*/ + +#include "zend_API.h" +#include "zend_types.h" + +#ifndef ZEND_FIBERS_H +#define ZEND_FIBERS_H + +BEGIN_EXTERN_C() + +void zend_register_fiber_ce(void); +void zend_fiber_init(void); +void zend_fiber_cleanup(void); +void zend_fiber_shutdown(void); + +extern ZEND_API zend_class_entry *zend_ce_fiber; + +typedef struct _zend_fiber_context zend_fiber_context; + +typedef void (*zend_fiber_coroutine)(zend_fiber_context *context); + +typedef struct _zend_fiber_stack { + void *pointer; + size_t size; + +#ifdef HAVE_VALGRIND + int valgrind; +#endif +} zend_fiber_stack; + +typedef struct _zend_fiber_context { + void *self; + void *caller; + zend_fiber_coroutine function; + zend_fiber_stack stack; +} zend_fiber_context; + +#if _POSIX_MAPPED_FILES +# include +# include +# define ZEND_FIBER_PAGESIZE sysconf(_SC_PAGESIZE) +#else +# define ZEND_FIBER_PAGESIZE 4096 +#endif + +#define ZEND_FIBER_GUARD_PAGES 1 + +#define ZEND_FIBER_DEFAULT_STACK_SIZE (ZEND_FIBER_PAGESIZE * (((sizeof(void *)) < 8) ? 512 : 2048)) + +typedef struct _zend_fiber { + /* Fiber PHP object handle. */ + zend_object std; + + /* Unique ID assigned to this fiber. */ + zend_long id; + + /* Status of the fiber, one of the ZEND_FIBER_STATUS_* constants. */ + zend_uchar status; + + /* Callback and info / cache to be used when fiber is started. */ + zend_fcall_info fci; + zend_fcall_info_cache fci_cache; + + /* Context of this fiber, will be initialized during call to Fiber::start(). */ + zend_fiber_context context; + + /* Current Zend VM execute data being run by the fiber. */ + zend_execute_data *execute_data; + + /* Exception to be thrown from Fiber::suspend(). */ + zval *error; + + /* Storage for temporaries and fiber return value. */ + zval value; +} zend_fiber; + +typedef struct _zend_fiber_error { + int type; + const char *filename; + uint32_t lineno; + zend_string *message; +} zend_fiber_error; + +typedef void (*zend_observer_fiber_switch_handler)(zend_fiber *from, zend_fiber *to); + +ZEND_API void zend_observer_fiber_switch_register(zend_observer_fiber_switch_handler handler); + +static const zend_uchar ZEND_FIBER_STATUS_INIT = 0x0; +static const zend_uchar ZEND_FIBER_STATUS_SUSPENDED = 0x1; +static const zend_uchar ZEND_FIBER_STATUS_RUNNING = 0x2; +static const zend_uchar ZEND_FIBER_STATUS_RETURNED = 0x4; +static const zend_uchar ZEND_FIBER_STATUS_THREW = 0x8; +static const zend_uchar ZEND_FIBER_STATUS_SHUTDOWN = 0x10; + +static const zend_uchar ZEND_FIBER_STATUS_FINISHED = 0x1c; + +const char *zend_fiber_backend_info(void); + +ZEND_API zend_bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_coroutine coroutine, size_t stack_size); +ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context); + +ZEND_COLD ZEND_NORETURN void zend_error_suspend_fiber( + int orig_type, const char *error_filename, uint32_t error_lineno, zend_string *message); + +ZEND_API void zend_fiber_switch_context(zend_fiber_context *to); +ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current); + +#define ZEND_FIBER_VM_STACK_SIZE (1024 * sizeof(zval)) + +END_EXTERN_C() + +#endif diff --git a/Zend/zend_fibers.stub.php b/Zend/zend_fibers.stub.php new file mode 100644 index 0000000000000..27f95abdfec24 --- /dev/null +++ b/Zend/zend_fibers.stub.php @@ -0,0 +1,36 @@ +ce_flags |= ZEND_ACC_FINAL; + + return class_entry; +} + +static zend_class_entry *register_class_FiberError(zend_class_entry *class_entry_Error) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "FiberError", class_FiberError_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_Error); + class_entry->ce_flags |= ZEND_ACC_FINAL; + + return class_entry; +} diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index dce548d79e35f..02da9ac86393e 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -61,6 +61,8 @@ END_EXTERN_C() typedef struct _zend_vm_stack *zend_vm_stack; typedef struct _zend_ini_entry zend_ini_entry; +typedef struct _zend_fiber zend_fiber; +typedef struct _zend_fiber_error zend_fiber_error; struct _zend_compiler_globals { @@ -249,6 +251,21 @@ struct _zend_executor_globals { zend_get_gc_buffer get_gc_buffer; + /* Active fiber, NULL when in main thread. */ + zend_fiber *current_fiber; + + /* Next fiber ID. */ + zend_long next_fiber_id; + + /* Default fiber C stack size. */ + zend_long fiber_stack_size; + + /* Pointer to fatal error that occurred in a fiber while switching to {main}. */ + zend_fiber_error *fiber_error; + + /* Currently executing fibers. */ + HashTable fibers; + void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index 06d4123e7f7fa..d67057899064d 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -281,6 +281,12 @@ char *alloca(); # define ZEND_NORETURN #endif +#if __has_attribute(force_align_arg_pointer) +# define ZEND_STACK_ALIGNED __attribute__((force_align_arg_pointer)) +#else +# define ZEND_STACK_ALIGNED +#endif + #if (defined(__GNUC__) && __GNUC__ >= 3 && !defined(__INTEL_COMPILER) && !defined(DARWIN) && !defined(__hpux) && !defined(_AIX) && !defined(__osf__)) # define HAVE_NORETURN_ALIAS # define HAVE_ATTRIBUTE_WEAK diff --git a/configure.ac b/configure.ac index eaf38d5497aec..92eabc3825516 100644 --- a/configure.ac +++ b/configure.ac @@ -1181,6 +1181,53 @@ dnl ---------------------------------------------------------------------------- PHP_HELP_SEPARATOR([Zend:]) PHP_CONFIGURE_PART(Configuring Zend) +AC_MSG_CHECKING(for fiber switching context) +fibers="yes" + +AS_CASE([$host_cpu], + [x86_64*|amd64*], [fiber_cpu="x86_64"], + [x86*|amd*|i?86*|pentium], [fiber_cpu="i386"], + [aarch64*|arm64*], [fiber_cpu="arm64"], + [arm*], [fiber_cpu="arm32"], + [ppc64*], [fiber_cpu="ppc64"], + [powerpc*], [fiber_cpu="ppc32"], + [mips64*], [fiber_cpu="mips64"], + [mips*], [fiber_cpu="mips32"], + [fiber_cpu="unknown"] +) + +AS_CASE([$host_os], + [darwin*], [fiber_os="mac"], + [fiber_os="other"] +) + +AS_CASE([$fiber_cpu], + [x86_64], [fiber_asm_file_prefix="x86_64_sysv"], + [i386], [fiber_asm_file_prefix="i386_sysv"], + [arm64], [fiber_asm_file_prefix="arm64_aapcs"], + [arm32], [fiber_asm_file_prefix="arm_aapcs"], + [ppc64], [fiber_asm_file_prefix="ppc64_sysv"], + [ppc32], [fiber_asm_file_prefix="ppc_sysv"], + [mips64], [fiber_asm_file_prefix="mips64_n64"], + [mips32], [fiber_asm_file_prefix="mips32_o32"], + [fiber_asm_file_prefix="unknown"] +) + +if test "$fiber_os" = 'mac'; then + fiber_asm_file="combined_sysv_macho_gas.S" +elif test "$fiber_asm_file_prefix" != 'unknown'; then + fiber_asm_file="${fiber_asm_file_prefix}_elf_gas.S" +else + fibers="no" +fi + +if test "$fibers" = 'yes'; then + PHP_ADD_SOURCES(Zend/asm, make_${fiber_asm_file} jump_${fiber_asm_file}) + AC_MSG_RESULT([$fiber_asm_file]) +else + AC_MSG_ERROR([Unable to determine platform!]) +fi + LIBZEND_BASIC_CHECKS LIBZEND_DLSYM_CHECK LIBZEND_OTHER_CHECKS @@ -1544,7 +1591,7 @@ PHP_ADD_SOURCES(Zend, \ zend_closures.c zend_weakrefs.c zend_float.c zend_string.c zend_signal.c zend_generators.c \ zend_virtual_cwd.c zend_ast.c zend_objects.c zend_object_handlers.c zend_objects_API.c \ zend_default_classes.c zend_inheritance.c zend_smart_str.c zend_cpuinfo.c zend_gdb.c \ - zend_observer.c zend_system_id.c zend_enum.c \ + zend_observer.c zend_system_id.c zend_enum.c zend_fibers.c \ Optimizer/zend_optimizer.c \ Optimizer/pass1.c \ Optimizer/pass3.c \ @@ -1568,6 +1615,7 @@ PHP_ADD_SOURCES(Zend, \ Optimizer/zend_dump.c \ , -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) + PHP_ADD_BUILD_DIR(main main/streams) PHP_ADD_BUILD_DIR(TSRM) PHP_ADD_BUILD_DIR(Zend Zend/Optimizer) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 36eb768387a41..57a446ca94d66 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -44,6 +44,7 @@ #include "zend_builtin_functions.h" #include "zend_smart_str.h" #include "zend_enum.h" +#include "zend_fibers.h" #include "php_reflection_arginfo.h" /* Key used to avoid leaking addresses in ReflectionProperty::getId() */ @@ -91,6 +92,7 @@ PHPAPI zend_class_entry *reflection_attribute_ptr; PHPAPI zend_class_entry *reflection_enum_ptr; PHPAPI zend_class_entry *reflection_enum_unit_case_ptr; PHPAPI zend_class_entry *reflection_enum_backed_case_ptr; +PHPAPI zend_class_entry *reflection_fiber_ptr; /* Exception throwing macro */ #define _DO_THROW(msg) \ @@ -154,6 +156,7 @@ typedef enum { REF_TYPE_OTHER, /* Must be 0 */ REF_TYPE_FUNCTION, REF_TYPE_GENERATOR, + REF_TYPE_FIBER, REF_TYPE_PARAMETER, REF_TYPE_TYPE, REF_TYPE_PROPERTY, @@ -266,6 +269,7 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ break; } case REF_TYPE_GENERATOR: + case REF_TYPE_FIBER: case REF_TYPE_CLASS_CONSTANT: case REF_TYPE_OTHER: break; @@ -6745,6 +6749,153 @@ ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue) ZVAL_COPY_OR_DUP(return_value, member_p); } +/* {{{ proto ReflectionFiber::__construct(Fiber $fiber) */ +ZEND_METHOD(ReflectionFiber, __construct) +{ + zval *fiber, *object; + reflection_object *intern; + + object = ZEND_THIS; + intern = Z_REFLECTION_P(object); + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) + Z_PARAM_OBJECT_OF_CLASS_EX(fiber, zend_ce_fiber, 0, 0) + ZEND_PARSE_PARAMETERS_END(); + + if (intern->ce) { + zval_ptr_dtor(&intern->obj); + } + + intern->ref_type = REF_TYPE_FIBER; + ZVAL_OBJ_COPY(&intern->obj, Z_OBJ_P(fiber)); + intern->ce = zend_ce_fiber; +} +/* }}} */ + +/* {{{ proto Fiber ReflectionFiber::getFiber() */ +ZEND_METHOD(ReflectionFiber, getFiber) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + RETURN_OBJ_COPY(Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj)); +} +/* }}} */ + +#define REFLECTION_CHECK_VALID_FIBER(fiber) do { \ + if (fiber == NULL || fiber->status == ZEND_FIBER_STATUS_INIT || fiber->status & ZEND_FIBER_STATUS_FINISHED) { \ + zend_throw_error(NULL, "Cannot fetch information from a fiber that has not been started or is terminated"); \ + return; \ + } \ + } while (0) + +/* {{{ proto array ReflectionFiber::getTrace(int $options) */ +ZEND_METHOD(ReflectionFiber, getTrace) +{ + zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); + zend_long options = DEBUG_BACKTRACE_PROVIDE_OBJECT; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(options); + ZEND_PARSE_PARAMETERS_END(); + + REFLECTION_CHECK_VALID_FIBER(fiber); + + if (EG(current_fiber) != fiber) { + // No need to replace current execute data if within the current fiber. + EG(current_execute_data) = fiber->execute_data; + } + + zend_fetch_debug_backtrace(return_value, 0, options, 0); + + EG(current_execute_data) = execute_data; // Restore original execute data. +} +/* }}} */ + +/* {{{ proto int ReflectionFiber::getExecutingLine() */ +ZEND_METHOD(ReflectionFiber, getExecutingLine) +{ + zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); + zend_execute_data *prev_execute_data; + + ZEND_PARSE_PARAMETERS_NONE(); + + REFLECTION_CHECK_VALID_FIBER(fiber); + + if (EG(current_fiber) == fiber) { + prev_execute_data = execute_data->prev_execute_data; + } else { + prev_execute_data = fiber->execute_data->prev_execute_data; + } + + RETURN_LONG(prev_execute_data->opline->lineno); +} +/* }}} */ + +/* {{{ proto string ReflectionFiber::getExecutingFile() */ +ZEND_METHOD(ReflectionFiber, getExecutingFile) +{ + zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); + zend_execute_data *prev_execute_data; + + ZEND_PARSE_PARAMETERS_NONE(); + + REFLECTION_CHECK_VALID_FIBER(fiber); + + if (EG(current_fiber) == fiber) { + prev_execute_data = execute_data->prev_execute_data; + } else { + prev_execute_data = fiber->execute_data->prev_execute_data; + } + + RETURN_STR_COPY(prev_execute_data->func->op_array.filename); +} +/* }}} */ + +/* {{{ proto bool ReflectionFiber::isStarted() */ +ZEND_METHOD(ReflectionFiber, isStarted) +{ + zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + RETURN_BOOL(fiber->status != ZEND_FIBER_STATUS_INIT); +} +/* }}} */ + +/* {{{ proto bool ReflectionFiber::isSuspended() */ +ZEND_METHOD(ReflectionFiber, isSuspended) +{ + zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + RETURN_BOOL(fiber->status == ZEND_FIBER_STATUS_SUSPENDED); +} +/* }}} */ + +/* {{{ proto bool ReflectionFiber::isRunning() */ +ZEND_METHOD(ReflectionFiber, isRunning) +{ + zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + RETURN_BOOL(fiber->status == ZEND_FIBER_STATUS_RUNNING); +} +/* }}} */ + +/* {{{ proto bool ReflectionFiber::isTerminated() */ +ZEND_METHOD(ReflectionFiber, isTerminated) +{ + zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); + + ZEND_PARSE_PARAMETERS_NONE(); + + RETURN_BOOL(fiber->status & ZEND_FIBER_STATUS_FINISHED); +} +/* }}} */ + /* {{{ _reflection_write_property */ static zval *_reflection_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) { @@ -6863,6 +7014,9 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ reflection_enum_backed_case_ptr = register_class_ReflectionEnumBackedCase(reflection_enum_unit_case_ptr); reflection_init_class_handlers(reflection_enum_backed_case_ptr); + reflection_fiber_ptr = register_class_ReflectionFiber(); + reflection_init_class_handlers(reflection_fiber_ptr); + REGISTER_REFLECTION_CLASS_CONST_LONG(attribute, "IS_INSTANCEOF", REFLECTION_ATTRIBUTE_IS_INSTANCEOF); REFLECTION_G(key_initialized) = 0; diff --git a/ext/reflection/php_reflection.h b/ext/reflection/php_reflection.h index de368a86c0bb3..68ee426ab2f1e 100644 --- a/ext/reflection/php_reflection.h +++ b/ext/reflection/php_reflection.h @@ -46,6 +46,7 @@ extern PHPAPI zend_class_entry *reflection_attribute_ptr; extern PHPAPI zend_class_entry *reflection_enum_ptr; extern PHPAPI zend_class_entry *reflection_enum_unit_case_ptr; extern PHPAPI zend_class_entry *reflection_enum_backed_case_ptr; +extern PHPAPI zend_class_entry *reflection_fiber_ptr; PHPAPI void zend_reflection_class_factory(zend_class_entry *ce, zval *object); diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index d8486a2804b15..18ca770872899 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -720,3 +720,24 @@ public function __construct(object|string $class, string $constant) {} public function getBackingValue(): int|string {} } + +final class ReflectionFiber +{ + public function __construct(Fiber $fiber) {} + + public function getFiber(): Fiber {} + + public function getExecutingFile(): string {} + + public function getExecutingLine(): int {} + + public function getTrace(int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT): array {} + + public function isStarted(): bool {} + + public function isSuspended(): bool {} + + public function isRunning(): bool {} + + public function isTerminated(): bool {} +} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 85a59378ade75..693ba283ae71c 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6f36123e16ed34e45a527094ab643b6b57669a5d */ + * Stub hash: 6eecf22be201eacd72f57bbc665e77c805162842 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -523,6 +523,29 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ReflectionEnumBackedCase_getBackingValue, 0, 0, MAY_BE_LONG|MAY_BE_STRING) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionFiber___construct, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, fiber, Fiber, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionFiber_getFiber, 0, 0, Fiber, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionFiber_getExecutingFile arginfo_class_ReflectionFunction___toString + +#define arginfo_class_ReflectionFiber_getExecutingLine arginfo_class_ReflectionAttribute_getTarget + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFiber_getTrace, 0, 0, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "DEBUG_BACKTRACE_PROVIDE_OBJECT") +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionFiber_isStarted arginfo_class_ReflectionClass_isEnum + +#define arginfo_class_ReflectionFiber_isSuspended arginfo_class_ReflectionClass_isEnum + +#define arginfo_class_ReflectionFiber_isRunning arginfo_class_ReflectionClass_isEnum + +#define arginfo_class_ReflectionFiber_isTerminated arginfo_class_ReflectionClass_isEnum + ZEND_METHOD(Reflection, getModifierNames); ZEND_METHOD(ReflectionClass, __clone); @@ -736,6 +759,15 @@ ZEND_METHOD(ReflectionEnumUnitCase, __construct); ZEND_METHOD(ReflectionEnumUnitCase, getEnum); ZEND_METHOD(ReflectionEnumBackedCase, __construct); ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue); +ZEND_METHOD(ReflectionFiber, __construct); +ZEND_METHOD(ReflectionFiber, getFiber); +ZEND_METHOD(ReflectionFiber, getExecutingFile); +ZEND_METHOD(ReflectionFiber, getExecutingLine); +ZEND_METHOD(ReflectionFiber, getTrace); +ZEND_METHOD(ReflectionFiber, isStarted); +ZEND_METHOD(ReflectionFiber, isSuspended); +ZEND_METHOD(ReflectionFiber, isRunning); +ZEND_METHOD(ReflectionFiber, isTerminated); static const zend_function_entry class_ReflectionException_methods[] = { @@ -1069,6 +1101,20 @@ static const zend_function_entry class_ReflectionEnumBackedCase_methods[] = { ZEND_FE_END }; + +static const zend_function_entry class_ReflectionFiber_methods[] = { + ZEND_ME(ReflectionFiber, __construct, arginfo_class_ReflectionFiber___construct, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFiber, getFiber, arginfo_class_ReflectionFiber_getFiber, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFiber, getExecutingFile, arginfo_class_ReflectionFiber_getExecutingFile, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFiber, getExecutingLine, arginfo_class_ReflectionFiber_getExecutingLine, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFiber, getTrace, arginfo_class_ReflectionFiber_getTrace, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFiber, isStarted, arginfo_class_ReflectionFiber_isStarted, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFiber, isSuspended, arginfo_class_ReflectionFiber_isSuspended, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFiber, isRunning, arginfo_class_ReflectionFiber_isRunning, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFiber, isTerminated, arginfo_class_ReflectionFiber_isTerminated, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static zend_class_entry *register_class_ReflectionException(zend_class_entry *class_entry_Exception) { zend_class_entry ce, *class_entry; @@ -1364,3 +1410,14 @@ static zend_class_entry *register_class_ReflectionEnumBackedCase(zend_class_entr return class_entry; } + +static zend_class_entry *register_class_ReflectionFiber(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ReflectionFiber", class_ReflectionFiber_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL; + + return class_entry; +} diff --git a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt index 084e05e118d8a..88551fcd7932b 100644 --- a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt @@ -8,7 +8,7 @@ $ext = new ReflectionExtension('reflection'); var_dump($ext->getClasses()); ?> --EXPECT-- -array(22) { +array(23) { ["ReflectionException"]=> object(ReflectionClass)#2 (1) { ["name"]=> @@ -119,4 +119,9 @@ array(22) { ["name"]=> string(24) "ReflectionEnumBackedCase" } + ["ReflectionFiber"]=> + object(ReflectionClass)#24 (1) { + ["name"]=> + string(15) "ReflectionFiber" + } } diff --git a/ext/reflection/tests/ReflectionFiber_basic.phpt b/ext/reflection/tests/ReflectionFiber_basic.phpt new file mode 100644 index 0000000000000..3364a9258dc7b --- /dev/null +++ b/ext/reflection/tests/ReflectionFiber_basic.phpt @@ -0,0 +1,87 @@ +--TEST-- +ReflectionFiber basic tests +--FILE-- +isStarted()); + var_dump($fiber->isRunning()); + var_dump($fiber->isSuspended()); + var_dump($fiber->isTerminated()); + Fiber::suspend(); +}); + +$reflection = new ReflectionFiber($fiber); + +var_dump($fiber === $reflection->getFiber()); + +var_dump($reflection->isStarted()); +var_dump($reflection->isRunning()); +var_dump($reflection->isSuspended()); +var_dump($reflection->isTerminated()); + +$fiber->start(); + +var_dump($reflection->isStarted()); +var_dump($reflection->isRunning()); +var_dump($reflection->isSuspended()); +var_dump($reflection->isTerminated()); + +var_dump($reflection->getExecutingFile()); +var_dump($reflection->getExecutingLine()); +var_dump($reflection->getTrace()); + +$fiber->resume(); + +var_dump($fiber->isStarted()); +var_dump($fiber->isRunning()); +var_dump($fiber->isSuspended()); +var_dump($fiber->isTerminated()); + +--EXPECTF-- +bool(true) +bool(false) +bool(false) +bool(false) +bool(false) +bool(true) +bool(true) +bool(false) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +string(%d) "%sReflectionFiber_basic.php" +int(9) +array(2) { + [0]=> + array(6) { + ["file"]=> + string(%d) "%sReflectionFiber_basic.php" + ["line"]=> + int(9) + ["function"]=> + string(7) "suspend" + ["class"]=> + string(5) "Fiber" + ["type"]=> + string(2) "::" + ["args"]=> + array(0) { + } + } + [1]=> + array(2) { + ["function"]=> + string(9) "{closure}" + ["args"]=> + array(0) { + } + } +} +bool(true) +bool(false) +bool(false) +bool(true) \ No newline at end of file diff --git a/ext/reflection/tests/ReflectionFiber_errors.phpt b/ext/reflection/tests/ReflectionFiber_errors.phpt new file mode 100644 index 0000000000000..86aaf7a99d85b --- /dev/null +++ b/ext/reflection/tests/ReflectionFiber_errors.phpt @@ -0,0 +1,63 @@ +--TEST-- +ReflectionFiber errors +--FILE-- +getTrace(); +} catch (Error $error) { + echo $error->getMessage(), "\n"; +} + +try { + $reflection->getExecutingFile(); +} catch (Error $error) { + echo $error->getMessage(), "\n"; +} + +try { + $reflection->getExecutingLine(); +} catch (Error $error) { + echo $error->getMessage(), "\n"; +} + +$fiber->start(); + +var_dump($reflection->getExecutingFile()); +var_dump($reflection->getExecutingLine()); + +$fiber->resume(); + +try { + $reflection->getTrace(); +} catch (Error $error) { + echo $error->getMessage(), "\n"; +} + +try { + $reflection->getExecutingFile(); +} catch (Error $error) { + echo $error->getMessage(), "\n"; +} + +try { + $reflection->getExecutingLine(); +} catch (Error $error) { + echo $error->getMessage(), "\n"; +} + +--EXPECTF-- +Cannot fetch information from a fiber that has not been started or is terminated +Cannot fetch information from a fiber that has not been started or is terminated +Cannot fetch information from a fiber that has not been started or is terminated +string(%d) "%s%eReflectionFiber_errors.php" +int(4) +Cannot fetch information from a fiber that has not been started or is terminated +Cannot fetch information from a fiber that has not been started or is terminated +Cannot fetch information from a fiber that has not been started or is terminated \ No newline at end of file diff --git a/main/main.c b/main/main.c index 7627ad5cd99ea..7004e705b8acd 100644 --- a/main/main.c +++ b/main/main.c @@ -73,6 +73,7 @@ #include "zend_dtrace.h" #include "zend_observer.h" #include "zend_system_id.h" +#include "zend_fibers.h" #include "php_content_types.h" #include "php_ticks.h" @@ -302,6 +303,35 @@ static PHP_INI_MH(OnSetLogFilter) } /* }}} */ +/* {{{ PHP_INI_MH */ +static PHP_INI_MH(OnUpdateFiberStackSize) +{ + zend_long tmp; + + if (OnUpdateLongGEZero(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage) == FAILURE) { + return FAILURE; + } + + if (EG(fiber_stack_size) == 0) { + EG(fiber_stack_size) = ZEND_FIBER_DEFAULT_STACK_SIZE; + return SUCCESS; + } + + EG(fiber_stack_size) += ZEND_FIBER_GUARD_PAGES; + + tmp = ZEND_FIBER_PAGESIZE * EG(fiber_stack_size); + + if (tmp / ZEND_FIBER_PAGESIZE != EG(fiber_stack_size)) { + EG(fiber_stack_size) = ZEND_FIBER_DEFAULT_STACK_SIZE; + return FAILURE; + } + + EG(fiber_stack_size) = tmp; + + return SUCCESS; +} +/* }}} */ + /* {{{ php_disable_classes */ static void php_disable_classes(void) { @@ -737,6 +767,8 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("syslog.facility", "LOG_USER", PHP_INI_SYSTEM, OnSetFacility, syslog_facility, php_core_globals, core_globals) STD_PHP_INI_ENTRY("syslog.ident", "php", PHP_INI_SYSTEM, OnUpdateString, syslog_ident, php_core_globals, core_globals) STD_PHP_INI_ENTRY("syslog.filter", "no-ctrl", PHP_INI_ALL, OnSetLogFilter, syslog_filter, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("fiber.stack_size", "0", PHP_INI_ALL, OnUpdateFiberStackSize, fiber_stack_size, zend_executor_globals, executor_globals) PHP_INI_END() /* }}} */ @@ -1772,12 +1804,17 @@ void php_request_shutdown(void *dummy) php_call_shutdown_functions(); } - /* 2. Call all possible __destruct() functions */ + /* 2. Cleanup all active fibers. */ + zend_try { + zend_fiber_cleanup(); + } zend_end_try(); + + /* 3. Call all possible __destruct() functions */ zend_try { zend_call_destructors(); } zend_end_try(); - /* 3. Flush all output buffers */ + /* 4. Flush all output buffers */ zend_try { bool send_buffer = SG(request_info).headers_only ? 0 : 1; @@ -1794,27 +1831,27 @@ void php_request_shutdown(void *dummy) } } zend_end_try(); - /* 4. Reset max_execution_time (no longer executing php code after response sent) */ + /* 5. Reset max_execution_time (no longer executing php code after response sent) */ zend_try { zend_unset_timeout(); } zend_end_try(); - /* 5. Call all extensions RSHUTDOWN functions */ + /* 6. Call all extensions RSHUTDOWN functions */ if (PG(modules_activated)) { zend_deactivate_modules(); } - /* 6. Shutdown output layer (send the set HTTP headers, cleanup output handlers, etc.) */ + /* 7. Shutdown output layer (send the set HTTP headers, cleanup output handlers, etc.) */ zend_try { php_output_deactivate(); } zend_end_try(); - /* 7. Free shutdown functions */ + /* 8. Free shutdown functions */ if (PG(modules_activated)) { php_free_shutdown_functions(); } - /* 8. Destroy super-globals */ + /* 9. Destroy super-globals */ zend_try { int i; @@ -1823,38 +1860,38 @@ void php_request_shutdown(void *dummy) } } zend_end_try(); - /* 9. Shutdown scanner/executor/compiler and restore ini entries */ + /* 10. Shutdown scanner/executor/compiler and restore ini entries */ zend_deactivate(); - /* 10. free request-bound globals */ + /* 11. free request-bound globals */ php_free_request_globals(); - /* 11. Call all extensions post-RSHUTDOWN functions */ + /* 12. Call all extensions post-RSHUTDOWN functions */ zend_try { zend_post_deactivate_modules(); } zend_end_try(); - /* 12. SAPI related shutdown (free stuff) */ + /* 13. SAPI related shutdown (free stuff) */ zend_try { sapi_deactivate(); } zend_end_try(); - /* 13. free virtual CWD memory */ + /* 14. free virtual CWD memory */ virtual_cwd_deactivate(); - /* 14. Destroy stream hashes */ + /* 15. Destroy stream hashes */ zend_try { php_shutdown_stream_hashes(); } zend_end_try(); - /* 15. Free Willy (here be crashes) */ + /* 16. Free Willy (here be crashes) */ zend_arena_destroy(CG(arena)); zend_interned_strings_deactivate(); zend_try { shutdown_memory_manager(CG(unclean_shutdown) || !report_memleaks, 0); } zend_end_try(); - /* 16. Deactivate Zend signals */ + /* 17. Deactivate Zend signals */ #ifdef ZEND_SIGNALS zend_signal_deactivate(); #endif diff --git a/win32/build/Makefile.frag.w32 b/win32/build/Makefile.frag.w32 new file mode 100644 index 0000000000000..6c74619c986a4 --- /dev/null +++ b/win32/build/Makefile.frag.w32 @@ -0,0 +1,7 @@ +$(BUILD_DIR)\Zend\jump_$(FIBER_ASM_ARCH)_ms_pe_masm.obj: Zend\asm\jump_$(FIBER_ASM_ARCH)_ms_pe_masm.asm + $(FIBER_ASSEMBLER) /DBOOST_CONTEXT_EXPORT=EXPORT /nologo /Fo $(BUILD_DIR)\Zend\jump_$(FIBER_ASM_ARCH)_ms_pe_masm.obj /c Zend\asm\jump_$(FIBER_ASM_ARCH)_ms_pe_masm.asm + +$(BUILD_DIR)\Zend\make_$(FIBER_ASM_ARCH)_ms_pe_masm.obj: Zend\asm\make_$(FIBER_ASM_ARCH)_ms_pe_masm.asm + $(FIBER_ASSEMBLER) /DBOOST_CONTEXT_EXPORT=EXPORT /nologo /Fo $(BUILD_DIR)\Zend\make_$(FIBER_ASM_ARCH)_ms_pe_masm.obj /c Zend\asm\make_$(FIBER_ASM_ARCH)_ms_pe_masm.asm + +$(BUILD_DIR)\$(PHPDLL): $(BUILD_DIR)\Zend\jump_$(FIBER_ASM_ARCH)_ms_pe_masm.obj $(BUILD_DIR)\Zend\make_$(FIBER_ASM_ARCH)_ms_pe_masm.obj diff --git a/win32/build/config.w32 b/win32/build/config.w32 index d8d5671b10c7a..28f9a1f320793 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -238,9 +238,20 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \ zend_default_classes.c zend_execute.c zend_strtod.c zend_gc.c zend_closures.c zend_weakrefs.c \ zend_float.c zend_string.c zend_generators.c zend_virtual_cwd.c zend_ast.c \ zend_inheritance.c zend_smart_str.c zend_cpuinfo.c zend_observer.c zend_system_id.c \ - zend_enum.c"); + zend_enum.c zend_fibers.c"); ADD_SOURCES("Zend\\Optimizer", "zend_optimizer.c pass1.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c"); +var FIBER_ASSEMBLER = X64 ? PATH_PROG('ML64') : PATH_PROG('ML'); +DEFINE('FIBER_ASSEMBLER', FIBER_ASSEMBLER); + +var FIBER_ASM_ARCH = X64 ? 'x86_64' : 'i386'; +DEFINE('FIBER_ASM_ARCH', FIBER_ASM_ARCH); + +ADD_FLAG('LDFLAGS', '$(BUILD_DIR)\\Zend\\jump_' + FIBER_ASM_ARCH + '_ms_pe_masm.obj'); +ADD_FLAG('LDFLAGS', '$(BUILD_DIR)\\Zend\\make_' + FIBER_ASM_ARCH + '_ms_pe_masm.obj'); + +ADD_MAKEFILE_FRAGMENT('win32/build/Makefile.frag.w32'); + ADD_FLAG("CFLAGS_BD_ZEND", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); if (VS_TOOLSET && VCVERS >= 1914) { ADD_FLAG("CFLAGS_BD_ZEND", "/d2FuncCache1"); From b7fc994c135456a6aa58065a2112041c8159c8ac Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Mon, 19 Apr 2021 10:06:01 -0500 Subject: [PATCH 02/43] Backup exception when destroying fiber --- .../throw-from-destruct-with-fiber.phpt | 21 +++++++++++++++++++ Zend/zend_fibers.c | 7 +++++++ 2 files changed, 28 insertions(+) create mode 100644 Zend/tests/fibers/throw-from-destruct-with-fiber.phpt diff --git a/Zend/tests/fibers/throw-from-destruct-with-fiber.phpt b/Zend/tests/fibers/throw-from-destruct-with-fiber.phpt new file mode 100644 index 0000000000000..0d3b0b388698d --- /dev/null +++ b/Zend/tests/fibers/throw-from-destruct-with-fiber.phpt @@ -0,0 +1,21 @@ +--TEST-- +Test destructor throwing with unfinished fiber +--FILE-- +start(); + throw new Exception; + } +}; + +--EXPECTF-- +Fatal error: Uncaught Exception in %sthrow-from-destruct-with-fiber.php:%d +Stack trace: +#0 %sthrow-from-destruct-with-fiber.php(%d): class@anonymous->__destruct() +#1 {main} + thrown in %sthrow-from-destruct-with-fiber.php on line %d diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 68641b0256e89..4b5425d89bc07 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -416,8 +416,11 @@ static void zend_fiber_object_destroy(zend_object *object) /* {{{ */ zval_ptr_dtor(&fiber->fci.function_name); } else { if (fiber->status == ZEND_FIBER_STATUS_SUSPENDED) { + zend_object *exception = EG(exception); + EG(exception) = NULL; fiber->status = ZEND_FIBER_STATUS_SHUTDOWN; zend_fiber_switch_to(fiber); + EG(exception) = exception; } zval_ptr_dtor(&fiber->value); @@ -434,6 +437,8 @@ static void zend_fiber_object_destroy(zend_object *object) /* {{{ */ void zend_fiber_cleanup(void) /* {{{ */ { zend_fiber *fiber; + zend_object *exception = EG(exception); + EG(exception) = NULL; ZEND_HASH_REVERSE_FOREACH_PTR(&EG(fibers), fiber) { if (fiber->status == ZEND_FIBER_STATUS_SUSPENDED) { @@ -442,6 +447,8 @@ void zend_fiber_cleanup(void) /* {{{ */ zend_fiber_switch_to(fiber); } } ZEND_HASH_FOREACH_END(); + + EG(exception) = exception; } /* }}} */ From 804f8ad063a2f0e3c7a88ff5a7c3124fa8640416 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Mon, 19 Apr 2021 11:25:50 -0500 Subject: [PATCH 03/43] Add closing ?> in tests Improved readability of the results of status tests. --- Zend/tests/fibers/catch.phpt | 1 + Zend/tests/fibers/double-start.phpt | 1 + Zend/tests/fibers/exit-in-fiber.phpt | 1 + Zend/tests/fibers/failing-fiber.phpt | 3 ++- Zend/tests/fibers/fast-finish-fiber.phpt | 1 + Zend/tests/fibers/fatal-error-in-fiber.phpt | 1 + .../fibers/fatal-error-in-nested-fiber.phpt | 1 + .../fatal-error-with-multiple-fibers.phpt | 1 + .../fibers/fiber-created-in-destruct.phpt | 1 + Zend/tests/fibers/fiber-error-construct.phpt | 1 + Zend/tests/fibers/fiber-in-destruct.phpt | 1 + .../fibers/fiber-in-shutdown-function.phpt | 1 + Zend/tests/fibers/fiber-status.phpt | 14 ++++++++++++- Zend/tests/fibers/fiber-this.phpt | 1 + .../tests/fibers/fiber-throw-in-destruct.phpt | 1 + .../fibers/get-return-after-throwing.phpt | 1 + .../get-return-from-unstarted-fiber.phpt | 1 + .../get-return-in-unfinished-fiber.phpt | 1 + Zend/tests/fibers/get-return.phpt | 1 + .../fibers/resume-non-running-fiber.phpt | 1 + Zend/tests/fibers/resume-running-fiber.phpt | 1 + .../tests/fibers/resume-terminated-fiber.phpt | 1 + Zend/tests/fibers/resume.phpt | 1 + Zend/tests/fibers/silence-operator.phpt | 1 + Zend/tests/fibers/start-arguments.phpt | 1 + ...d-in-force-close-fiber-after-shutdown.phpt | 1 + .../fibers/suspend-in-force-close-fiber.phpt | 1 + .../fibers/suspend-in-nested-function.phpt | 1 + Zend/tests/fibers/suspend-outside-fiber.phpt | 1 + .../throw-from-destruct-with-fiber.phpt | 1 + .../fibers/throw-into-non-running-fiber.phpt | 1 + Zend/tests/fibers/throw.phpt | 1 + .../fibers/unfinished-fiber-with-finally.phpt | 1 + ...nfinished-fiber-with-nested-try-catch.phpt | 1 + ...inished-fiber-with-suspend-in-finally.phpt | 1 + ...nfinished-fiber-with-throw-in-finally.phpt | 1 + Zend/tests/fibers/unfinished-fiber.phpt | 1 + Zend/tests/fibers/unstarted-fiber.phpt | 1 + .../tests/ReflectionFiber_basic.phpt | 20 ++++++++++++++----- .../tests/ReflectionFiber_errors.phpt | 3 ++- 40 files changed, 68 insertions(+), 8 deletions(-) diff --git a/Zend/tests/fibers/catch.phpt b/Zend/tests/fibers/catch.phpt index acbf69a5d07be..92c139ca1b4d0 100644 --- a/Zend/tests/fibers/catch.phpt +++ b/Zend/tests/fibers/catch.phpt @@ -16,6 +16,7 @@ var_dump($value); $fiber->throw(new Exception('test')); +?> --EXPECT-- string(4) "test" string(4) "test" diff --git a/Zend/tests/fibers/double-start.phpt b/Zend/tests/fibers/double-start.phpt index 2617c0cd570dd..8b18e24ce0141 100644 --- a/Zend/tests/fibers/double-start.phpt +++ b/Zend/tests/fibers/double-start.phpt @@ -11,6 +11,7 @@ $fiber->start(); $fiber->start(); +?> --EXPECTF-- Fatal error: Uncaught FiberError: Cannot start a fiber that has already been started in %sdouble-start.php:%d Stack trace: diff --git a/Zend/tests/fibers/exit-in-fiber.phpt b/Zend/tests/fibers/exit-in-fiber.phpt index 5cdc62d0ec69d..8f1583283beab 100644 --- a/Zend/tests/fibers/exit-in-fiber.phpt +++ b/Zend/tests/fibers/exit-in-fiber.phpt @@ -15,5 +15,6 @@ $fiber->resume(); echo "unreachable\n"; +?> --EXPECT-- resumed diff --git a/Zend/tests/fibers/failing-fiber.phpt b/Zend/tests/fibers/failing-fiber.phpt index df2b25e38ed2b..7490a7f6196ca 100644 --- a/Zend/tests/fibers/failing-fiber.phpt +++ b/Zend/tests/fibers/failing-fiber.phpt @@ -1,5 +1,5 @@ --TEST-- -Test throwing into fiber +Test throwing from fiber --FILE-- resume($value); +?> --EXPECTF-- string(4) "test" diff --git a/Zend/tests/fibers/fast-finish-fiber.phpt b/Zend/tests/fibers/fast-finish-fiber.phpt index 8f2eb8c3fe212..2b4d80377d504 100644 --- a/Zend/tests/fibers/fast-finish-fiber.phpt +++ b/Zend/tests/fibers/fast-finish-fiber.phpt @@ -9,6 +9,7 @@ var_dump($fiber->start()); var_dump($fiber->getReturn()); var_dump($fiber->isTerminated()); +?> --EXPECTF-- bool(false) NULL diff --git a/Zend/tests/fibers/fatal-error-in-fiber.phpt b/Zend/tests/fibers/fatal-error-in-fiber.phpt index 0a6e18bb1ab3d..5cdda24852944 100644 --- a/Zend/tests/fibers/fatal-error-in-fiber.phpt +++ b/Zend/tests/fibers/fatal-error-in-fiber.phpt @@ -9,5 +9,6 @@ $fiber = new Fiber(function (): void { $fiber->start(); +?> --EXPECTF-- Fatal error: Fatal error in fiber in %sfatal-error-in-fiber.php on line %d diff --git a/Zend/tests/fibers/fatal-error-in-nested-fiber.phpt b/Zend/tests/fibers/fatal-error-in-nested-fiber.phpt index 7d5988b8d099a..d012576aa2388 100644 --- a/Zend/tests/fibers/fatal-error-in-nested-fiber.phpt +++ b/Zend/tests/fibers/fatal-error-in-nested-fiber.phpt @@ -20,6 +20,7 @@ var_dump($fiber->start()); $fiber->resume(); +?> --EXPECTF-- int(2) int(1) diff --git a/Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt b/Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt index e82b2ec9f180b..2803d4fcb25e3 100644 --- a/Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt +++ b/Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt @@ -14,6 +14,7 @@ var_dump($fiber1->start()); var_dump($fiber2->start()); $fiber2->resume(); +?> --EXPECTF-- int(1) int(2) diff --git a/Zend/tests/fibers/fiber-created-in-destruct.phpt b/Zend/tests/fibers/fiber-created-in-destruct.phpt index f78b5b88d9b18..bec9551a87bab 100644 --- a/Zend/tests/fibers/fiber-created-in-destruct.phpt +++ b/Zend/tests/fibers/fiber-created-in-destruct.phpt @@ -17,6 +17,7 @@ $object = new class() { } }; +?> --EXPECT-- int(1) NULL diff --git a/Zend/tests/fibers/fiber-error-construct.phpt b/Zend/tests/fibers/fiber-error-construct.phpt index bfff39394d720..8af23ff86ebdc 100644 --- a/Zend/tests/fibers/fiber-error-construct.phpt +++ b/Zend/tests/fibers/fiber-error-construct.phpt @@ -9,5 +9,6 @@ try { echo $exception->getMessage(), "\n"; } +?> --EXPECT-- The "FiberError" class is reserved for internal use and cannot be manually instantiated diff --git a/Zend/tests/fibers/fiber-in-destruct.phpt b/Zend/tests/fibers/fiber-in-destruct.phpt index 7301521ecfe25..71b89f808fe55 100644 --- a/Zend/tests/fibers/fiber-in-destruct.phpt +++ b/Zend/tests/fibers/fiber-in-destruct.phpt @@ -26,6 +26,7 @@ var_dump($fiber->resume()); var_dump($fiber->resume()); var_dump($fiber->getReturn()); +?> --EXPECT-- int(1) int(2) diff --git a/Zend/tests/fibers/fiber-in-shutdown-function.phpt b/Zend/tests/fibers/fiber-in-shutdown-function.phpt index ba7afeda43865..b39daff775b18 100644 --- a/Zend/tests/fibers/fiber-in-shutdown-function.phpt +++ b/Zend/tests/fibers/fiber-in-shutdown-function.phpt @@ -16,6 +16,7 @@ register_shutdown_function(function (): void { var_dump($fiber->getReturn()); }); +?> --EXPECT-- int(1) int(2) diff --git a/Zend/tests/fibers/fiber-status.phpt b/Zend/tests/fibers/fiber-status.phpt index c0b60e2e92a6a..45b2c60e05711 100644 --- a/Zend/tests/fibers/fiber-status.phpt +++ b/Zend/tests/fibers/fiber-status.phpt @@ -5,6 +5,7 @@ Fiber status methods $fiber = new Fiber(function (): void { $fiber = Fiber::this(); + echo "\nWithin Fiber:\n"; var_dump($fiber->isStarted()); var_dump($fiber->isRunning()); var_dump($fiber->isSuspended()); @@ -12,6 +13,7 @@ $fiber = new Fiber(function (): void { Fiber::suspend(); }); +echo "\nBefore Start:\n"; var_dump($fiber->isStarted()); var_dump($fiber->isRunning()); var_dump($fiber->isSuspended()); @@ -19,6 +21,7 @@ var_dump($fiber->isTerminated()); $fiber->start(); +echo "\nAfter Start:\n"; var_dump($fiber->isStarted()); var_dump($fiber->isRunning()); var_dump($fiber->isSuspended()); @@ -26,25 +29,34 @@ var_dump($fiber->isTerminated()); $fiber->resume(); +echo "\nAfter Resume:\n"; var_dump($fiber->isStarted()); var_dump($fiber->isRunning()); var_dump($fiber->isSuspended()); var_dump($fiber->isTerminated()); +?> --EXPECT-- +Before Start: bool(false) bool(false) bool(false) bool(false) + +Within Fiber: bool(true) bool(true) bool(false) bool(false) + +After Start: bool(true) bool(false) bool(true) bool(false) + +After Resume: bool(true) bool(false) bool(false) -bool(true) \ No newline at end of file +bool(true) diff --git a/Zend/tests/fibers/fiber-this.phpt b/Zend/tests/fibers/fiber-this.phpt index 4bc3e7f4a2920..714a848f0aad0 100644 --- a/Zend/tests/fibers/fiber-this.phpt +++ b/Zend/tests/fibers/fiber-this.phpt @@ -11,6 +11,7 @@ $fiber = new Fiber(function (): void { $fiber->start(); +?> --EXPECTF-- NULL object(Fiber)#%d (0) { diff --git a/Zend/tests/fibers/fiber-throw-in-destruct.phpt b/Zend/tests/fibers/fiber-throw-in-destruct.phpt index 34a3d5e71c63f..72b0adbae3cca 100644 --- a/Zend/tests/fibers/fiber-throw-in-destruct.phpt +++ b/Zend/tests/fibers/fiber-throw-in-destruct.phpt @@ -16,6 +16,7 @@ $object = new class() { } }; +?> --EXPECTF-- int(1) diff --git a/Zend/tests/fibers/get-return-after-throwing.phpt b/Zend/tests/fibers/get-return-after-throwing.phpt index f1b912e5fb579..620f74e3239ca 100644 --- a/Zend/tests/fibers/get-return-after-throwing.phpt +++ b/Zend/tests/fibers/get-return-after-throwing.phpt @@ -13,6 +13,7 @@ try { $fiber->getReturn(); +?> --EXPECTF-- test diff --git a/Zend/tests/fibers/get-return-from-unstarted-fiber.phpt b/Zend/tests/fibers/get-return-from-unstarted-fiber.phpt index a88000b172fd9..dab0e591f5ff6 100644 --- a/Zend/tests/fibers/get-return-from-unstarted-fiber.phpt +++ b/Zend/tests/fibers/get-return-from-unstarted-fiber.phpt @@ -7,6 +7,7 @@ $fiber = new Fiber(fn() => Fiber::suspend(1)); $fiber->getReturn(); +?> --EXPECTF-- Fatal error: Uncaught FiberError: Cannot get fiber return value: The fiber has not been started in %sget-return-from-unstarted-fiber.php:%d Stack trace: diff --git a/Zend/tests/fibers/get-return-in-unfinished-fiber.phpt b/Zend/tests/fibers/get-return-in-unfinished-fiber.phpt index 130bc44fb2601..f3f61800164ca 100644 --- a/Zend/tests/fibers/get-return-in-unfinished-fiber.phpt +++ b/Zend/tests/fibers/get-return-in-unfinished-fiber.phpt @@ -9,6 +9,7 @@ var_dump($fiber->start()); $fiber->getReturn(); +?> --EXPECTF-- int(1) diff --git a/Zend/tests/fibers/get-return.phpt b/Zend/tests/fibers/get-return.phpt index c7c87f6bc7f53..5abbb22cabac2 100644 --- a/Zend/tests/fibers/get-return.phpt +++ b/Zend/tests/fibers/get-return.phpt @@ -13,6 +13,7 @@ var_dump($value); var_dump($fiber->resume($value + 1)); var_dump($fiber->getReturn()); +?> --EXPECT-- int(1) NULL diff --git a/Zend/tests/fibers/resume-non-running-fiber.phpt b/Zend/tests/fibers/resume-non-running-fiber.phpt index 79b5d9c5014d0..1efd32eda2efd 100644 --- a/Zend/tests/fibers/resume-non-running-fiber.phpt +++ b/Zend/tests/fibers/resume-non-running-fiber.phpt @@ -7,6 +7,7 @@ $fiber = new Fiber(fn() => null); $fiber->resume(); +?> --EXPECTF-- Fatal error: Uncaught FiberError: Cannot resume a fiber that is not suspended in %sresume-non-running-fiber.php:%d Stack trace: diff --git a/Zend/tests/fibers/resume-running-fiber.phpt b/Zend/tests/fibers/resume-running-fiber.phpt index d5bc2e50c0f28..9003a02817cf7 100644 --- a/Zend/tests/fibers/resume-running-fiber.phpt +++ b/Zend/tests/fibers/resume-running-fiber.phpt @@ -10,6 +10,7 @@ $fiber = new Fiber(function (): void { $fiber->start(); +?> --EXPECTF-- Fatal error: Uncaught FiberError: Cannot resume a fiber that is not suspended in %sresume-running-fiber.php:%d Stack trace: diff --git a/Zend/tests/fibers/resume-terminated-fiber.phpt b/Zend/tests/fibers/resume-terminated-fiber.phpt index 5d4c66e77ebb8..dae85d8854824 100644 --- a/Zend/tests/fibers/resume-terminated-fiber.phpt +++ b/Zend/tests/fibers/resume-terminated-fiber.phpt @@ -9,6 +9,7 @@ $fiber->start(); $fiber->resume(); +?> --EXPECTF-- Fatal error: Uncaught FiberError: Cannot resume a fiber that is not suspended in %sresume-terminated-fiber.php:%d Stack trace: diff --git a/Zend/tests/fibers/resume.phpt b/Zend/tests/fibers/resume.phpt index 195f0e3cb0eb3..97c479c47e070 100644 --- a/Zend/tests/fibers/resume.phpt +++ b/Zend/tests/fibers/resume.phpt @@ -12,6 +12,7 @@ $value = $fiber->start(); var_dump($value); $fiber->resume($value + 1); +?> --EXPECT-- int(1) int(2) diff --git a/Zend/tests/fibers/silence-operator.phpt b/Zend/tests/fibers/silence-operator.phpt index 203fcc484eaf9..dac8f57673aa0 100644 --- a/Zend/tests/fibers/silence-operator.phpt +++ b/Zend/tests/fibers/silence-operator.phpt @@ -21,6 +21,7 @@ $fiber->resume(); trigger_error("Warning D", E_USER_WARNING); +?> --EXPECTF-- Warning: Warning C in %ssilence-operator.php on line %d diff --git a/Zend/tests/fibers/start-arguments.phpt b/Zend/tests/fibers/start-arguments.phpt index 308267abe73a1..bccb38da2e078 100644 --- a/Zend/tests/fibers/start-arguments.phpt +++ b/Zend/tests/fibers/start-arguments.phpt @@ -17,6 +17,7 @@ $fiber = new Fiber(function (int $x): int { $fiber->start('test'); +?> --EXPECTF-- int(1) diff --git a/Zend/tests/fibers/suspend-in-force-close-fiber-after-shutdown.phpt b/Zend/tests/fibers/suspend-in-force-close-fiber-after-shutdown.phpt index a214e4839bfb5..2db5a14e45cee 100644 --- a/Zend/tests/fibers/suspend-in-force-close-fiber-after-shutdown.phpt +++ b/Zend/tests/fibers/suspend-in-force-close-fiber-after-shutdown.phpt @@ -15,6 +15,7 @@ $fiber->start(); echo "done\n"; +?> --EXPECTF-- done diff --git a/Zend/tests/fibers/suspend-in-force-close-fiber.phpt b/Zend/tests/fibers/suspend-in-force-close-fiber.phpt index afef1d5a68f0c..c2c2b2105857b 100644 --- a/Zend/tests/fibers/suspend-in-force-close-fiber.phpt +++ b/Zend/tests/fibers/suspend-in-force-close-fiber.phpt @@ -15,6 +15,7 @@ $fiber->start(); unset($fiber); +?> --EXPECTF-- Fatal error: Uncaught FiberError: Cannot suspend in a force closed fiber in %ssuspend-in-force-close-fiber.php:%d Stack trace: diff --git a/Zend/tests/fibers/suspend-in-nested-function.phpt b/Zend/tests/fibers/suspend-in-nested-function.phpt index af6577dd912cd..1f5c9c82d9c20 100644 --- a/Zend/tests/fibers/suspend-in-nested-function.phpt +++ b/Zend/tests/fibers/suspend-in-nested-function.phpt @@ -20,6 +20,7 @@ var_dump($fiber->getReturn()); echo "done\n"; +?> --EXPECT-- int(1) int(2) diff --git a/Zend/tests/fibers/suspend-outside-fiber.phpt b/Zend/tests/fibers/suspend-outside-fiber.phpt index 21ec150eb9080..5fb524cd90f7a 100644 --- a/Zend/tests/fibers/suspend-outside-fiber.phpt +++ b/Zend/tests/fibers/suspend-outside-fiber.phpt @@ -5,6 +5,7 @@ Suspend outside fiber $value = Fiber::suspend(1); +?> --EXPECTF-- Fatal error: Uncaught FiberError: Cannot suspend outside of a fiber in %ssuspend-outside-fiber.php:%d Stack trace: diff --git a/Zend/tests/fibers/throw-from-destruct-with-fiber.phpt b/Zend/tests/fibers/throw-from-destruct-with-fiber.phpt index 0d3b0b388698d..b67fa5d0f84b4 100644 --- a/Zend/tests/fibers/throw-from-destruct-with-fiber.phpt +++ b/Zend/tests/fibers/throw-from-destruct-with-fiber.phpt @@ -13,6 +13,7 @@ new class() { } }; +?> --EXPECTF-- Fatal error: Uncaught Exception in %sthrow-from-destruct-with-fiber.php:%d Stack trace: diff --git a/Zend/tests/fibers/throw-into-non-running-fiber.phpt b/Zend/tests/fibers/throw-into-non-running-fiber.phpt index 83c7894b1cec8..c195865a494d4 100644 --- a/Zend/tests/fibers/throw-into-non-running-fiber.phpt +++ b/Zend/tests/fibers/throw-into-non-running-fiber.phpt @@ -7,6 +7,7 @@ $fiber = new Fiber(fn() => null); $fiber->throw(new Exception('test')); +?> --EXPECTF-- Fatal error: Uncaught FiberError: Cannot resume a fiber that is not suspended in %sthrow-into-non-running-fiber.php:%d Stack trace: diff --git a/Zend/tests/fibers/throw.phpt b/Zend/tests/fibers/throw.phpt index bdd67cb61fb7c..3087d0c086194 100644 --- a/Zend/tests/fibers/throw.phpt +++ b/Zend/tests/fibers/throw.phpt @@ -12,6 +12,7 @@ var_dump($value); $fiber->throw(new Exception('test')); +?> --EXPECTF-- string(4) "test" diff --git a/Zend/tests/fibers/unfinished-fiber-with-finally.phpt b/Zend/tests/fibers/unfinished-fiber-with-finally.phpt index 140a5fc09f0d4..84ba74e646c6d 100644 --- a/Zend/tests/fibers/unfinished-fiber-with-finally.phpt +++ b/Zend/tests/fibers/unfinished-fiber-with-finally.phpt @@ -23,6 +23,7 @@ unset($fiber); // Destroy fiber object, executing finally block. echo "done\n"; +?> --EXPECT-- fiber finally diff --git a/Zend/tests/fibers/unfinished-fiber-with-nested-try-catch.phpt b/Zend/tests/fibers/unfinished-fiber-with-nested-try-catch.phpt index f13fe06ec5ac5..664e4a052b8f8 100644 --- a/Zend/tests/fibers/unfinished-fiber-with-nested-try-catch.phpt +++ b/Zend/tests/fibers/unfinished-fiber-with-nested-try-catch.phpt @@ -39,6 +39,7 @@ unset($fiber); // Destroy fiber object, executing finally block. echo "done\n"; +?> --EXPECT-- fiber inner finally diff --git a/Zend/tests/fibers/unfinished-fiber-with-suspend-in-finally.phpt b/Zend/tests/fibers/unfinished-fiber-with-suspend-in-finally.phpt index 3c10f3fdb54e2..8c7ccbdac711e 100644 --- a/Zend/tests/fibers/unfinished-fiber-with-suspend-in-finally.phpt +++ b/Zend/tests/fibers/unfinished-fiber-with-suspend-in-finally.phpt @@ -28,6 +28,7 @@ unset($fiber); // Destroy fiber object, executing finally block. echo "done\n"; +?> --EXPECTF-- fiber inner finally diff --git a/Zend/tests/fibers/unfinished-fiber-with-throw-in-finally.phpt b/Zend/tests/fibers/unfinished-fiber-with-throw-in-finally.phpt index 06306794bd7e8..fd0f856188a71 100644 --- a/Zend/tests/fibers/unfinished-fiber-with-throw-in-finally.phpt +++ b/Zend/tests/fibers/unfinished-fiber-with-throw-in-finally.phpt @@ -38,6 +38,7 @@ unset($fiber); // Destroy fiber object, executing finally block. echo "done\n"; +?> --EXPECT-- fiber inner finally diff --git a/Zend/tests/fibers/unfinished-fiber.phpt b/Zend/tests/fibers/unfinished-fiber.phpt index 7dd9a7095f856..13bc8a68e002f 100644 --- a/Zend/tests/fibers/unfinished-fiber.phpt +++ b/Zend/tests/fibers/unfinished-fiber.phpt @@ -19,6 +19,7 @@ $fiber->start(); echo "done\n"; +?> --EXPECT-- fiber done diff --git a/Zend/tests/fibers/unstarted-fiber.phpt b/Zend/tests/fibers/unstarted-fiber.phpt index 7724d7f3ad0f0..6e6916fe80b16 100644 --- a/Zend/tests/fibers/unstarted-fiber.phpt +++ b/Zend/tests/fibers/unstarted-fiber.phpt @@ -7,5 +7,6 @@ $fiber = new Fiber(fn() => null); echo "done"; +?> --EXPECT-- done diff --git a/ext/reflection/tests/ReflectionFiber_basic.phpt b/ext/reflection/tests/ReflectionFiber_basic.phpt index 3364a9258dc7b..6d75f915c9acf 100644 --- a/ext/reflection/tests/ReflectionFiber_basic.phpt +++ b/ext/reflection/tests/ReflectionFiber_basic.phpt @@ -5,6 +5,7 @@ ReflectionFiber basic tests $fiber = new Fiber(function (): void { $fiber = Fiber::this(); + echo "\nWithin Fiber:\n"; var_dump($fiber->isStarted()); var_dump($fiber->isRunning()); var_dump($fiber->isSuspended()); @@ -14,8 +15,8 @@ $fiber = new Fiber(function (): void { $reflection = new ReflectionFiber($fiber); +echo "Before Start:\n"; var_dump($fiber === $reflection->getFiber()); - var_dump($reflection->isStarted()); var_dump($reflection->isRunning()); var_dump($reflection->isSuspended()); @@ -23,45 +24,52 @@ var_dump($reflection->isTerminated()); $fiber->start(); +echo "\nAfter Start:\n"; var_dump($reflection->isStarted()); var_dump($reflection->isRunning()); var_dump($reflection->isSuspended()); var_dump($reflection->isTerminated()); - var_dump($reflection->getExecutingFile()); var_dump($reflection->getExecutingLine()); var_dump($reflection->getTrace()); $fiber->resume(); +echo "\nAfter Resume:\n"; var_dump($fiber->isStarted()); var_dump($fiber->isRunning()); var_dump($fiber->isSuspended()); var_dump($fiber->isTerminated()); +?> --EXPECTF-- +Before Start: bool(true) bool(false) bool(false) bool(false) bool(false) + +Within Fiber: bool(true) bool(true) bool(false) bool(false) + +After Start: bool(true) bool(false) bool(true) bool(false) string(%d) "%sReflectionFiber_basic.php" -int(9) +int(10) array(2) { [0]=> array(6) { ["file"]=> string(%d) "%sReflectionFiber_basic.php" ["line"]=> - int(9) + int(10) ["function"]=> string(7) "suspend" ["class"]=> @@ -81,7 +89,9 @@ array(2) { } } } + +After Resume: bool(true) bool(false) bool(false) -bool(true) \ No newline at end of file +bool(true) diff --git a/ext/reflection/tests/ReflectionFiber_errors.phpt b/ext/reflection/tests/ReflectionFiber_errors.phpt index 86aaf7a99d85b..812fa5483c426 100644 --- a/ext/reflection/tests/ReflectionFiber_errors.phpt +++ b/ext/reflection/tests/ReflectionFiber_errors.phpt @@ -52,6 +52,7 @@ try { echo $error->getMessage(), "\n"; } +?> --EXPECTF-- Cannot fetch information from a fiber that has not been started or is terminated Cannot fetch information from a fiber that has not been started or is terminated @@ -60,4 +61,4 @@ string(%d) "%s%eReflectionFiber_errors.php" int(4) Cannot fetch information from a fiber that has not been started or is terminated Cannot fetch information from a fiber that has not been started or is terminated -Cannot fetch information from a fiber that has not been started or is terminated \ No newline at end of file +Cannot fetch information from a fiber that has not been started or is terminated From b3b1595a5d4ff7452feb9767ab3af5210a11ada8 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Mon, 19 Apr 2021 12:04:21 -0500 Subject: [PATCH 04/43] Update test to use ref-counted return --- Zend/tests/fibers/get-return.phpt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Zend/tests/fibers/get-return.phpt b/Zend/tests/fibers/get-return.phpt index 5abbb22cabac2..be090b4f44c9f 100644 --- a/Zend/tests/fibers/get-return.phpt +++ b/Zend/tests/fibers/get-return.phpt @@ -3,18 +3,18 @@ Test fiber return value --FILE-- start(); var_dump($value); -var_dump($fiber->resume($value + 1)); +var_dump($fiber->resume($value . "y")); var_dump($fiber->getReturn()); ?> --EXPECT-- -int(1) +string(1) "x" NULL -int(2) +string(2) "xy" From a507a715799f4119e17896c70c5a45f2c4fa678c Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Mon, 19 Apr 2021 12:05:45 -0500 Subject: [PATCH 05/43] Do not execute finally blocks on fatal error --- Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt | 8 +++++++- Zend/zend_fibers.c | 9 ++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt b/Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt index 2803d4fcb25e3..8f66fba5e2c1e 100644 --- a/Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt +++ b/Zend/tests/fibers/fatal-error-with-multiple-fibers.phpt @@ -3,7 +3,13 @@ Fatal error in a fiber with other active fibers --FILE-- Fiber::suspend(1)); +$fiber1 = new Fiber(function (): void { + try { + \Fiber::suspend(1); + } finally { + echo "not executed"; + } +}); $fiber2 = new Fiber(function (): void { \Fiber::suspend(2); diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 4b5425d89bc07..487fc377056c9 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -288,14 +288,13 @@ static void zend_fiber_switch_to(zend_fiber *fiber) /* {{{ */ zend_observer_fiber_switch_notify(fiber, previous); - if (UNEXPECTED(EG(fiber_error))) { + if (UNEXPECTED(EG(fiber_error)) && fiber->status != ZEND_FIBER_STATUS_SHUTDOWN) { if (previous) { zend_fiber_suspend(previous); // Still in fiber, suspend again until in {main}. abort(); // This fiber should never be resumed. } zend_fiber_error *error = EG(fiber_error); - EG(fiber_error) = NULL; // Null error reference so suspended fibers do not issue error again during shutdown. zend_error_at_noreturn(error->type, error->filename, error->lineno, "%s", ZSTR_VAL(error->message)); } } @@ -540,7 +539,11 @@ ZEND_METHOD(Fiber, suspend) if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { // This occurs when the fiber is GC'ed while suspended, do not add a ref. - zend_throw_fiber_exit(); + if (EG(fiber_error)) { + zend_throw_unwind_exit(); + } else { + zend_throw_fiber_exit(); + } return; } From 7302381b43e46df3ef81dabbeff35bd5a6db1068 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Mon, 19 Apr 2021 12:14:35 -0500 Subject: [PATCH 06/43] A couple minor fixes --- Zend/zend_fibers.h | 6 +++--- Zend/zend_fibers.stub.php | 7 ++----- configure.ac | 1 - 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index 69cd8de7ff291..e425adc0a3358 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -17,12 +17,12 @@ +----------------------------------------------------------------------+ */ -#include "zend_API.h" -#include "zend_types.h" - #ifndef ZEND_FIBERS_H #define ZEND_FIBERS_H +#include "zend_API.h" +#include "zend_types.h" + BEGIN_EXTERN_C() void zend_register_fiber_ce(void); diff --git a/Zend/zend_fibers.stub.php b/Zend/zend_fibers.stub.php index 27f95abdfec24..6fd0977c67eff 100644 --- a/Zend/zend_fibers.stub.php +++ b/Zend/zend_fibers.stub.php @@ -24,13 +24,10 @@ public function getReturn(): mixed {} public static function this(): ?Fiber {} - public static function suspend(mixed $value = null): mixed { } + public static function suspend(mixed $value = null): mixed {} } final class FiberError extends Error { - public function __construct() - { - throw new \Error('The "FiberError" class is reserved for internal use and cannot be manually instantiated'); - } + public function __construct() {} } diff --git a/configure.ac b/configure.ac index 92eabc3825516..5237a59f16ccc 100644 --- a/configure.ac +++ b/configure.ac @@ -1615,7 +1615,6 @@ PHP_ADD_SOURCES(Zend, \ Optimizer/zend_dump.c \ , -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) - PHP_ADD_BUILD_DIR(main main/streams) PHP_ADD_BUILD_DIR(TSRM) PHP_ADD_BUILD_DIR(Zend Zend/Optimizer) From eeaffc3d993a3582fcdcbca4da4b4e6b835b623b Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Mon, 19 Apr 2021 13:00:11 -0500 Subject: [PATCH 07/43] Switch function in constant to actual function --- Zend/zend_fibers.c | 25 +++++++++++++------------ Zend/zend_fibers.h | 16 +++++++++++++--- main/main.c | 9 +++++---- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 487fc377056c9..d804d13e4db3a 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -100,12 +100,13 @@ zend_always_inline static void zend_observer_fiber_switch_notify(zend_fiber *fro static zend_bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) /* {{{ */ { - ZEND_ASSERT(size >= ZEND_FIBER_PAGESIZE + ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE); - void *pointer; + const size_t page_size = zend_fiber_page_size(); + + ZEND_ASSERT(size >= page_size + ZEND_FIBER_GUARD_PAGES * page_size); - stack->size = (size + ZEND_FIBER_PAGESIZE - 1) / ZEND_FIBER_PAGESIZE * ZEND_FIBER_PAGESIZE; - size_t msize = stack->size + ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE; + stack->size = (size + page_size - 1) / page_size * page_size; + const size_t msize = stack->size + ZEND_FIBER_GUARD_PAGES * page_size; #ifdef PHP_WIN32 pointer = VirtualAlloc(0, msize, MEM_COMMIT, PAGE_READWRITE); @@ -117,7 +118,7 @@ static zend_bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) # if ZEND_FIBER_GUARD_PAGES DWORD protect; - if (!VirtualProtect(pointer, ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE, PAGE_READWRITE | PAGE_GUARD, &protect)) { + if (!VirtualProtect(pointer, ZEND_FIBER_GUARD_PAGES * page_size, PAGE_READWRITE | PAGE_GUARD, &protect)) { VirtualFree(pointer, 0, MEM_RELEASE); return 0; } @@ -130,18 +131,18 @@ static zend_bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) } # if ZEND_FIBER_GUARD_PAGES - if (mprotect(pointer, ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE, PROT_NONE) < 0) { + if (mprotect(pointer, ZEND_FIBER_GUARD_PAGES * page_size, PROT_NONE) < 0) { munmap(pointer, msize); return 0; } # endif #endif - stack->pointer = (void *) ((uintptr_t) pointer + ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE); + stack->pointer = (void *) ((uintptr_t) pointer + ZEND_FIBER_GUARD_PAGES * page_size); #ifdef VALGRIND_STACK_REGISTER uintptr_t base = (uintptr_t) stack->pointer; - stack->valgrind = VALGRIND_STACK_REGISTER(base, base + msize - ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE); + stack->valgrind = VALGRIND_STACK_REGISTER(base, base + msize - ZEND_FIBER_GUARD_PAGES * page_size); #endif return 1; @@ -158,14 +159,14 @@ void zend_fiber_stack_free(zend_fiber_stack *stack) /* {{{ */ VALGRIND_STACK_DEREGISTER(stack->valgrind); #endif - void *pointer = (void *) ((uintptr_t) stack->pointer - ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE); + const size_t page_size = zend_fiber_page_size(); + + void *pointer = (void *) ((uintptr_t) stack->pointer - ZEND_FIBER_GUARD_PAGES * page_size); #ifdef PHP_WIN32 VirtualFree(pointer, 0, MEM_RELEASE); #else - size_t length = stack->size + ZEND_FIBER_GUARD_PAGES * ZEND_FIBER_PAGESIZE; - - munmap(pointer, length); + munmap(pointer, stack->size + ZEND_FIBER_GUARD_PAGES * page_size); #endif stack->pointer = NULL; diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index e425adc0a3358..27eebdca77059 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -55,14 +55,24 @@ typedef struct _zend_fiber_context { #if _POSIX_MAPPED_FILES # include # include -# define ZEND_FIBER_PAGESIZE sysconf(_SC_PAGESIZE) + +ZEND_API static zend_always_inline size_t zend_fiber_page_size() +{ + static size_t page_size; + + if (!page_size) { + page_size = sysconf(_SC_PAGESIZE); + } + + return page_size; +} #else -# define ZEND_FIBER_PAGESIZE 4096 +# define zend_fiber_page_size() 4096 #endif #define ZEND_FIBER_GUARD_PAGES 1 -#define ZEND_FIBER_DEFAULT_STACK_SIZE (ZEND_FIBER_PAGESIZE * (((sizeof(void *)) < 8) ? 512 : 2048)) +#define ZEND_FIBER_DEFAULT_PAGE_COUNT (((sizeof(void *)) < 8) ? 512 : 2048) typedef struct _zend_fiber { /* Fiber PHP object handle. */ diff --git a/main/main.c b/main/main.c index 7004e705b8acd..498388a8f68bb 100644 --- a/main/main.c +++ b/main/main.c @@ -307,22 +307,23 @@ static PHP_INI_MH(OnSetLogFilter) static PHP_INI_MH(OnUpdateFiberStackSize) { zend_long tmp; + const size_t page_size = zend_fiber_page_size(); if (OnUpdateLongGEZero(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage) == FAILURE) { return FAILURE; } if (EG(fiber_stack_size) == 0) { - EG(fiber_stack_size) = ZEND_FIBER_DEFAULT_STACK_SIZE; + EG(fiber_stack_size) = page_size * ZEND_FIBER_DEFAULT_PAGE_COUNT; return SUCCESS; } EG(fiber_stack_size) += ZEND_FIBER_GUARD_PAGES; - tmp = ZEND_FIBER_PAGESIZE * EG(fiber_stack_size); + tmp = page_size * EG(fiber_stack_size); - if (tmp / ZEND_FIBER_PAGESIZE != EG(fiber_stack_size)) { - EG(fiber_stack_size) = ZEND_FIBER_DEFAULT_STACK_SIZE; + if (tmp / page_size != EG(fiber_stack_size)) { + EG(fiber_stack_size) = page_size * ZEND_FIBER_DEFAULT_PAGE_COUNT; return FAILURE; } From 96a97184c6989c9e5d40f415449dac84e1058d54 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Mon, 19 Apr 2021 13:15:04 -0500 Subject: [PATCH 08/43] Remove ZEND_PARSE_PARAMS_THROW from ZPP No longer necessary, ZPP now always throws. --- Zend/zend_fibers.c | 8 ++++---- ext/reflection/php_reflection.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index d804d13e4db3a..3544d48a32c85 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -457,7 +457,7 @@ ZEND_METHOD(Fiber, __construct) { zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis()); - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) + ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_FUNC_EX(fiber->fci, fiber->fci_cache, 0, 0) ZEND_PARSE_PARAMETERS_END(); @@ -519,7 +519,7 @@ ZEND_METHOD(Fiber, suspend) return; } - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) + ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_ZVAL(value); ZEND_PARSE_PARAMETERS_END(); @@ -573,7 +573,7 @@ ZEND_METHOD(Fiber, resume) zend_fiber *fiber; zval *value = NULL; - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) + ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_ZVAL(value); ZEND_PARSE_PARAMETERS_END(); @@ -610,7 +610,7 @@ ZEND_METHOD(Fiber, throw) zend_fiber *fiber; zval *exception; - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) + ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_OBJECT_OF_CLASS_EX(exception, zend_ce_throwable, 0, 0) ZEND_PARSE_PARAMETERS_END(); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 57a446ca94d66..0cc8fca5f897c 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6758,7 +6758,7 @@ ZEND_METHOD(ReflectionFiber, __construct) object = ZEND_THIS; intern = Z_REFLECTION_P(object); - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) + ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_OBJECT_OF_CLASS_EX(fiber, zend_ce_fiber, 0, 0) ZEND_PARSE_PARAMETERS_END(); @@ -6794,7 +6794,7 @@ ZEND_METHOD(ReflectionFiber, getTrace) zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); zend_long options = DEBUG_BACKTRACE_PROVIDE_OBJECT; - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) + ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_LONG(options); ZEND_PARSE_PARAMETERS_END(); From aaf2759abafdc7549a79c7380dacec3ea22aaaaf Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Mon, 19 Apr 2021 13:17:51 -0500 Subject: [PATCH 09/43] Update stub hash --- Zend/zend_fibers_arginfo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_fibers_arginfo.h b/Zend/zend_fibers_arginfo.h index 9bd1500947af6..5a0c02d83aaa0 100644 --- a/Zend/zend_fibers_arginfo.h +++ b/Zend/zend_fibers_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6ff9337518d40d5e989966a4b1e1a1d4882c65cf */ + * Stub hash: 378c07d55a5eb5096370884733e5d0fd7fe5af93 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Fiber___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) From 9f6090f37846a3020fc4689b99f8b837b2ec3a93 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Mon, 19 Apr 2021 13:19:05 -0500 Subject: [PATCH 10/43] Remove visibility attribute --- Zend/zend_fibers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index 27eebdca77059..bfd3c1b18f5db 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -56,7 +56,7 @@ typedef struct _zend_fiber_context { # include # include -ZEND_API static zend_always_inline size_t zend_fiber_page_size() +static zend_always_inline size_t zend_fiber_page_size() { static size_t page_size; From 6032c3b2bea6855888e27a1bbe34161e39beb0f7 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Mon, 19 Apr 2021 13:26:15 -0500 Subject: [PATCH 11/43] Z_PARAM_OBJECT_OF_CLASS_EX to Z_PARAM_OBJECT_OF_CLASS --- Zend/zend_fibers.c | 2 +- ext/reflection/php_reflection.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 3544d48a32c85..1d961f140344c 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -611,7 +611,7 @@ ZEND_METHOD(Fiber, throw) zval *exception; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS_EX(exception, zend_ce_throwable, 0, 0) + Z_PARAM_OBJECT_OF_CLASS(exception, zend_ce_throwable) ZEND_PARSE_PARAMETERS_END(); fiber = (zend_fiber *) Z_OBJ_P(getThis()); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 0cc8fca5f897c..cffc5b5401789 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6759,7 +6759,7 @@ ZEND_METHOD(ReflectionFiber, __construct) intern = Z_REFLECTION_P(object); ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS_EX(fiber, zend_ce_fiber, 0, 0) + Z_PARAM_OBJECT_OF_CLASS(fiber, zend_ce_fiber) ZEND_PARSE_PARAMETERS_END(); if (intern->ce) { From 47b50b9233891e1ed50cdd9bfad307c9a9948873 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Tue, 20 Apr 2021 22:02:37 -0500 Subject: [PATCH 12/43] Add @strict-properties to stub --- Zend/zend_fibers.stub.php | 5 ++++- Zend/zend_fibers_arginfo.h | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Zend/zend_fibers.stub.php b/Zend/zend_fibers.stub.php index 6fd0977c67eff..1ffad6ebceef4 100644 --- a/Zend/zend_fibers.stub.php +++ b/Zend/zend_fibers.stub.php @@ -1,6 +1,9 @@ ce_flags |= ZEND_ACC_FINAL; + class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES; return class_entry; } From 340506c60450a261f277b3cc9466318a77851b11 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Tue, 20 Apr 2021 22:11:56 -0500 Subject: [PATCH 13/43] Variety of minor changes from feedback --- Zend/zend_fibers.c | 83 ++++++++++++++++++++++++++++++---------------- Zend/zend_fibers.h | 20 ++--------- 2 files changed, 56 insertions(+), 47 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 1d961f140344c..7c61011686fe6 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -17,9 +17,9 @@ +----------------------------------------------------------------------+ */ -#include "php.h" #include "zend.h" #include "zend_API.h" +#include "zend_ini.h" #include "zend_vm.h" #include "zend_interfaces.h" #include "zend_exceptions.h" @@ -29,7 +29,13 @@ #include "zend_fibers_arginfo.h" #ifdef HAVE_VALGRIND -#include "valgrind/valgrind.h" +#include +#endif + +#ifndef PHP_WIN32 +#include +#include +#include #endif ZEND_API zend_class_entry *zend_ce_fiber; @@ -52,6 +58,8 @@ typedef struct _transfer_t { extern fcontext_t make_fcontext(void *sp, size_t size, void (*fn)(transfer_t)); extern transfer_t jump_fcontext(fcontext_t to, void *vp); +#define ZEND_FIBER_DEFAULT_PAGE_SIZE 4096 + #define ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, trace_num) do { \ stack = EG(vm_stack); \ stack->top = EG(vm_stack_top); \ @@ -86,7 +94,7 @@ ZEND_API void zend_observer_fiber_switch_register(zend_observer_fiber_switch_han } /* }}} */ -zend_always_inline static void zend_observer_fiber_switch_notify(zend_fiber *from, zend_fiber *to) /* {{{ */ +static zend_always_inline void zend_observer_fiber_switch_notify(zend_fiber *from, zend_fiber *to) /* {{{ */ { zend_llist_element *element; zend_observer_fiber_switch_handler callback; @@ -98,7 +106,22 @@ zend_always_inline static void zend_observer_fiber_switch_notify(zend_fiber *fro } /* }}} */ -static zend_bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) /* {{{ */ +size_t zend_fiber_page_size() +{ +#if _POSIX_MAPPED_FILES + static size_t page_size; + + if (!page_size) { + page_size = sysconf(_SC_PAGESIZE); + } + + return page_size; +#else + return ZEND_FIBER_DEFAULT_PAGE_SIZE; +#endif +} + +static bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) /* {{{ */ { void *pointer; const size_t page_size = zend_fiber_page_size(); @@ -112,7 +135,7 @@ static zend_bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) pointer = VirtualAlloc(0, msize, MEM_COMMIT, PAGE_READWRITE); if (!pointer) { - return 0; + return false; } # if ZEND_FIBER_GUARD_PAGES @@ -120,20 +143,20 @@ static zend_bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) if (!VirtualProtect(pointer, ZEND_FIBER_GUARD_PAGES * page_size, PAGE_READWRITE | PAGE_GUARD, &protect)) { VirtualFree(pointer, 0, MEM_RELEASE); - return 0; + return false; } # endif #else pointer = mmap(NULL, msize, PROT_READ | PROT_WRITE, ZEND_FIBER_STACK_FLAGS, -1, 0); if (pointer == MAP_FAILED) { - return 0; + return false; } # if ZEND_FIBER_GUARD_PAGES if (mprotect(pointer, ZEND_FIBER_GUARD_PAGES * page_size, PROT_NONE) < 0) { munmap(pointer, msize); - return 0; + return false; } # endif #endif @@ -145,7 +168,7 @@ static zend_bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) stack->valgrind = VALGRIND_STACK_REGISTER(base, base + msize - ZEND_FIBER_GUARD_PAGES * page_size); #endif - return 1; + return true; } /* }}} */ @@ -195,10 +218,10 @@ static ZEND_NORETURN void zend_fiber_trampoline(transfer_t transfer) /* {{{ */ } /* }}} */ -ZEND_API zend_bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_coroutine coroutine, size_t stack_size) /* {{{ */ +ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_coroutine coroutine, size_t stack_size) /* {{{ */ { if (UNEXPECTED(!zend_fiber_stack_allocate(&context->stack, stack_size))) { - return 0; + return false; } // Stack grows down, calculate the top of the stack. make_fcontext then shifts pointer to lower 16-byte boundary. @@ -208,22 +231,18 @@ ZEND_API zend_bool zend_fiber_init_context(zend_fiber_context *context, zend_fib if (UNEXPECTED(!context->self)) { zend_fiber_stack_free(&context->stack); - return 0; + return false; } context->function = coroutine; context->caller = NULL; - return 1; + return true; } /* }}} */ ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context) /* {{{ */ { - if (!context) { - return; - } - zend_fiber_stack_free(&context->stack); } /* }}} */ @@ -352,7 +371,7 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context) / fiber->execute_data = (zend_execute_data *) stack->top; - ZEND_SECURE_ZERO(fiber->execute_data, sizeof(zend_execute_data)); + memset(fiber->execute_data, 0, sizeof(zend_execute_data)); EG(current_execute_data) = fiber->execute_data; EG(jit_trace_num) = 0; @@ -392,15 +411,13 @@ static zend_object *zend_fiber_object_create(zend_class_entry *ce) /* {{{ */ zend_fiber *fiber; fiber = emalloc(sizeof(zend_fiber)); - ZEND_SECURE_ZERO(fiber, sizeof(zend_fiber)); + memset(fiber, 0, sizeof(zend_fiber)); fiber->id = EG(next_fiber_id)++; zend_object_std_init(&fiber->std, ce); fiber->std.handlers = &zend_fiber_handlers; - ZVAL_UNDEF(&fiber->value); - zend_hash_index_add_ptr(&EG(fibers), fiber->id, fiber); return &fiber->std; @@ -458,7 +475,7 @@ ZEND_METHOD(Fiber, __construct) zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis()); ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_FUNC_EX(fiber->fci, fiber->fci_cache, 0, 0) + Z_PARAM_FUNC(fiber->fci, fiber->fci_cache) ZEND_PARSE_PARAMETERS_END(); // Keep a reference to closures or callable objects until the fiber is started. @@ -470,14 +487,22 @@ ZEND_METHOD(Fiber, __construct) ZEND_METHOD(Fiber, start) { zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis()); + zval *params; + uint32_t param_count; + zend_array *named_params; + + ZEND_PARSE_PARAMETERS_START(0, -1) + Z_PARAM_VARIADIC_WITH_NAMED(params, param_count, named_params); + ZEND_PARSE_PARAMETERS_END(); if (fiber->status != ZEND_FIBER_STATUS_INIT) { zend_throw_error(zend_ce_fiber_error, "Cannot start a fiber that has already been started"); return; } - fiber->fci.params = ZEND_CALL_ARG(execute_data, 1); - fiber->fci.param_count = ZEND_CALL_NUM_ARGS(execute_data); + fiber->fci.params = params; + fiber->fci.param_count = param_count; + fiber->fci.named_params = named_params; if (!zend_fiber_init_context(&fiber->context, zend_fiber_execute, EG(fiber_stack_size))) { zend_throw_error(NULL, "Could not create fiber context"); @@ -505,6 +530,11 @@ ZEND_METHOD(Fiber, suspend) zend_fiber *fiber = EG(current_fiber); zval *error, *value = NULL; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(value); + ZEND_PARSE_PARAMETERS_END(); + if (UNEXPECTED(!fiber)) { zend_throw_error(zend_ce_fiber_error, "Cannot suspend outside of a fiber"); return; @@ -519,11 +549,6 @@ ZEND_METHOD(Fiber, suspend) return; } - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(value); - ZEND_PARSE_PARAMETERS_END(); - if (value) { ZVAL_COPY(&fiber->value, value); } else { diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index bfd3c1b18f5db..48823b3a9ac27 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -52,24 +52,6 @@ typedef struct _zend_fiber_context { zend_fiber_stack stack; } zend_fiber_context; -#if _POSIX_MAPPED_FILES -# include -# include - -static zend_always_inline size_t zend_fiber_page_size() -{ - static size_t page_size; - - if (!page_size) { - page_size = sysconf(_SC_PAGESIZE); - } - - return page_size; -} -#else -# define zend_fiber_page_size() 4096 -#endif - #define ZEND_FIBER_GUARD_PAGES 1 #define ZEND_FIBER_DEFAULT_PAGE_COUNT (((sizeof(void *)) < 8) ? 512 : 2048) @@ -132,6 +114,8 @@ ZEND_COLD ZEND_NORETURN void zend_error_suspend_fiber( ZEND_API void zend_fiber_switch_context(zend_fiber_context *to); ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current); +size_t zend_fiber_page_size(); + #define ZEND_FIBER_VM_STACK_SIZE (1024 * sizeof(zval)) END_EXTERN_C() From 12dc23808acda8421d252bac93561bb3b79d38ce Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Tue, 20 Apr 2021 22:15:33 -0500 Subject: [PATCH 14/43] RETURN_THROWS() where appropriate Removed a status check that is impossible to be true. --- Zend/zend_fibers.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 7c61011686fe6..49cad71e99b76 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -497,7 +497,7 @@ ZEND_METHOD(Fiber, start) if (fiber->status != ZEND_FIBER_STATUS_INIT) { zend_throw_error(zend_ce_fiber_error, "Cannot start a fiber that has already been started"); - return; + RETURN_THROWS(); } fiber->fci.params = params; @@ -506,7 +506,7 @@ ZEND_METHOD(Fiber, start) if (!zend_fiber_init_context(&fiber->context, zend_fiber_execute, EG(fiber_stack_size))) { zend_throw_error(NULL, "Could not create fiber context"); - return; + RETURN_THROWS(); } fiber->status = ZEND_FIBER_STATUS_RUNNING; @@ -537,16 +537,12 @@ ZEND_METHOD(Fiber, suspend) if (UNEXPECTED(!fiber)) { zend_throw_error(zend_ce_fiber_error, "Cannot suspend outside of a fiber"); - return; + RETURN_THROWS(); } - if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_RUNNING)) { - if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { - zend_throw_error(zend_ce_fiber_error, "Cannot suspend in a force closed fiber"); - } else { - zend_throw_error(zend_ce_fiber_error, "Cannot suspend in a fiber that is not running"); - } - return; + if (UNEXPECTED(fiber->status == ZEND_FIBER_STATUS_SHUTDOWN)) { + zend_throw_error(zend_ce_fiber_error, "Cannot suspend in a force closed fiber"); + RETURN_THROWS(); } if (value) { @@ -570,7 +566,7 @@ ZEND_METHOD(Fiber, suspend) } else { zend_throw_fiber_exit(); } - return; + RETURN_THROWS(); } fiber->status = ZEND_FIBER_STATUS_RUNNING; @@ -607,7 +603,7 @@ ZEND_METHOD(Fiber, resume) if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_SUSPENDED)) { zend_throw_error(zend_ce_fiber_error, "Cannot resume a fiber that is not suspended"); - return; + RETURN_THROWS(); } if (value) { @@ -643,7 +639,7 @@ ZEND_METHOD(Fiber, throw) if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_SUSPENDED)) { zend_throw_error(zend_ce_fiber_error, "Cannot resume a fiber that is not suspended"); - return; + RETURN_THROWS(); } Z_ADDREF_P(exception); @@ -735,7 +731,7 @@ ZEND_METHOD(Fiber, getReturn) } zend_throw_error(zend_ce_fiber_error, "Cannot get fiber return value: %s", message); - return; + RETURN_THROWS(); } RETURN_COPY(&fiber->value); From f285a3ae03e33886ff13523c9459d59a617360b0 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Tue, 20 Apr 2021 22:19:06 -0500 Subject: [PATCH 15/43] Rename fiber->error to exception Also removed unnecessary opline adjustment. --- Zend/zend_fibers.c | 22 ++++++++++------------ Zend/zend_fibers.h | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 49cad71e99b76..8e508cbde7427 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -528,7 +528,7 @@ ZEND_METHOD(Fiber, start) ZEND_METHOD(Fiber, suspend) { zend_fiber *fiber = EG(current_fiber); - zval *error, *value = NULL; + zval *exception, *value = NULL; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL @@ -573,18 +573,16 @@ ZEND_METHOD(Fiber, suspend) // Add reference while fiber is running. GC_ADDREF(&fiber->std); - if (!fiber->error) { - RETVAL_COPY_VALUE(&fiber->value); - ZVAL_UNDEF(&fiber->value); - return; - } + if (fiber->exception) { + exception = fiber->exception; + fiber->exception = NULL; - error = fiber->error; - fiber->error = NULL; + zend_throw_exception_object(exception); + RETURN_THROWS(); + } - execute_data->opline--; - zend_throw_exception_object(error); - execute_data->opline++; + RETVAL_COPY_VALUE(&fiber->value); + ZVAL_UNDEF(&fiber->value); } /* }}} */ @@ -643,7 +641,7 @@ ZEND_METHOD(Fiber, throw) } Z_ADDREF_P(exception); - fiber->error = exception; + fiber->exception = exception; fiber->status = ZEND_FIBER_STATUS_RUNNING; diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index 48823b3a9ac27..0d72d0f9d627f 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -77,7 +77,7 @@ typedef struct _zend_fiber { zend_execute_data *execute_data; /* Exception to be thrown from Fiber::suspend(). */ - zval *error; + zval *exception; /* Storage for temporaries and fiber return value. */ zval value; From a8b985aa15ea180ce402006ade461be742c1e0d2 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Tue, 20 Apr 2021 22:59:35 -0500 Subject: [PATCH 16/43] Remove fiber ID property --- Zend/zend.c | 1 - Zend/zend_fibers.c | 7 ++----- Zend/zend_fibers.h | 3 --- Zend/zend_globals.h | 3 --- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index b6cc668180c6c..bb42cbfefe311 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -761,7 +761,6 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{ executor_globals->exception = NULL; executor_globals->objects_store.object_buckets = NULL; executor_globals->current_fiber = NULL; - executor_globals->next_fiber_id = 0; executor_globals->fiber_error = NULL; #ifdef ZEND_WIN32 zend_get_windows_version_info(&executor_globals->windows_version_info); diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 8e508cbde7427..8fbe6d9fca634 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -413,12 +413,10 @@ static zend_object *zend_fiber_object_create(zend_class_entry *ce) /* {{{ */ fiber = emalloc(sizeof(zend_fiber)); memset(fiber, 0, sizeof(zend_fiber)); - fiber->id = EG(next_fiber_id)++; - zend_object_std_init(&fiber->std, ce); fiber->std.handlers = &zend_fiber_handlers; - zend_hash_index_add_ptr(&EG(fibers), fiber->id, fiber); + zend_hash_index_add_ptr(&EG(fibers), (uintptr_t) fiber, fiber); return &fiber->std; } @@ -443,7 +441,7 @@ static void zend_fiber_object_destroy(zend_object *object) /* {{{ */ zval_ptr_dtor(&fiber->value); } - zend_hash_index_del(&EG(fibers), fiber->id); + zend_hash_index_del(&EG(fibers), (uintptr_t) fiber); zend_fiber_destroy_context(&fiber->context); @@ -783,7 +781,6 @@ void zend_register_fiber_ce(void) void zend_fiber_init(void) { EG(current_fiber) = NULL; - EG(next_fiber_id) = 0; EG(fiber_error) = NULL; zend_hash_init(&EG(fibers), 0, NULL, NULL, 0); diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index 0d72d0f9d627f..b29cd72bb2f78 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -60,9 +60,6 @@ typedef struct _zend_fiber { /* Fiber PHP object handle. */ zend_object std; - /* Unique ID assigned to this fiber. */ - zend_long id; - /* Status of the fiber, one of the ZEND_FIBER_STATUS_* constants. */ zend_uchar status; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 02da9ac86393e..d5c33a78763fd 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -254,9 +254,6 @@ struct _zend_executor_globals { /* Active fiber, NULL when in main thread. */ zend_fiber *current_fiber; - /* Next fiber ID. */ - zend_long next_fiber_id; - /* Default fiber C stack size. */ zend_long fiber_stack_size; From da140b2d8f813badffbbe52825673433e3fb59fa Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Tue, 20 Apr 2021 23:13:08 -0500 Subject: [PATCH 17/43] Move ini setting to zend.c Changed setting to use values similar to memory_limit, e.g., fiber.stack_size = 8M --- Zend/zend.c | 13 +++++++++++++ main/main.c | 32 -------------------------------- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index bb42cbfefe311..fe56754ac5262 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -175,6 +175,17 @@ static ZEND_INI_MH(OnSetExceptionStringParamMaxLen) /* {{{ */ } /* }}} */ +static ZEND_INI_MH(OnUpdateFiberStackSize) /* {{{ */ +{ + if (new_value) { + EG(fiber_stack_size) = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + } else { + EG(fiber_stack_size) = zend_fiber_page_size() * ZEND_FIBER_DEFAULT_PAGE_COUNT; + } + return SUCCESS; +} +/* }}} */ + #if ZEND_DEBUG # define SIGNAL_CHECK_DEFAULT "1" #else @@ -193,6 +204,8 @@ ZEND_INI_BEGIN() #endif STD_ZEND_INI_BOOLEAN("zend.exception_ignore_args", "0", ZEND_INI_ALL, OnUpdateBool, exception_ignore_args, zend_executor_globals, executor_globals) STD_ZEND_INI_ENTRY("zend.exception_string_param_max_len", "15", ZEND_INI_ALL, OnSetExceptionStringParamMaxLen, exception_string_param_max_len, zend_executor_globals, executor_globals) + STD_ZEND_INI_ENTRY("fiber.stack_size", NULL, ZEND_INI_ALL, OnUpdateFiberStackSize, fiber_stack_size, zend_executor_globals, executor_globals) + ZEND_INI_END() ZEND_API size_t zend_vspprintf(char **pbuf, size_t max_len, const char *format, va_list ap) /* {{{ */ diff --git a/main/main.c b/main/main.c index 498388a8f68bb..78c0f258c12b0 100644 --- a/main/main.c +++ b/main/main.c @@ -303,36 +303,6 @@ static PHP_INI_MH(OnSetLogFilter) } /* }}} */ -/* {{{ PHP_INI_MH */ -static PHP_INI_MH(OnUpdateFiberStackSize) -{ - zend_long tmp; - const size_t page_size = zend_fiber_page_size(); - - if (OnUpdateLongGEZero(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage) == FAILURE) { - return FAILURE; - } - - if (EG(fiber_stack_size) == 0) { - EG(fiber_stack_size) = page_size * ZEND_FIBER_DEFAULT_PAGE_COUNT; - return SUCCESS; - } - - EG(fiber_stack_size) += ZEND_FIBER_GUARD_PAGES; - - tmp = page_size * EG(fiber_stack_size); - - if (tmp / page_size != EG(fiber_stack_size)) { - EG(fiber_stack_size) = page_size * ZEND_FIBER_DEFAULT_PAGE_COUNT; - return FAILURE; - } - - EG(fiber_stack_size) = tmp; - - return SUCCESS; -} -/* }}} */ - /* {{{ php_disable_classes */ static void php_disable_classes(void) { @@ -768,8 +738,6 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("syslog.facility", "LOG_USER", PHP_INI_SYSTEM, OnSetFacility, syslog_facility, php_core_globals, core_globals) STD_PHP_INI_ENTRY("syslog.ident", "php", PHP_INI_SYSTEM, OnUpdateString, syslog_ident, php_core_globals, core_globals) STD_PHP_INI_ENTRY("syslog.filter", "no-ctrl", PHP_INI_ALL, OnSetLogFilter, syslog_filter, php_core_globals, core_globals) - - STD_PHP_INI_ENTRY("fiber.stack_size", "0", PHP_INI_ALL, OnUpdateFiberStackSize, fiber_stack_size, zend_executor_globals, executor_globals) PHP_INI_END() /* }}} */ From 6dab14e85ba6f45a26fdc997137b2eef5d02bdd5 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Tue, 20 Apr 2021 23:54:24 -0500 Subject: [PATCH 18/43] Add test for fibers created during cleanup --- .../fibers/fiber-created-during-cleanup.phpt | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Zend/tests/fibers/fiber-created-during-cleanup.phpt diff --git a/Zend/tests/fibers/fiber-created-during-cleanup.phpt b/Zend/tests/fibers/fiber-created-during-cleanup.phpt new file mode 100644 index 0000000000000..5b6aa7a308827 --- /dev/null +++ b/Zend/tests/fibers/fiber-created-during-cleanup.phpt @@ -0,0 +1,43 @@ +--TEST-- +Fibers created during cleanup +--FILE-- +start(); + } + }); + $fibers[$i]->start(); +} + +?> +--EXPECT-- +finally +new +new finally +finally +new +new finally +finally +new +new finally +finally +new +new finally +finally +new +new finally From 75c4fa3c55b3f639ebd29e70f69e169841bd5306 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Wed, 21 Apr 2021 10:17:53 -0500 Subject: [PATCH 19/43] Reduce default fiber stack size 8M to 2M on 64-bit, 2M to 1M on 32-bit. --- Zend/zend.c | 2 +- Zend/zend_fibers.c | 2 +- Zend/zend_fibers.h | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index fe56754ac5262..3bb6fe2045e37 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -180,7 +180,7 @@ static ZEND_INI_MH(OnUpdateFiberStackSize) /* {{{ */ if (new_value) { EG(fiber_stack_size) = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); } else { - EG(fiber_stack_size) = zend_fiber_page_size() * ZEND_FIBER_DEFAULT_PAGE_COUNT; + EG(fiber_stack_size) = ZEND_FIBER_DEFAULT_C_STACK_SIZE; } return SUCCESS; } diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 8fbe6d9fca634..f840f9968e5ee 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -106,7 +106,7 @@ static zend_always_inline void zend_observer_fiber_switch_notify(zend_fiber *fro } /* }}} */ -size_t zend_fiber_page_size() +static size_t zend_fiber_page_size() { #if _POSIX_MAPPED_FILES static size_t page_size; diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index b29cd72bb2f78..0b07977f6ae63 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -52,10 +52,6 @@ typedef struct _zend_fiber_context { zend_fiber_stack stack; } zend_fiber_context; -#define ZEND_FIBER_GUARD_PAGES 1 - -#define ZEND_FIBER_DEFAULT_PAGE_COUNT (((sizeof(void *)) < 8) ? 512 : 2048) - typedef struct _zend_fiber { /* Fiber PHP object handle. */ zend_object std; @@ -111,7 +107,9 @@ ZEND_COLD ZEND_NORETURN void zend_error_suspend_fiber( ZEND_API void zend_fiber_switch_context(zend_fiber_context *to); ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current); -size_t zend_fiber_page_size(); +#define ZEND_FIBER_GUARD_PAGES 1 + +#define ZEND_FIBER_DEFAULT_C_STACK_SIZE (4096 * (((sizeof(void *)) < 8) ? 256 : 512)) #define ZEND_FIBER_VM_STACK_SIZE (1024 * sizeof(zval)) From e2bd2c63b158108382a7149b433e59dd48c22fc7 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Wed, 21 Apr 2021 10:34:09 -0500 Subject: [PATCH 20/43] Add zend_error_zstr_at and use for fatal errors --- Zend/zend_fibers.c | 16 ++++++++-------- Zend/zend_fibers.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index f840f9968e5ee..26672e16e36d3 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -315,25 +315,25 @@ static void zend_fiber_switch_to(zend_fiber *fiber) /* {{{ */ } zend_fiber_error *error = EG(fiber_error); - zend_error_at_noreturn(error->type, error->filename, error->lineno, "%s", ZSTR_VAL(error->message)); + zend_error_zstr_at(error->type, error->filename, error->lineno, error->message); } } /* }}} */ /* {{{ */ -ZEND_COLD ZEND_NORETURN void zend_error_suspend_fiber( +ZEND_COLD void zend_error_suspend_fiber( int orig_type, const char *error_filename, uint32_t error_lineno, zend_string *message) { ZEND_ASSERT(EG(current_fiber) && "Must be within an active fiber!"); - zend_fiber_error error; + zend_fiber_error *error = emalloc(sizeof(zend_fiber_error)); - error.type = orig_type; - error.filename = error_filename; - error.lineno = error_lineno; - error.message = message; + error->type = orig_type; + error->filename = error_filename; + error->lineno = error_lineno; + error->message = message; - EG(fiber_error) = &error; + EG(fiber_error) = error; zend_fiber_suspend(EG(current_fiber)); diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index 0b07977f6ae63..3889ee26430b7 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -101,7 +101,7 @@ const char *zend_fiber_backend_info(void); ZEND_API zend_bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_coroutine coroutine, size_t stack_size); ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context); -ZEND_COLD ZEND_NORETURN void zend_error_suspend_fiber( +ZEND_COLD void zend_error_suspend_fiber( int orig_type, const char *error_filename, uint32_t error_lineno, zend_string *message); ZEND_API void zend_fiber_switch_context(zend_fiber_context *to); From cf14c633ea0a0e00a51521a67aa55659574b3e52 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Wed, 21 Apr 2021 12:39:00 -0500 Subject: [PATCH 21/43] Add silence operator outside fiber test --- ...hpt => silence-operator-inside-fiber.phpt} | 4 +-- .../silence-operator-outside-fiber.phpt | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) rename Zend/tests/fibers/{silence-operator.phpt => silence-operator-inside-fiber.phpt} (78%) create mode 100644 Zend/tests/fibers/silence-operator-outside-fiber.phpt diff --git a/Zend/tests/fibers/silence-operator.phpt b/Zend/tests/fibers/silence-operator-inside-fiber.phpt similarity index 78% rename from Zend/tests/fibers/silence-operator.phpt rename to Zend/tests/fibers/silence-operator-inside-fiber.phpt index dac8f57673aa0..f391c4dd28cde 100644 --- a/Zend/tests/fibers/silence-operator.phpt +++ b/Zend/tests/fibers/silence-operator-inside-fiber.phpt @@ -23,6 +23,6 @@ trigger_error("Warning D", E_USER_WARNING); ?> --EXPECTF-- -Warning: Warning C in %ssilence-operator.php on line %d +Warning: Warning C in %ssilence-operator-inside-fiber.php on line %d -Warning: Warning D in %ssilence-operator.php on line %d +Warning: Warning D in %ssilence-operator-inside-fiber.php on line %d diff --git a/Zend/tests/fibers/silence-operator-outside-fiber.phpt b/Zend/tests/fibers/silence-operator-outside-fiber.phpt new file mode 100644 index 0000000000000..18f5a9f580798 --- /dev/null +++ b/Zend/tests/fibers/silence-operator-outside-fiber.phpt @@ -0,0 +1,28 @@ +--TEST-- +Silence operator does not leak into fiber +--FILE-- +start(); + +trigger_error("Warning B", E_USER_WARNING); + +@$fiber->resume(); + +trigger_error("Warning D", E_USER_WARNING); + +?> +--EXPECTF-- +Warning: Warning A in %ssilence-operator-outside-fiber.php on line %d + +Warning: Warning B in %ssilence-operator-outside-fiber.php on line %d + +Warning: Warning C in %ssilence-operator-outside-fiber.php on line %d + +Warning: Warning D in %ssilence-operator-outside-fiber.php on line %d From a547a21ddd6827265b98cf2079c37ff74e61d152 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Wed, 21 Apr 2021 12:40:58 -0500 Subject: [PATCH 22/43] Remove unused function --- Zend/zend_fibers.c | 6 ------ Zend/zend_fibers.h | 2 -- 2 files changed, 8 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 26672e16e36d3..1d3ca467a785b 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -196,12 +196,6 @@ void zend_fiber_stack_free(zend_fiber_stack *stack) /* {{{ */ } /* }}} */ -const char *zend_fiber_backend_info(void) /* {{{ */ -{ - return "assembler (boost.context v1.76.0)"; -} -/* }}} */ - static ZEND_NORETURN void zend_fiber_trampoline(transfer_t transfer) /* {{{ */ { zend_fiber_context *context = transfer.data; diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index 3889ee26430b7..f70a6521aa7f1 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -96,8 +96,6 @@ static const zend_uchar ZEND_FIBER_STATUS_SHUTDOWN = 0x10; static const zend_uchar ZEND_FIBER_STATUS_FINISHED = 0x1c; -const char *zend_fiber_backend_info(void); - ZEND_API zend_bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_coroutine coroutine, size_t stack_size); ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context); From 842dce38e08199bb3034e063804702a1859e9f1b Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Wed, 21 Apr 2021 12:44:39 -0500 Subject: [PATCH 23/43] Remove {{{ and }}} --- Zend/zend_fibers.c | 74 ++++++++++------------------------------------ 1 file changed, 16 insertions(+), 58 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 1d3ca467a785b..0b75a4d29916e 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -88,13 +88,12 @@ extern transfer_t jump_fcontext(fcontext_t to, void *vp); -ZEND_API void zend_observer_fiber_switch_register(zend_observer_fiber_switch_handler handler) /* {{{ */ +ZEND_API void zend_observer_fiber_switch_register(zend_observer_fiber_switch_handler handler) { zend_llist_add_element(&zend_fiber_observers_list, &handler); } -/* }}} */ -static zend_always_inline void zend_observer_fiber_switch_notify(zend_fiber *from, zend_fiber *to) /* {{{ */ +static zend_always_inline void zend_observer_fiber_switch_notify(zend_fiber *from, zend_fiber *to) { zend_llist_element *element; zend_observer_fiber_switch_handler callback; @@ -104,7 +103,6 @@ static zend_always_inline void zend_observer_fiber_switch_notify(zend_fiber *fro callback(from, to); } } -/* }}} */ static size_t zend_fiber_page_size() { @@ -121,7 +119,7 @@ static size_t zend_fiber_page_size() #endif } -static bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) /* {{{ */ +static bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) { void *pointer; const size_t page_size = zend_fiber_page_size(); @@ -170,9 +168,8 @@ static bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) /* { return true; } -/* }}} */ -void zend_fiber_stack_free(zend_fiber_stack *stack) /* {{{ */ +void zend_fiber_stack_free(zend_fiber_stack *stack) { if (!stack->pointer) { return; @@ -194,9 +191,8 @@ void zend_fiber_stack_free(zend_fiber_stack *stack) /* {{{ */ stack->pointer = NULL; } -/* }}} */ -static ZEND_NORETURN void zend_fiber_trampoline(transfer_t transfer) /* {{{ */ +static ZEND_NORETURN void zend_fiber_trampoline(transfer_t transfer) { zend_fiber_context *context = transfer.data; @@ -210,9 +206,8 @@ static ZEND_NORETURN void zend_fiber_trampoline(transfer_t transfer) /* {{{ */ abort(); } -/* }}} */ -ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_coroutine coroutine, size_t stack_size) /* {{{ */ +ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_coroutine coroutine, size_t stack_size) { if (UNEXPECTED(!zend_fiber_stack_allocate(&context->stack, stack_size))) { return false; @@ -233,15 +228,13 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_co return true; } -/* }}} */ -ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context) /* {{{ */ +ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context) { zend_fiber_stack_free(&context->stack); } -/* }}} */ -ZEND_API void zend_fiber_switch_context(zend_fiber_context *to) /* {{{ */ +ZEND_API void zend_fiber_switch_context(zend_fiber_context *to) { ZEND_ASSERT(to && to->self && to->stack.pointer && "Invalid fiber context"); @@ -249,9 +242,8 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_context *to) /* {{{ */ to->self = transfer.context; } -/* }}} */ -ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current) /* {{{ */ +ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current) { ZEND_ASSERT(current && current->caller && current->stack.pointer && "Invalid fiber context"); @@ -259,9 +251,8 @@ ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current) /* {{{ */ current->caller = transfer.context; } -/* }}} */ -static void zend_fiber_suspend(zend_fiber *fiber) /* {{{ */ +static void zend_fiber_suspend(zend_fiber *fiber) { zend_vm_stack stack; size_t stack_page_size; @@ -275,9 +266,8 @@ static void zend_fiber_suspend(zend_fiber *fiber) /* {{{ */ ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num); } -/* }}} */ -static void zend_fiber_switch_to(zend_fiber *fiber) /* {{{ */ +static void zend_fiber_switch_to(zend_fiber *fiber) { zend_fiber *previous; zend_vm_stack stack; @@ -312,9 +302,7 @@ static void zend_fiber_switch_to(zend_fiber *fiber) /* {{{ */ zend_error_zstr_at(error->type, error->filename, error->lineno, error->message); } } -/* }}} */ -/* {{{ */ ZEND_COLD void zend_error_suspend_fiber( int orig_type, const char *error_filename, uint32_t error_lineno, zend_string *message) { @@ -333,9 +321,8 @@ ZEND_COLD void zend_error_suspend_fiber( abort(); // This fiber should never be resumed. } -/* }}} */ -static zend_always_inline zend_vm_stack zend_fiber_vm_stack_alloc(size_t size) /* {{{ */ +static zend_always_inline zend_vm_stack zend_fiber_vm_stack_alloc(size_t size) { zend_vm_stack page = emalloc(size); @@ -345,9 +332,8 @@ static zend_always_inline zend_vm_stack zend_fiber_vm_stack_alloc(size_t size) / return page; } -/* }}} */ -static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context) /* {{{ */ +static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context) { zend_fiber *fiber = EG(current_fiber); ZEND_ASSERT(fiber); @@ -398,9 +384,8 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context) / // Remove reference added at last resume. GC_DELREF(&fiber->std); } -/* }}} */ -static zend_object *zend_fiber_object_create(zend_class_entry *ce) /* {{{ */ +static zend_object *zend_fiber_object_create(zend_class_entry *ce) { zend_fiber *fiber; @@ -414,9 +399,8 @@ static zend_object *zend_fiber_object_create(zend_class_entry *ce) /* {{{ */ return &fiber->std; } -/* }}} */ -static void zend_fiber_object_destroy(zend_object *object) /* {{{ */ +static void zend_fiber_object_destroy(zend_object *object) { zend_fiber *fiber = (zend_fiber *) object; @@ -441,9 +425,8 @@ static void zend_fiber_object_destroy(zend_object *object) /* {{{ */ zend_object_std_dtor(&fiber->std); } -/* }}} */ -void zend_fiber_cleanup(void) /* {{{ */ +void zend_fiber_cleanup(void) { zend_fiber *fiber; zend_object *exception = EG(exception); @@ -459,9 +442,7 @@ void zend_fiber_cleanup(void) /* {{{ */ EG(exception) = exception; } -/* }}} */ -/* {{{ proto Fiber::__construct(callable $callback) */ ZEND_METHOD(Fiber, __construct) { zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis()); @@ -473,9 +454,7 @@ ZEND_METHOD(Fiber, __construct) // Keep a reference to closures or callable objects until the fiber is started. Z_TRY_ADDREF(fiber->fci.function_name); } -/* }}} */ -/* {{{ proto mixed Fiber::start(mixed ...$args) */ ZEND_METHOD(Fiber, start) { zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis()); @@ -514,9 +493,7 @@ ZEND_METHOD(Fiber, start) RETVAL_COPY_VALUE(&fiber->value); ZVAL_UNDEF(&fiber->value); } -/* }}} */ -/* {{{ proto mixed Fiber::suspend(mixed $value) */ ZEND_METHOD(Fiber, suspend) { zend_fiber *fiber = EG(current_fiber); @@ -576,9 +553,7 @@ ZEND_METHOD(Fiber, suspend) RETVAL_COPY_VALUE(&fiber->value); ZVAL_UNDEF(&fiber->value); } -/* }}} */ -/* {{{ proto mixed Fiber::resume(mixed $value = null) */ ZEND_METHOD(Fiber, resume) { zend_fiber *fiber; @@ -613,9 +588,7 @@ ZEND_METHOD(Fiber, resume) RETVAL_COPY_VALUE(&fiber->value); ZVAL_UNDEF(&fiber->value); } -/* }}} */ -/* {{{ proto mixed Fiber::throw(Throwable $exception) */ ZEND_METHOD(Fiber, throw) { zend_fiber *fiber; @@ -646,9 +619,7 @@ ZEND_METHOD(Fiber, throw) RETVAL_COPY_VALUE(&fiber->value); ZVAL_UNDEF(&fiber->value); } -/* }}} */ -/* {{{ proto bool Fiber::isStarted() */ ZEND_METHOD(Fiber, isStarted) { zend_fiber *fiber; @@ -659,9 +630,7 @@ ZEND_METHOD(Fiber, isStarted) RETURN_BOOL(fiber->status != ZEND_FIBER_STATUS_INIT); } -/* }}} */ -/* {{{ proto bool Fiber::isSuspended() */ ZEND_METHOD(Fiber, isSuspended) { zend_fiber *fiber; @@ -672,9 +641,7 @@ ZEND_METHOD(Fiber, isSuspended) RETURN_BOOL(fiber->status == ZEND_FIBER_STATUS_SUSPENDED); } -/* }}} */ -/* {{{ proto bool Fiber::isRunning() */ ZEND_METHOD(Fiber, isRunning) { zend_fiber *fiber; @@ -685,9 +652,7 @@ ZEND_METHOD(Fiber, isRunning) RETURN_BOOL(fiber->status == ZEND_FIBER_STATUS_RUNNING); } -/* }}} */ -/* {{{ proto bool Fiber::isTerminated() */ ZEND_METHOD(Fiber, isTerminated) { zend_fiber *fiber; @@ -698,9 +663,7 @@ ZEND_METHOD(Fiber, isTerminated) RETURN_BOOL(fiber->status & ZEND_FIBER_STATUS_FINISHED); } -/* }}} */ -/* {{{ proto mixed Fiber::getReturn() */ ZEND_METHOD(Fiber, getReturn) { zend_fiber *fiber; @@ -726,9 +689,7 @@ ZEND_METHOD(Fiber, getReturn) RETURN_COPY(&fiber->value); } -/* }}} */ -/* {{{ proto Fiber|null Fiber::this() */ ZEND_METHOD(Fiber, this) { zend_fiber *fiber; @@ -743,9 +704,7 @@ ZEND_METHOD(Fiber, this) RETURN_OBJ_COPY(&fiber->std); } -/* }}} */ -/* {{{ proto FiberError::__construct(string $message) */ ZEND_METHOD(FiberError, __construct) { zend_throw_error( @@ -754,7 +713,6 @@ ZEND_METHOD(FiberError, __construct) ZSTR_VAL(Z_OBJCE_P(getThis())->name) ); } -/* }}} */ void zend_register_fiber_ce(void) From d465f6ae6b6d436f749ff7b9fcb251177bbfb432 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Wed, 21 Apr 2021 15:51:38 -0500 Subject: [PATCH 24/43] No need to recalc size --- Zend/zend_fibers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 0b75a4d29916e..2d280278d8d41 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -163,7 +163,7 @@ static bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size) #ifdef VALGRIND_STACK_REGISTER uintptr_t base = (uintptr_t) stack->pointer; - stack->valgrind = VALGRIND_STACK_REGISTER(base, base + msize - ZEND_FIBER_GUARD_PAGES * page_size); + stack->valgrind = VALGRIND_STACK_REGISTER(base, base + stack->size); #endif return true; From d40b8287ca3d96419d9c1014873a5b4e252e233a Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Wed, 21 Apr 2021 16:16:41 -0500 Subject: [PATCH 25/43] Move observer to zend_observer, add tests in zend_test --- Zend/zend_fibers.c | 25 +------ Zend/zend_fibers.h | 4 -- Zend/zend_observer.c | 19 ++++++ Zend/zend_observer.h | 6 ++ ext/zend_test/test.c | 48 ++++++++++++++ ext/zend_test/tests/observer_fiber_01.phpt | 29 ++++++++ ext/zend_test/tests/observer_fiber_02.phpt | 28 ++++++++ ext/zend_test/tests/observer_fiber_03.phpt | 77 ++++++++++++++++++++++ ext/zend_test/tests/observer_fiber_04.phpt | 51 ++++++++++++++ ext/zend_test/tests/observer_fiber_05.phpt | 50 ++++++++++++++ ext/zend_test/tests/observer_fiber_06.phpt | 34 ++++++++++ 11 files changed, 345 insertions(+), 26 deletions(-) create mode 100644 ext/zend_test/tests/observer_fiber_01.phpt create mode 100644 ext/zend_test/tests/observer_fiber_02.phpt create mode 100644 ext/zend_test/tests/observer_fiber_03.phpt create mode 100644 ext/zend_test/tests/observer_fiber_04.phpt create mode 100644 ext/zend_test/tests/observer_fiber_05.phpt create mode 100644 ext/zend_test/tests/observer_fiber_06.phpt diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 2d280278d8d41..e01eed2ba3b6e 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -24,6 +24,7 @@ #include "zend_interfaces.h" #include "zend_exceptions.h" #include "zend_builtin_functions.h" +#include "zend_observer.h" #include "zend_fibers.h" #include "zend_fibers_arginfo.h" @@ -46,8 +47,6 @@ static zend_object_handlers zend_fiber_handlers; static zend_object *zend_fiber_object_create(zend_class_entry *ce); static void zend_fiber_object_destroy(zend_object *object); -static zend_llist zend_fiber_observers_list; - typedef void *fcontext_t; typedef struct _transfer_t { @@ -88,22 +87,6 @@ extern transfer_t jump_fcontext(fcontext_t to, void *vp); -ZEND_API void zend_observer_fiber_switch_register(zend_observer_fiber_switch_handler handler) -{ - zend_llist_add_element(&zend_fiber_observers_list, &handler); -} - -static zend_always_inline void zend_observer_fiber_switch_notify(zend_fiber *from, zend_fiber *to) -{ - zend_llist_element *element; - zend_observer_fiber_switch_handler callback; - - for (element = zend_fiber_observers_list.head; element; element = element->next) { - callback = *(zend_observer_fiber_switch_handler *) element->data; - callback(from, to); - } -} - static size_t zend_fiber_page_size() { #if _POSIX_MAPPED_FILES @@ -362,6 +345,8 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context) // Reference added while running, removed when suspended, and added again once resumed. GC_ADDREF(&fiber->std); + fiber->status = ZEND_FIBER_STATUS_RUNNING; + zend_call_function(&fiber->fci, &fiber->fci_cache); if (EG(exception)) { @@ -480,8 +465,6 @@ ZEND_METHOD(Fiber, start) RETURN_THROWS(); } - fiber->status = ZEND_FIBER_STATUS_RUNNING; - zend_fiber_switch_to(fiber); zval_ptr_dtor(&fiber->fci.function_name); @@ -736,11 +719,9 @@ void zend_fiber_init(void) EG(fiber_error) = NULL; zend_hash_init(&EG(fibers), 0, NULL, NULL, 0); - zend_llist_init(&zend_fiber_observers_list, sizeof(zend_observer_fiber_switch_handler), NULL, 0); } void zend_fiber_shutdown(void) { zend_hash_destroy(&EG(fibers)); - zend_llist_destroy(&zend_fiber_observers_list); } diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index f70a6521aa7f1..aef8deead5cb1 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -83,10 +83,6 @@ typedef struct _zend_fiber_error { zend_string *message; } zend_fiber_error; -typedef void (*zend_observer_fiber_switch_handler)(zend_fiber *from, zend_fiber *to); - -ZEND_API void zend_observer_fiber_switch_register(zend_observer_fiber_switch_handler handler); - static const zend_uchar ZEND_FIBER_STATUS_INIT = 0x0; static const zend_uchar ZEND_FIBER_STATUS_SUSPENDED = 0x1; static const zend_uchar ZEND_FIBER_STATUS_RUNNING = 0x2; diff --git a/Zend/zend_observer.c b/Zend/zend_observer.c index 2c515c40ede9c..53a28707ecc57 100644 --- a/Zend/zend_observer.c +++ b/Zend/zend_observer.c @@ -40,6 +40,7 @@ typedef struct _zend_observer_fcall_data { zend_llist zend_observers_fcall_list; zend_llist zend_observer_error_callbacks; +zend_llist zend_observer_fiber_switch; int zend_observer_fcall_op_array_extension = -1; @@ -72,6 +73,7 @@ ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init init) { ZEND_API void zend_observer_startup(void) { zend_llist_init(&zend_observers_fcall_list, sizeof(zend_observer_fcall_init), NULL, 1); zend_llist_init(&zend_observer_error_callbacks, sizeof(zend_observer_error_cb), NULL, 1); + zend_llist_init(&zend_observer_fiber_switch, sizeof(zend_observer_fiber_switch_handler), NULL, 1); } ZEND_API void zend_observer_activate(void) { @@ -89,6 +91,7 @@ ZEND_API void zend_observer_deactivate(void) { ZEND_API void zend_observer_shutdown(void) { zend_llist_destroy(&zend_observers_fcall_list); zend_llist_destroy(&zend_observer_error_callbacks); + zend_llist_destroy(&zend_observer_fiber_switch); } static void zend_observer_fcall_install(zend_execute_data *execute_data) { @@ -255,3 +258,19 @@ void zend_observer_error_notify(int type, zend_string *error_filename, uint32_t callback(type, error_filename, error_lineno, message); } } + +ZEND_API void zend_observer_fiber_switch_register(zend_observer_fiber_switch_handler handler) +{ + zend_llist_add_element(&zend_observer_fiber_switch, &handler); +} + +void zend_observer_fiber_switch_notify(zend_fiber *from, zend_fiber *to) +{ + zend_llist_element *element; + zend_observer_fiber_switch_handler callback; + + for (element = zend_observer_fiber_switch.head; element; element = element->next) { + callback = *(zend_observer_fiber_switch_handler *) element->data; + callback(from, to); + } +} diff --git a/Zend/zend_observer.h b/Zend/zend_observer.h index 40f0b384a24d3..6ee73f84a4e2e 100644 --- a/Zend/zend_observer.h +++ b/Zend/zend_observer.h @@ -22,6 +22,7 @@ #include "zend.h" #include "zend_compile.h" +#include "zend_fibers.h" BEGIN_EXTERN_C() @@ -77,6 +78,11 @@ typedef void (*zend_observer_error_cb)(int type, zend_string *error_filename, ui ZEND_API void zend_observer_error_register(zend_observer_error_cb callback); void zend_observer_error_notify(int type, zend_string *error_filename, uint32_t error_lineno, zend_string *message); +typedef void (*zend_observer_fiber_switch_handler)(zend_fiber *from, zend_fiber *to); + +ZEND_API void zend_observer_fiber_switch_register(zend_observer_fiber_switch_handler handler); +void zend_observer_fiber_switch_notify(zend_fiber *from, zend_fiber *to); + END_EXTERN_C() #endif /* ZEND_OBSERVER_H */ diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 6469f6f4a10e5..a744eed48b52a 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -27,6 +27,7 @@ #include "zend_attributes.h" #include "zend_observer.h" #include "zend_smart_str.h" +#include "zend_fibers.h" ZEND_BEGIN_MODULE_GLOBALS(zend_test) int observer_enabled; @@ -40,6 +41,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test) int observer_show_opcode; char *observer_show_opcode_in_user_handler; int observer_nesting_depth; + int observer_fiber_switch; int replace_zend_execute_ex; ZEND_END_MODULE_GLOBALS(zend_test) @@ -348,6 +350,7 @@ PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("zend_test.observer.show_init_backtrace", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_init_backtrace, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.observer.show_opcode", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_opcode, zend_zend_test_globals, zend_test_globals) 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) PHP_INI_END() @@ -395,6 +398,45 @@ static void observer_set_user_opcode_handler(const char *opcode_names, user_opco } } +static void fiber_address_observer(zend_fiber *from, zend_fiber *to) +{ + if (ZT_G(observer_fiber_switch)) { + php_printf("\n", (uintptr_t) from, (uintptr_t) to); + } +} + +static void fiber_enter_observer(zend_fiber *from, zend_fiber *to) +{ + if (ZT_G(observer_fiber_switch)) { + if (to) { + if (to->status == ZEND_FIBER_STATUS_INIT) { + php_printf("\n", (uintptr_t) to); + } else if (to->status == ZEND_FIBER_STATUS_RUNNING && (!from || from->status == ZEND_FIBER_STATUS_RUNNING)) { + php_printf("\n", (uintptr_t) to); + } else if (to->status == ZEND_FIBER_STATUS_SHUTDOWN) { + php_printf("\n", (uintptr_t) to); + } + } + } +} + +static void fiber_suspend_observer(zend_fiber *from, zend_fiber *to) +{ + if (ZT_G(observer_fiber_switch)) { + if (from) { + if (from->status == ZEND_FIBER_STATUS_SUSPENDED) { + php_printf("\n", (uintptr_t) from); + } else if (from->status == ZEND_FIBER_STATUS_RETURNED) { + php_printf("\n", (uintptr_t) from); + } else if (from->status == ZEND_FIBER_STATUS_THREW) { + php_printf("\n", (uintptr_t) from); + } else if (from->status == ZEND_FIBER_STATUS_SHUTDOWN) { + php_printf("\n", (uintptr_t) from); + } + } + } +} + PHP_MINIT_FUNCTION(zend_test) { zend_test_interface = register_class__ZendTestInterface(); @@ -442,6 +484,12 @@ PHP_MINIT_FUNCTION(zend_test) observer_set_user_opcode_handler(ZT_G(observer_show_opcode_in_user_handler), observer_show_opcode_in_user_handler); } + if (ZT_G(observer_enabled)) { + zend_observer_fiber_switch_register(fiber_address_observer); + zend_observer_fiber_switch_register(fiber_enter_observer); + zend_observer_fiber_switch_register(fiber_suspend_observer); + } + return SUCCESS; } diff --git a/ext/zend_test/tests/observer_fiber_01.phpt b/ext/zend_test/tests/observer_fiber_01.phpt new file mode 100644 index 0000000000000..cbbb2d19da6d0 --- /dev/null +++ b/ext/zend_test/tests/observer_fiber_01.phpt @@ -0,0 +1,29 @@ +--TEST-- +Observer: Basic fiber switching +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.fiber_switch=1 +--FILE-- +start(); +$fiber->resume(); + +?> +--EXPECTF-- + + + + + + + + + + diff --git a/ext/zend_test/tests/observer_fiber_02.phpt b/ext/zend_test/tests/observer_fiber_02.phpt new file mode 100644 index 0000000000000..b7992ca629e7b --- /dev/null +++ b/ext/zend_test/tests/observer_fiber_02.phpt @@ -0,0 +1,28 @@ +--TEST-- +Observer: Unfinished fiber +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.fiber_switch=1 +--FILE-- +start(); + +?> +--EXPECTF-- + + + + + + + + + + diff --git a/ext/zend_test/tests/observer_fiber_03.phpt b/ext/zend_test/tests/observer_fiber_03.phpt new file mode 100644 index 0000000000000..af8955ab1d2d5 --- /dev/null +++ b/ext/zend_test/tests/observer_fiber_03.phpt @@ -0,0 +1,77 @@ +--TEST-- +Observer: Nested fibers +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.fiber_switch=1 +--FILE-- +start(); + + Fiber::suspend(); + var_dump(2); + + $fiber->resume(); + + Fiber::suspend(); + var_dump(4); + + $fiber->resume(); +}); + +$fiber->start(); +$fiber->resume(); +$fiber->resume(); +$fiber->resume(); + +?> +--EXPECTF-- + + + + + + + + +int(1) + + + + + + + + + +int(2) + + +int(3) + + + + + + +int(4) + + +int(5) + + + + diff --git a/ext/zend_test/tests/observer_fiber_04.phpt b/ext/zend_test/tests/observer_fiber_04.phpt new file mode 100644 index 0000000000000..222a86d27edf4 --- /dev/null +++ b/ext/zend_test/tests/observer_fiber_04.phpt @@ -0,0 +1,51 @@ +--TEST-- +Observer: Nested fibers with unfinished fiber +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.fiber_switch=1 +--FILE-- +start(); + + Fiber::suspend(); +}); + +$fiber->start(); +$fiber->resume(); +$fiber->resume(); + +?> +--EXPECTF-- + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/zend_test/tests/observer_fiber_05.phpt b/ext/zend_test/tests/observer_fiber_05.phpt new file mode 100644 index 0000000000000..4babd64a4c3fa --- /dev/null +++ b/ext/zend_test/tests/observer_fiber_05.phpt @@ -0,0 +1,50 @@ +--TEST-- +Observer: Nested fibers with both unfinished +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.fiber_switch=1 +--FILE-- +start(); + + Fiber::suspend(); +}); + +$fiber->start(); +$fiber->resume(); + +?> +--EXPECTF-- + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/zend_test/tests/observer_fiber_06.phpt b/ext/zend_test/tests/observer_fiber_06.phpt new file mode 100644 index 0000000000000..e0c61ef69146f --- /dev/null +++ b/ext/zend_test/tests/observer_fiber_06.phpt @@ -0,0 +1,34 @@ +--TEST-- +Observer: Throwing fiber +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.fiber_switch=1 +--FILE-- +start(); + +try { + $fiber->throw(new Exception); +} catch (Exception $exception) { + +} + +?> +--EXPECTF-- + + + + + + + + + + From a0140428a8b4c585d31a14b9ea591fe51329abee Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Wed, 21 Apr 2021 16:32:00 -0500 Subject: [PATCH 26/43] Update arginfo hash after rebase --- ext/reflection/php_reflection_arginfo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 693ba283ae71c..019192aa47876 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6eecf22be201eacd72f57bbc665e77c805162842 */ + * Stub hash: 5d890da977c41b3e230d368e6beb37f08a3d1446 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) From 7dc3d4dfccded863612589b8b2d18e0984561407 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Wed, 21 Apr 2021 16:48:49 -0500 Subject: [PATCH 27/43] Treat address as string in tests --- ext/zend_test/test.c | 16 +++---- ext/zend_test/tests/observer_fiber_01.phpt | 16 +++---- ext/zend_test/tests/observer_fiber_02.phpt | 16 +++---- ext/zend_test/tests/observer_fiber_03.phpt | 56 +++++++++++----------- ext/zend_test/tests/observer_fiber_04.phpt | 40 ++++++++-------- ext/zend_test/tests/observer_fiber_05.phpt | 40 ++++++++-------- ext/zend_test/tests/observer_fiber_06.phpt | 16 +++---- 7 files changed, 100 insertions(+), 100 deletions(-) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index a744eed48b52a..77f6e41eb85fe 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -401,7 +401,7 @@ static void observer_set_user_opcode_handler(const char *opcode_names, user_opco static void fiber_address_observer(zend_fiber *from, zend_fiber *to) { if (ZT_G(observer_fiber_switch)) { - php_printf("\n", (uintptr_t) from, (uintptr_t) to); + php_printf("\n", from, to); } } @@ -410,11 +410,11 @@ static void fiber_enter_observer(zend_fiber *from, zend_fiber *to) if (ZT_G(observer_fiber_switch)) { if (to) { if (to->status == ZEND_FIBER_STATUS_INIT) { - php_printf("\n", (uintptr_t) to); + php_printf("\n", to); } else if (to->status == ZEND_FIBER_STATUS_RUNNING && (!from || from->status == ZEND_FIBER_STATUS_RUNNING)) { - php_printf("\n", (uintptr_t) to); + php_printf("\n", to); } else if (to->status == ZEND_FIBER_STATUS_SHUTDOWN) { - php_printf("\n", (uintptr_t) to); + php_printf("\n", to); } } } @@ -425,13 +425,13 @@ static void fiber_suspend_observer(zend_fiber *from, zend_fiber *to) if (ZT_G(observer_fiber_switch)) { if (from) { if (from->status == ZEND_FIBER_STATUS_SUSPENDED) { - php_printf("\n", (uintptr_t) from); + php_printf("\n", from); } else if (from->status == ZEND_FIBER_STATUS_RETURNED) { - php_printf("\n", (uintptr_t) from); + php_printf("\n", from); } else if (from->status == ZEND_FIBER_STATUS_THREW) { - php_printf("\n", (uintptr_t) from); + php_printf("\n", from); } else if (from->status == ZEND_FIBER_STATUS_SHUTDOWN) { - php_printf("\n", (uintptr_t) from); + php_printf("\n", from); } } } diff --git a/ext/zend_test/tests/observer_fiber_01.phpt b/ext/zend_test/tests/observer_fiber_01.phpt index cbbb2d19da6d0..6b15d814a524b 100644 --- a/ext/zend_test/tests/observer_fiber_01.phpt +++ b/ext/zend_test/tests/observer_fiber_01.phpt @@ -18,12 +18,12 @@ $fiber->resume(); ?> --EXPECTF-- - - + + - - - - - - + + + + + + diff --git a/ext/zend_test/tests/observer_fiber_02.phpt b/ext/zend_test/tests/observer_fiber_02.phpt index b7992ca629e7b..212d9e6ad8bc0 100644 --- a/ext/zend_test/tests/observer_fiber_02.phpt +++ b/ext/zend_test/tests/observer_fiber_02.phpt @@ -17,12 +17,12 @@ $fiber->start(); ?> --EXPECTF-- - - + + - - - - - - + + + + + + diff --git a/ext/zend_test/tests/observer_fiber_03.phpt b/ext/zend_test/tests/observer_fiber_03.phpt index af8955ab1d2d5..4f3de09ae4eee 100644 --- a/ext/zend_test/tests/observer_fiber_03.phpt +++ b/ext/zend_test/tests/observer_fiber_03.phpt @@ -40,38 +40,38 @@ $fiber->resume(); ?> --EXPECTF-- - - + + - - - - + + + + int(1) - - + + - - - - - - + + + + + + int(2) - - + + int(3) - - - - - - + + + + + + int(4) - - + + int(5) - - - - + + + + diff --git a/ext/zend_test/tests/observer_fiber_04.phpt b/ext/zend_test/tests/observer_fiber_04.phpt index 222a86d27edf4..489a95dbc4f14 100644 --- a/ext/zend_test/tests/observer_fiber_04.phpt +++ b/ext/zend_test/tests/observer_fiber_04.phpt @@ -27,25 +27,25 @@ $fiber->resume(); ?> --EXPECTF-- - - + + - - - - - - + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/ext/zend_test/tests/observer_fiber_05.phpt b/ext/zend_test/tests/observer_fiber_05.phpt index 4babd64a4c3fa..f7738cd6d4a4f 100644 --- a/ext/zend_test/tests/observer_fiber_05.phpt +++ b/ext/zend_test/tests/observer_fiber_05.phpt @@ -26,25 +26,25 @@ $fiber->resume(); ?> --EXPECTF-- - - + + - - - - - - + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/ext/zend_test/tests/observer_fiber_06.phpt b/ext/zend_test/tests/observer_fiber_06.phpt index e0c61ef69146f..2b3f2ec1a9640 100644 --- a/ext/zend_test/tests/observer_fiber_06.phpt +++ b/ext/zend_test/tests/observer_fiber_06.phpt @@ -23,12 +23,12 @@ try { ?> --EXPECTF-- - - + + - - - - - - + + + + + + From 79abf312d7dec66a6217635ff11e1187c0deca65 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Thu, 22 Apr 2021 13:20:48 -0500 Subject: [PATCH 28/43] Rename FiberExit to GracefulExit --- Zend/zend_exceptions.c | 20 ++++++++++---------- Zend/zend_exceptions.h | 4 ++-- Zend/zend_fibers.c | 6 ++++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index b49a10b621a1a..1c813866aa817 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -43,11 +43,11 @@ ZEND_API zend_class_entry *zend_ce_arithmetic_error; ZEND_API zend_class_entry *zend_ce_division_by_zero_error; ZEND_API zend_class_entry *zend_ce_unhandled_match_error; -/* Internal pseudo-exception that is not exposed to userland. */ +/* Internal pseudo-exception that is not exposed to userland. Throwing this exception *does not* execute finally blocks. */ static zend_class_entry zend_ce_unwind_exit; -/* Internal pseudo-exception used in destroyed fibers that is not exposed to userland. */ -static zend_class_entry zend_ce_fiber_exit; +/* Internal pseudo-exception that is not exposed to userland. Throwing this exception *does* execute finally blocks. */ +static zend_class_entry zend_ce_graceful_exit; ZEND_API void (*zend_throw_exception_hook)(zend_object *ex); @@ -97,7 +97,7 @@ void zend_exception_set_previous(zend_object *exception, zend_object *add_previo return; } - if (exception == add_previous || zend_is_unwind_exit(add_previous) || zend_is_fiber_exit(add_previous)) { + if (exception == add_previous || zend_is_unwind_exit(add_previous) || zend_is_graceful_exit(add_previous)) { OBJ_RELEASE(add_previous); return; } @@ -795,7 +795,7 @@ void zend_register_default_exception(void) /* {{{ */ INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL); - INIT_CLASS_ENTRY(zend_ce_fiber_exit, "FiberExit", NULL); + INIT_CLASS_ENTRY(zend_ce_graceful_exit, "GracefulExit", NULL); } /* }}} */ @@ -954,7 +954,7 @@ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severit zend_string_release_ex(str, 0); zend_string_release_ex(file, 0); - } else if (ce_exception == &zend_ce_unwind_exit) { + } else if (ce_exception == &zend_ce_unwind_exit || ce_exception == &zend_ce_graceful_exit) { /* We successfully unwound, nothing more to do. * We still return FAILURE in this case, as further execution should still be aborted. */ } else { @@ -992,10 +992,10 @@ ZEND_API ZEND_COLD void zend_throw_unwind_exit(void) EG(current_execute_data)->opline = EG(exception_op); } -ZEND_API ZEND_COLD void zend_throw_fiber_exit(void) +ZEND_API ZEND_COLD void zend_throw_graceful_exit(void) { ZEND_ASSERT(!EG(exception)); - EG(exception) = zend_objects_new(&zend_ce_fiber_exit); + EG(exception) = zend_objects_new(&zend_ce_graceful_exit); EG(opline_before_exception) = EG(current_execute_data)->opline; EG(current_execute_data)->opline = EG(exception_op); } @@ -1005,7 +1005,7 @@ ZEND_API bool zend_is_unwind_exit(const zend_object *ex) return ex->ce == &zend_ce_unwind_exit; } -ZEND_API bool zend_is_fiber_exit(const zend_object *ex) +ZEND_API bool zend_is_graceful_exit(const zend_object *ex) { - return ex->ce == &zend_ce_fiber_exit; + return ex->ce == &zend_ce_graceful_exit; } diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h index 7dbc0a1e9ca3d..f34d220adab85 100644 --- a/Zend/zend_exceptions.h +++ b/Zend/zend_exceptions.h @@ -70,9 +70,9 @@ extern ZEND_API void (*zend_throw_exception_hook)(zend_object *ex); ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int severity); ZEND_API ZEND_COLD void zend_throw_unwind_exit(void); -ZEND_API ZEND_COLD void zend_throw_fiber_exit(void); +ZEND_API ZEND_COLD void zend_throw_graceful_exit(void); ZEND_API bool zend_is_unwind_exit(const zend_object *ex); -ZEND_API bool zend_is_fiber_exit(const zend_object *ex); +ZEND_API bool zend_is_graceful_exit(const zend_object *ex); #include "zend_globals.h" diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index e01eed2ba3b6e..9766fbf262ccb 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -351,7 +351,7 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context) if (EG(exception)) { if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { - if (EXPECTED(zend_is_fiber_exit(EG(exception)))) { + if (EXPECTED(zend_is_graceful_exit(EG(exception)) || zend_is_unwind_exit(EG(exception)))) { zend_clear_exception(); } else { zend_exception_error(EG(exception), E_ERROR); @@ -514,9 +514,11 @@ ZEND_METHOD(Fiber, suspend) if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { // This occurs when the fiber is GC'ed while suspended, do not add a ref. if (EG(fiber_error)) { + // Throw UnwindExit so finally blocks are not executed on fatal error. zend_throw_unwind_exit(); } else { - zend_throw_fiber_exit(); + // Otherwise throw GracefulExit to execute finally blocks. + zend_throw_graceful_exit(); } RETURN_THROWS(); } From 7e7927cf57daf30341da7afcc838bf3709ea7b2f Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Fri, 23 Apr 2021 10:29:55 -0500 Subject: [PATCH 29/43] Drop zend_fiber_cleanup Using dtor_obj handler instead of free_obj fixed the issue this was attempting to handle. --- Zend/zend_fibers.c | 19 ++----------------- Zend/zend_fibers.h | 1 - main/main.c | 36 +++++++++++++++--------------------- 3 files changed, 17 insertions(+), 39 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 9766fbf262ccb..79502767c2cae 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -397,6 +397,7 @@ static void zend_fiber_object_destroy(zend_object *object) zend_object *exception = EG(exception); EG(exception) = NULL; fiber->status = ZEND_FIBER_STATUS_SHUTDOWN; + GC_ADDREF(&fiber->std); zend_fiber_switch_to(fiber); EG(exception) = exception; } @@ -409,23 +410,7 @@ static void zend_fiber_object_destroy(zend_object *object) zend_fiber_destroy_context(&fiber->context); zend_object_std_dtor(&fiber->std); -} - -void zend_fiber_cleanup(void) -{ - zend_fiber *fiber; - zend_object *exception = EG(exception); - EG(exception) = NULL; - - ZEND_HASH_REVERSE_FOREACH_PTR(&EG(fibers), fiber) { - if (fiber->status == ZEND_FIBER_STATUS_SUSPENDED) { - fiber->status = ZEND_FIBER_STATUS_SHUTDOWN; - GC_ADDREF(&fiber->std); - zend_fiber_switch_to(fiber); - } - } ZEND_HASH_FOREACH_END(); - EG(exception) = exception; } ZEND_METHOD(Fiber, __construct) @@ -708,7 +693,7 @@ void zend_register_fiber_ce(void) zend_ce_fiber->unserialize = zend_class_unserialize_deny; zend_fiber_handlers = std_object_handlers; - zend_fiber_handlers.free_obj = zend_fiber_object_destroy; + zend_fiber_handlers.dtor_obj = zend_fiber_object_destroy; zend_fiber_handlers.clone_obj = NULL; zend_ce_fiber_error = register_class_FiberError(zend_ce_error); diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index aef8deead5cb1..6cdba6270f7f1 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -27,7 +27,6 @@ BEGIN_EXTERN_C() void zend_register_fiber_ce(void); void zend_fiber_init(void); -void zend_fiber_cleanup(void); void zend_fiber_shutdown(void); extern ZEND_API zend_class_entry *zend_ce_fiber; diff --git a/main/main.c b/main/main.c index 78c0f258c12b0..7627ad5cd99ea 100644 --- a/main/main.c +++ b/main/main.c @@ -73,7 +73,6 @@ #include "zend_dtrace.h" #include "zend_observer.h" #include "zend_system_id.h" -#include "zend_fibers.h" #include "php_content_types.h" #include "php_ticks.h" @@ -1773,17 +1772,12 @@ void php_request_shutdown(void *dummy) php_call_shutdown_functions(); } - /* 2. Cleanup all active fibers. */ - zend_try { - zend_fiber_cleanup(); - } zend_end_try(); - - /* 3. Call all possible __destruct() functions */ + /* 2. Call all possible __destruct() functions */ zend_try { zend_call_destructors(); } zend_end_try(); - /* 4. Flush all output buffers */ + /* 3. Flush all output buffers */ zend_try { bool send_buffer = SG(request_info).headers_only ? 0 : 1; @@ -1800,27 +1794,27 @@ void php_request_shutdown(void *dummy) } } zend_end_try(); - /* 5. Reset max_execution_time (no longer executing php code after response sent) */ + /* 4. Reset max_execution_time (no longer executing php code after response sent) */ zend_try { zend_unset_timeout(); } zend_end_try(); - /* 6. Call all extensions RSHUTDOWN functions */ + /* 5. Call all extensions RSHUTDOWN functions */ if (PG(modules_activated)) { zend_deactivate_modules(); } - /* 7. Shutdown output layer (send the set HTTP headers, cleanup output handlers, etc.) */ + /* 6. Shutdown output layer (send the set HTTP headers, cleanup output handlers, etc.) */ zend_try { php_output_deactivate(); } zend_end_try(); - /* 8. Free shutdown functions */ + /* 7. Free shutdown functions */ if (PG(modules_activated)) { php_free_shutdown_functions(); } - /* 9. Destroy super-globals */ + /* 8. Destroy super-globals */ zend_try { int i; @@ -1829,38 +1823,38 @@ void php_request_shutdown(void *dummy) } } zend_end_try(); - /* 10. Shutdown scanner/executor/compiler and restore ini entries */ + /* 9. Shutdown scanner/executor/compiler and restore ini entries */ zend_deactivate(); - /* 11. free request-bound globals */ + /* 10. free request-bound globals */ php_free_request_globals(); - /* 12. Call all extensions post-RSHUTDOWN functions */ + /* 11. Call all extensions post-RSHUTDOWN functions */ zend_try { zend_post_deactivate_modules(); } zend_end_try(); - /* 13. SAPI related shutdown (free stuff) */ + /* 12. SAPI related shutdown (free stuff) */ zend_try { sapi_deactivate(); } zend_end_try(); - /* 14. free virtual CWD memory */ + /* 13. free virtual CWD memory */ virtual_cwd_deactivate(); - /* 15. Destroy stream hashes */ + /* 14. Destroy stream hashes */ zend_try { php_shutdown_stream_hashes(); } zend_end_try(); - /* 16. Free Willy (here be crashes) */ + /* 15. Free Willy (here be crashes) */ zend_arena_destroy(CG(arena)); zend_interned_strings_deactivate(); zend_try { shutdown_memory_manager(CG(unclean_shutdown) || !report_memleaks, 0); } zend_end_try(); - /* 17. Deactivate Zend signals */ + /* 16. Deactivate Zend signals */ #ifdef ZEND_SIGNALS zend_signal_deactivate(); #endif From c2b33d8ca081c250f575da270c84062b15d02b0a Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Fri, 23 Apr 2021 10:44:09 -0500 Subject: [PATCH 30/43] Separate dtor and free --- Zend/zend_fibers.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 79502767c2cae..d8301f6c8c43e 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -389,28 +389,32 @@ static void zend_fiber_object_destroy(zend_object *object) { zend_fiber *fiber = (zend_fiber *) object; - if (UNEXPECTED(fiber->status == ZEND_FIBER_STATUS_INIT)) { + if (fiber->status == ZEND_FIBER_STATUS_SUSPENDED) { + zend_object *exception = EG(exception); + EG(exception) = NULL; + fiber->status = ZEND_FIBER_STATUS_SHUTDOWN; + GC_ADDREF(&fiber->std); + zend_fiber_switch_to(fiber); + EG(exception) = exception; + } +} + +static void zend_fiber_object_free(zend_object *object) +{ + zend_fiber *fiber = (zend_fiber *) object; + + if (fiber->status == ZEND_FIBER_STATUS_INIT) { // Fiber was never started, so we need to release the reference to the callback. zval_ptr_dtor(&fiber->fci.function_name); - } else { - if (fiber->status == ZEND_FIBER_STATUS_SUSPENDED) { - zend_object *exception = EG(exception); - EG(exception) = NULL; - fiber->status = ZEND_FIBER_STATUS_SHUTDOWN; - GC_ADDREF(&fiber->std); - zend_fiber_switch_to(fiber); - EG(exception) = exception; - } - - zval_ptr_dtor(&fiber->value); } + zval_ptr_dtor(&fiber->value); + zend_hash_index_del(&EG(fibers), (uintptr_t) fiber); zend_fiber_destroy_context(&fiber->context); zend_object_std_dtor(&fiber->std); - } ZEND_METHOD(Fiber, __construct) @@ -694,6 +698,7 @@ void zend_register_fiber_ce(void) zend_fiber_handlers = std_object_handlers; zend_fiber_handlers.dtor_obj = zend_fiber_object_destroy; + zend_fiber_handlers.free_obj = zend_fiber_object_free; zend_fiber_handlers.clone_obj = NULL; zend_ce_fiber_error = register_class_FiberError(zend_ce_error); From 437010d577e5e7370dd759aef15f41c551868a52 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Fri, 23 Apr 2021 11:03:13 -0500 Subject: [PATCH 31/43] Feedback changes --- Zend/zend_fibers.c | 4 +++- Zend/zend_fibers.stub.php | 6 ++---- Zend/zend_fibers_arginfo.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index d8301f6c8c43e..b94b1e546f712 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -450,7 +450,7 @@ ZEND_METHOD(Fiber, start) fiber->fci.named_params = named_params; if (!zend_fiber_init_context(&fiber->context, zend_fiber_execute, EG(fiber_stack_size))) { - zend_throw_error(NULL, "Could not create fiber context"); + zend_throw_exception(NULL, "Could not create fiber context", 0); RETURN_THROWS(); } @@ -486,6 +486,8 @@ ZEND_METHOD(Fiber, suspend) RETURN_THROWS(); } + ZEND_ASSERT(fiber->status == ZEND_FIBER_STATUS_RUNNING); + if (value) { ZVAL_COPY(&fiber->value, value); } else { diff --git a/Zend/zend_fibers.stub.php b/Zend/zend_fibers.stub.php index 1ffad6ebceef4..aa22362efb354 100644 --- a/Zend/zend_fibers.stub.php +++ b/Zend/zend_fibers.stub.php @@ -1,10 +1,8 @@ Date: Fri, 23 Apr 2021 11:13:21 -0500 Subject: [PATCH 32/43] Remove active fiber table --- Zend/zend_execute_API.c | 2 -- Zend/zend_fibers.c | 11 ----------- Zend/zend_fibers.h | 1 - Zend/zend_globals.h | 3 --- 4 files changed, 17 deletions(-) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 7fd8624af94d7..d73ed0ea9eb3c 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -375,8 +375,6 @@ void shutdown_executor(void) /* {{{ */ zend_objects_store_free_object_storage(&EG(objects_store), fast_shutdown); - zend_fiber_shutdown(); - zend_weakrefs_shutdown(); zend_try { diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index b94b1e546f712..eaca06e535126 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -380,8 +380,6 @@ static zend_object *zend_fiber_object_create(zend_class_entry *ce) zend_object_std_init(&fiber->std, ce); fiber->std.handlers = &zend_fiber_handlers; - zend_hash_index_add_ptr(&EG(fibers), (uintptr_t) fiber, fiber); - return &fiber->std; } @@ -410,8 +408,6 @@ static void zend_fiber_object_free(zend_object *object) zval_ptr_dtor(&fiber->value); - zend_hash_index_del(&EG(fibers), (uintptr_t) fiber); - zend_fiber_destroy_context(&fiber->context); zend_object_std_dtor(&fiber->std); @@ -711,11 +707,4 @@ void zend_fiber_init(void) { EG(current_fiber) = NULL; EG(fiber_error) = NULL; - - zend_hash_init(&EG(fibers), 0, NULL, NULL, 0); -} - -void zend_fiber_shutdown(void) -{ - zend_hash_destroy(&EG(fibers)); } diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index 6cdba6270f7f1..b9a12f6492bb4 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -27,7 +27,6 @@ BEGIN_EXTERN_C() void zend_register_fiber_ce(void); void zend_fiber_init(void); -void zend_fiber_shutdown(void); extern ZEND_API zend_class_entry *zend_ce_fiber; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index d5c33a78763fd..cd1e7b57c04c0 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -260,9 +260,6 @@ struct _zend_executor_globals { /* Pointer to fatal error that occurred in a fiber while switching to {main}. */ zend_fiber_error *fiber_error; - /* Currently executing fibers. */ - HashTable fibers; - void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; From 28559100152c50a6e36a5b42f73ceda8676620f3 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Fri, 23 Apr 2021 11:22:49 -0500 Subject: [PATCH 33/43] Update for error handler changes after rebase --- Zend/zend_fibers.c | 2 +- Zend/zend_fibers.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index eaca06e535126..178dfc110b6fd 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -287,7 +287,7 @@ static void zend_fiber_switch_to(zend_fiber *fiber) } ZEND_COLD void zend_error_suspend_fiber( - int orig_type, const char *error_filename, uint32_t error_lineno, zend_string *message) + int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message) { ZEND_ASSERT(EG(current_fiber) && "Must be within an active fiber!"); diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index b9a12f6492bb4..5dc8877e1601c 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -76,7 +76,7 @@ typedef struct _zend_fiber { typedef struct _zend_fiber_error { int type; - const char *filename; + zend_string *filename; uint32_t lineno; zend_string *message; } zend_fiber_error; @@ -94,7 +94,7 @@ ZEND_API zend_bool zend_fiber_init_context(zend_fiber_context *context, zend_fib ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context); ZEND_COLD void zend_error_suspend_fiber( - int orig_type, const char *error_filename, uint32_t error_lineno, zend_string *message); + int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message); ZEND_API void zend_fiber_switch_context(zend_fiber_context *to); ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current); From 18580148b1abbfaeaaffe04ceae2bdf371b1e281 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Fri, 23 Apr 2021 11:33:56 -0500 Subject: [PATCH 34/43] Retain reference to callable while running --- Zend/tests/fibers/invocable-class.phpt | 27 ++++++++++++++++++++++++++ Zend/zend_fibers.c | 8 ++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/fibers/invocable-class.phpt diff --git a/Zend/tests/fibers/invocable-class.phpt b/Zend/tests/fibers/invocable-class.phpt new file mode 100644 index 0000000000000..1ab4a4a94d874 --- /dev/null +++ b/Zend/tests/fibers/invocable-class.phpt @@ -0,0 +1,27 @@ +--TEST-- +Reference to invocable class retained while running +--FILE-- +start(); + +?> +--EXPECT-- +object(Test)#1 (0) { +} +object(Test)#1 (0) { +} diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 178dfc110b6fd..1192fb60cdb78 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -349,6 +349,8 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context) zend_call_function(&fiber->fci, &fiber->fci_cache); + zval_ptr_dtor(&fiber->fci.function_name); + if (EG(exception)) { if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { if (EXPECTED(zend_is_graceful_exit(EG(exception)) || zend_is_unwind_exit(EG(exception)))) { @@ -390,9 +392,13 @@ static void zend_fiber_object_destroy(zend_object *object) if (fiber->status == ZEND_FIBER_STATUS_SUSPENDED) { zend_object *exception = EG(exception); EG(exception) = NULL; + fiber->status = ZEND_FIBER_STATUS_SHUTDOWN; + // Additional reference needed while running, removed in zend_fiber_execute. GC_ADDREF(&fiber->std); + zend_fiber_switch_to(fiber); + EG(exception) = exception; } } @@ -452,8 +458,6 @@ ZEND_METHOD(Fiber, start) zend_fiber_switch_to(fiber); - zval_ptr_dtor(&fiber->fci.function_name); - if (fiber->status & ZEND_FIBER_STATUS_FINISHED) { RETURN_NULL(); } From c6ff7102056ba566b2eef2ff24d80441e23f7783 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Fri, 23 Apr 2021 11:50:28 -0500 Subject: [PATCH 35/43] Add test for error_reporting in fiber after change --- Zend/tests/fibers/error-reporting.phpt | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Zend/tests/fibers/error-reporting.phpt diff --git a/Zend/tests/fibers/error-reporting.phpt b/Zend/tests/fibers/error-reporting.phpt new file mode 100644 index 0000000000000..099110d4ed2d5 --- /dev/null +++ b/Zend/tests/fibers/error-reporting.phpt @@ -0,0 +1,26 @@ +--TEST-- +Error reporting change reflected inside fiber +--FILE-- +start(); + +trigger_error("Notice B", E_USER_NOTICE); // Should be silenced. + +$fiber->resume(); + +trigger_error("Warning B", E_USER_WARNING); + +?> +--EXPECTF-- +Warning: Warning A in %serror-reporting.php on line %d + +Warning: Warning B in %serror-reporting.php on line %d From 0cc4b26ea5b83317242f1570bed79686cd54df17 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Fri, 23 Apr 2021 12:20:26 -0500 Subject: [PATCH 36/43] Update ReflectionFiber Removed is* methods available on Fiber and added getCallable. --- ext/reflection/php_reflection.c | 52 ++--------- ext/reflection/php_reflection.stub.php | 10 +-- ext/reflection/php_reflection_arginfo.h | 23 ++--- .../tests/ReflectionFiber_basic.phpt | 89 +++++++++++-------- .../tests/ReflectionFiber_errors.phpt | 7 ++ 5 files changed, 74 insertions(+), 107 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index cffc5b5401789..2679374750659 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6772,23 +6772,20 @@ ZEND_METHOD(ReflectionFiber, __construct) } /* }}} */ -/* {{{ proto Fiber ReflectionFiber::getFiber() */ ZEND_METHOD(ReflectionFiber, getFiber) { ZEND_PARSE_PARAMETERS_NONE(); RETURN_OBJ_COPY(Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj)); } -/* }}} */ #define REFLECTION_CHECK_VALID_FIBER(fiber) do { \ if (fiber == NULL || fiber->status == ZEND_FIBER_STATUS_INIT || fiber->status & ZEND_FIBER_STATUS_FINISHED) { \ zend_throw_error(NULL, "Cannot fetch information from a fiber that has not been started or is terminated"); \ - return; \ + RETURN_THROWS(); \ } \ } while (0) -/* {{{ proto array ReflectionFiber::getTrace(int $options) */ ZEND_METHOD(ReflectionFiber, getTrace) { zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); @@ -6810,9 +6807,7 @@ ZEND_METHOD(ReflectionFiber, getTrace) EG(current_execute_data) = execute_data; // Restore original execute data. } -/* }}} */ -/* {{{ proto int ReflectionFiber::getExecutingLine() */ ZEND_METHOD(ReflectionFiber, getExecutingLine) { zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); @@ -6830,9 +6825,7 @@ ZEND_METHOD(ReflectionFiber, getExecutingLine) RETURN_LONG(prev_execute_data->opline->lineno); } -/* }}} */ -/* {{{ proto string ReflectionFiber::getExecutingFile() */ ZEND_METHOD(ReflectionFiber, getExecutingFile) { zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); @@ -6850,51 +6843,20 @@ ZEND_METHOD(ReflectionFiber, getExecutingFile) RETURN_STR_COPY(prev_execute_data->func->op_array.filename); } -/* }}} */ - -/* {{{ proto bool ReflectionFiber::isStarted() */ -ZEND_METHOD(ReflectionFiber, isStarted) -{ - zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); - - ZEND_PARSE_PARAMETERS_NONE(); - - RETURN_BOOL(fiber->status != ZEND_FIBER_STATUS_INIT); -} -/* }}} */ -/* {{{ proto bool ReflectionFiber::isSuspended() */ -ZEND_METHOD(ReflectionFiber, isSuspended) +ZEND_METHOD(ReflectionFiber, getCallable) { zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); ZEND_PARSE_PARAMETERS_NONE(); - RETURN_BOOL(fiber->status == ZEND_FIBER_STATUS_SUSPENDED); -} -/* }}} */ - -/* {{{ proto bool ReflectionFiber::isRunning() */ -ZEND_METHOD(ReflectionFiber, isRunning) -{ - zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); - - ZEND_PARSE_PARAMETERS_NONE(); - - RETURN_BOOL(fiber->status == ZEND_FIBER_STATUS_RUNNING); -} -/* }}} */ - -/* {{{ proto bool ReflectionFiber::isTerminated() */ -ZEND_METHOD(ReflectionFiber, isTerminated) -{ - zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj); - - ZEND_PARSE_PARAMETERS_NONE(); + if (fiber == NULL || fiber->status & ZEND_FIBER_STATUS_FINISHED) { + zend_throw_error(NULL, "Cannot fetch the callable from a fiber that has terminated"); \ + RETURN_THROWS(); + } - RETURN_BOOL(fiber->status & ZEND_FIBER_STATUS_FINISHED); + RETURN_COPY(&fiber->fci.function_name); } -/* }}} */ /* {{{ _reflection_write_property */ static zval *_reflection_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 18ca770872899..fe8def14b72b2 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -731,13 +731,7 @@ public function getExecutingFile(): string {} public function getExecutingLine(): int {} - public function getTrace(int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT): array {} - - public function isStarted(): bool {} - - public function isSuspended(): bool {} + public function getCallable(): callable {} - public function isRunning(): bool {} - - public function isTerminated(): bool {} + public function getTrace(int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT): array {} } diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 019192aa47876..a6d6bfdd726cf 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 5d890da977c41b3e230d368e6beb37f08a3d1446 */ + * Stub hash: 388312e928b54992da6b7e0e0f15dec72d9290f1 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -534,18 +534,13 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionFiber_getExecutingLine arginfo_class_ReflectionAttribute_getTarget +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFiber_getCallable, 0, 0, IS_CALLABLE, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFiber_getTrace, 0, 0, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "DEBUG_BACKTRACE_PROVIDE_OBJECT") ZEND_END_ARG_INFO() -#define arginfo_class_ReflectionFiber_isStarted arginfo_class_ReflectionClass_isEnum - -#define arginfo_class_ReflectionFiber_isSuspended arginfo_class_ReflectionClass_isEnum - -#define arginfo_class_ReflectionFiber_isRunning arginfo_class_ReflectionClass_isEnum - -#define arginfo_class_ReflectionFiber_isTerminated arginfo_class_ReflectionClass_isEnum - ZEND_METHOD(Reflection, getModifierNames); ZEND_METHOD(ReflectionClass, __clone); @@ -763,11 +758,8 @@ ZEND_METHOD(ReflectionFiber, __construct); ZEND_METHOD(ReflectionFiber, getFiber); ZEND_METHOD(ReflectionFiber, getExecutingFile); ZEND_METHOD(ReflectionFiber, getExecutingLine); +ZEND_METHOD(ReflectionFiber, getCallable); ZEND_METHOD(ReflectionFiber, getTrace); -ZEND_METHOD(ReflectionFiber, isStarted); -ZEND_METHOD(ReflectionFiber, isSuspended); -ZEND_METHOD(ReflectionFiber, isRunning); -ZEND_METHOD(ReflectionFiber, isTerminated); static const zend_function_entry class_ReflectionException_methods[] = { @@ -1107,11 +1099,8 @@ static const zend_function_entry class_ReflectionFiber_methods[] = { ZEND_ME(ReflectionFiber, getFiber, arginfo_class_ReflectionFiber_getFiber, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFiber, getExecutingFile, arginfo_class_ReflectionFiber_getExecutingFile, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFiber, getExecutingLine, arginfo_class_ReflectionFiber_getExecutingLine, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFiber, getCallable, arginfo_class_ReflectionFiber_getCallable, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFiber, getTrace, arginfo_class_ReflectionFiber_getTrace, ZEND_ACC_PUBLIC) - ZEND_ME(ReflectionFiber, isStarted, arginfo_class_ReflectionFiber_isStarted, ZEND_ACC_PUBLIC) - ZEND_ME(ReflectionFiber, isSuspended, arginfo_class_ReflectionFiber_isSuspended, ZEND_ACC_PUBLIC) - ZEND_ME(ReflectionFiber, isRunning, arginfo_class_ReflectionFiber_isRunning, ZEND_ACC_PUBLIC) - ZEND_ME(ReflectionFiber, isTerminated, arginfo_class_ReflectionFiber_isTerminated, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/ext/reflection/tests/ReflectionFiber_basic.phpt b/ext/reflection/tests/ReflectionFiber_basic.phpt index 6d75f915c9acf..cd332fad7ebc6 100644 --- a/ext/reflection/tests/ReflectionFiber_basic.phpt +++ b/ext/reflection/tests/ReflectionFiber_basic.phpt @@ -3,73 +3,86 @@ ReflectionFiber basic tests --FILE-- isStarted()); - var_dump($fiber->isRunning()); - var_dump($fiber->isSuspended()); - var_dump($fiber->isTerminated()); + var_dump($reflection->getExecutingFile()); + var_dump($reflection->getExecutingLine()); + var_dump($reflection->getTrace()); Fiber::suspend(); -}); +}; + +$fiber = new Fiber($callable); $reflection = new ReflectionFiber($fiber); echo "Before Start:\n"; var_dump($fiber === $reflection->getFiber()); -var_dump($reflection->isStarted()); -var_dump($reflection->isRunning()); -var_dump($reflection->isSuspended()); -var_dump($reflection->isTerminated()); +var_dump($callable === $reflection->getCallable()); $fiber->start(); echo "\nAfter Start:\n"; -var_dump($reflection->isStarted()); -var_dump($reflection->isRunning()); -var_dump($reflection->isSuspended()); -var_dump($reflection->isTerminated()); var_dump($reflection->getExecutingFile()); var_dump($reflection->getExecutingLine()); +var_dump($callable === $reflection->getCallable()); var_dump($reflection->getTrace()); $fiber->resume(); echo "\nAfter Resume:\n"; -var_dump($fiber->isStarted()); -var_dump($fiber->isRunning()); -var_dump($fiber->isSuspended()); -var_dump($fiber->isTerminated()); +$reflection->getTrace(); ?> --EXPECTF-- Before Start: bool(true) -bool(false) -bool(false) -bool(false) -bool(false) +bool(true) Within Fiber: -bool(true) -bool(true) -bool(false) -bool(false) +string(%d) "%sReflectionFiber_basic.php" +int(7) +array(2) { + [0]=> + array(7) { + ["file"]=> + string(%d) "%sReflectionFiber_basic.php" + ["line"]=> + int(8) + ["function"]=> + string(8) "getTrace" + ["class"]=> + string(15) "ReflectionFiber" + ["object"]=> + object(ReflectionFiber)#4 (0) { + } + ["type"]=> + string(2) "->" + ["args"]=> + array(0) { + } + } + [1]=> + array(2) { + ["function"]=> + string(9) "{closure}" + ["args"]=> + array(0) { + } + } +} After Start: -bool(true) -bool(false) -bool(true) -bool(false) string(%d) "%sReflectionFiber_basic.php" -int(10) +int(9) +bool(true) array(2) { [0]=> array(6) { ["file"]=> string(%d) "%sReflectionFiber_basic.php" ["line"]=> - int(10) + int(9) ["function"]=> string(7) "suspend" ["class"]=> @@ -91,7 +104,9 @@ array(2) { } After Resume: -bool(true) -bool(false) -bool(false) -bool(true) + +Fatal error: Uncaught Error: Cannot fetch information from a fiber that has not been started or is terminated in %sReflectionFiber_basic.php:%d +Stack trace: +#0 %sReflectionFiber_basic.php(%d): ReflectionFiber->getTrace() +#1 {main} + thrown in %sReflectionFiber_basic.php on line %d diff --git a/ext/reflection/tests/ReflectionFiber_errors.phpt b/ext/reflection/tests/ReflectionFiber_errors.phpt index 812fa5483c426..640cdcb3bc898 100644 --- a/ext/reflection/tests/ReflectionFiber_errors.phpt +++ b/ext/reflection/tests/ReflectionFiber_errors.phpt @@ -52,6 +52,12 @@ try { echo $error->getMessage(), "\n"; } +try { + $reflection->getCallable(); +} catch (Error $error) { + echo $error->getMessage(), "\n"; +} + ?> --EXPECTF-- Cannot fetch information from a fiber that has not been started or is terminated @@ -62,3 +68,4 @@ int(4) Cannot fetch information from a fiber that has not been started or is terminated Cannot fetch information from a fiber that has not been started or is terminated Cannot fetch information from a fiber that has not been started or is terminated +Cannot fetch the callable from a fiber that has terminated From 9304138272ad5c7bd68af8e385e2a3ab98317853 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Fri, 23 Apr 2021 12:25:46 -0500 Subject: [PATCH 37/43] Fix test due to changed destruct order --- ext/zend_test/tests/observer_fiber_05.phpt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/zend_test/tests/observer_fiber_05.phpt b/ext/zend_test/tests/observer_fiber_05.phpt index f7738cd6d4a4f..574efb36aff1d 100644 --- a/ext/zend_test/tests/observer_fiber_05.phpt +++ b/ext/zend_test/tests/observer_fiber_05.phpt @@ -42,9 +42,11 @@ $fiber->resume(); - + + - + + From 32dc90b0d9bda33c686bff0ad4d4b8f0f1941012 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Sat, 24 Apr 2021 11:12:45 -0500 Subject: [PATCH 38/43] Remove running references The extra reference while running is not necessary with the current API. --- Zend/zend_fibers.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 1192fb60cdb78..69af0ff6e6b4e 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -342,9 +342,6 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context) fiber->fci.retval = &fiber->value; - // Reference added while running, removed when suspended, and added again once resumed. - GC_ADDREF(&fiber->std); - fiber->status = ZEND_FIBER_STATUS_RUNNING; zend_call_function(&fiber->fci, &fiber->fci_cache); @@ -367,9 +364,6 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context) zend_vm_stack_destroy(); fiber->execute_data = NULL; - - // Remove reference added at last resume. - GC_DELREF(&fiber->std); } static zend_object *zend_fiber_object_create(zend_class_entry *ce) @@ -394,8 +388,6 @@ static void zend_fiber_object_destroy(zend_object *object) EG(exception) = NULL; fiber->status = ZEND_FIBER_STATUS_SHUTDOWN; - // Additional reference needed while running, removed in zend_fiber_execute. - GC_ADDREF(&fiber->std); zend_fiber_switch_to(fiber); @@ -497,9 +489,6 @@ ZEND_METHOD(Fiber, suspend) fiber->execute_data = execute_data; fiber->status = ZEND_FIBER_STATUS_SUSPENDED; - // Remove running reference while suspended. - GC_DELREF(&fiber->std); - zend_fiber_suspend(fiber); if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { @@ -515,8 +504,6 @@ ZEND_METHOD(Fiber, suspend) } fiber->status = ZEND_FIBER_STATUS_RUNNING; - // Add reference while fiber is running. - GC_ADDREF(&fiber->std); if (fiber->exception) { exception = fiber->exception; From e7588b72138ff8caf74ed9e5f1edfff238b64975 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Sun, 25 Apr 2021 10:31:42 -0500 Subject: [PATCH 39/43] Add s390x support --- Zend/asm/jump_s390x_sysv_elf_gas.S | 156 +++++++++++++++++++++++++++++ Zend/asm/make_s390x_sysv_elf_gas.S | 108 ++++++++++++++++++++ configure.ac | 2 + 3 files changed, 266 insertions(+) create mode 100644 Zend/asm/jump_s390x_sysv_elf_gas.S create mode 100644 Zend/asm/make_s390x_sysv_elf_gas.S diff --git a/Zend/asm/jump_s390x_sysv_elf_gas.S b/Zend/asm/jump_s390x_sysv_elf_gas.S new file mode 100644 index 0000000000000..c2a578b2663eb --- /dev/null +++ b/Zend/asm/jump_s390x_sysv_elf_gas.S @@ -0,0 +1,156 @@ +/******************************************************* + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | t.fctx | t.data | r2 | r6 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | r7 | r8 | r9 | r10 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | r11 | r12 | r13 | r14 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 104 | 112 | 120 | * + * ------------------------------------------------- * + * | f8 | f9 | f10 | f11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 136 | 144 | 152 | * + * ------------------------------------------------- * + * | f12 | f13 | f14 | f15 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 168 | 176 | | * + * ------------------------------------------------- * + * | fpc | pc | | | * + * ------------------------------------------------- * + *******************************************************/ + +.text +.align 8 +.global jump_fcontext +.type jump_fcontext, @function + +#define ARG_OFFSET 0 +#define GR_OFFSET 16 +#define FP_OFFSET 96 +#define FPC_OFFSET 160 +#define PC_OFFSET 168 +#define CONTEXT_SIZE 176 + +#define REG_SAVE_AREA_SIZE 160 + +/* + +typedef void* fcontext_t; + +struct transfer_t { + fcontext_t fctx; + void * data; +}; + +transfer_t jump_fcontext( fcontext_t const to, + void * data); + +Incoming args +r2 - Hidden argument to the location where the return transfer_t needs to be returned +r3 - Context we want to switch to +r4 - Data pointer + +*/ + +jump_fcontext: + .machine "z10" + /* Reserve stack space to store the current context. */ + aghi %r15,-CONTEXT_SIZE + + /* Save the argument register holding the location of the return value. */ + stg %r2,GR_OFFSET(%r15) + + /* Save the call-saved general purpose registers. */ + stmg %r6,%r14,GR_OFFSET+8(%r15) + + /* Save call-saved floating point registers. */ + std %f8,FP_OFFSET(%r15) + std %f9,FP_OFFSET+8(%r15) + std %f10,FP_OFFSET+16(%r15) + std %f11,FP_OFFSET+24(%r15) + std %f12,FP_OFFSET+32(%r15) + std %f13,FP_OFFSET+40(%r15) + std %f14,FP_OFFSET+48(%r15) + std %f15,FP_OFFSET+56(%r15) + + /* Save the return address as current pc. */ + stg %r14,PC_OFFSET(%r15) + + /* Save the floating point control register. */ + stfpc FPC_OFFSET(%r15) + + /* Backup the stack pointer pointing to the old context-data into r1. */ + lgr %r1,%r15 + + /* Load the new context pointer as stack pointer. */ + lgr %r15,%r3 + + /* Restore the call-saved GPRs from the new context. */ + lmg %r6,%r14,GR_OFFSET+8(%r15) + + /* Restore call-saved floating point registers. */ + ld %f8,FP_OFFSET(%r15) + ld %f9,FP_OFFSET+8(%r15) + ld %f10,FP_OFFSET+16(%r15) + ld %f11,FP_OFFSET+24(%r15) + ld %f12,FP_OFFSET+32(%r15) + ld %f13,FP_OFFSET+40(%r15) + ld %f14,FP_OFFSET+48(%r15) + ld %f15,FP_OFFSET+56(%r15) + + /* Load the floating point control register. */ + lfpc FPC_OFFSET(%r15) + + /* Restore PC - the location where we will jump to at the end. */ + lg %r5,PC_OFFSET(%r15) + + ltg %r2,GR_OFFSET(%r15) + jnz use_return_slot + + /* We restore a make_fcontext context. Use the function + argument slot in the context we just saved and allocate the + register save area for the target function. */ + la %r2,ARG_OFFSET(%r1) + aghi %r15,-REG_SAVE_AREA_SIZE + +use_return_slot: + /* Save the two fields in transfer_t. When calling a + make_fcontext function this becomes the function argument of + the target function, otherwise it will be the return value of + jump_fcontext. */ + stg %r1,0(%r2) + stg %r4,8(%r2) + + /* Free the restored context. */ + aghi %r15,CONTEXT_SIZE + + /* Jump to the PC loaded from the new context. */ + br %r5 + + +.size jump_fcontext,.-jump_fcontext +.section .note.GNU-stack,"",%progbits diff --git a/Zend/asm/make_s390x_sysv_elf_gas.S b/Zend/asm/make_s390x_sysv_elf_gas.S new file mode 100644 index 0000000000000..e7e2d5f6e0c9f --- /dev/null +++ b/Zend/asm/make_s390x_sysv_elf_gas.S @@ -0,0 +1,108 @@ +/******************************************************* + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | t.fctx | t.data | r2 | r6 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | r7 | r8 | r9 | r10 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | r11 | r12 | r13 | r14 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 104 | 112 | 120 | * + * ------------------------------------------------- * + * | f8 | f9 | f10 | f11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 136 | 144 | 152 | * + * ------------------------------------------------- * + * | f12 | f13 | f14 | f15 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 168 | 176 | | * + * ------------------------------------------------- * + * | fpc | pc | | | * + * ------------------------------------------------- * + *******************************************************/ + +.text +.align 8 +.global make_fcontext +.type make_fcontext, @function + +#define ARG_OFFSET 0 +#define GR_OFFSET 16 +#define R14_OFFSET 88 +#define FP_OFFSET 96 +#define FPC_OFFSET 160 +#define PC_OFFSET 168 +#define CONTEXT_SIZE 176 + +/* + +fcontext_t make_fcontext( void * sp, std::size_t size, void (* fn)( transfer_t) ); + +Create and return a context below SP to call FN. + +Incoming args +r2 - The stack location where to create the context +r3 - The size of the context +r4 - The address of the context function + +*/ + +make_fcontext: + .machine "z10" + /* Align the stack to an 8 byte boundary. */ + nill %r2,0xfff0 + + /* Allocate stack space for the context. */ + aghi %r2,-CONTEXT_SIZE + + /* Set the r2 save slot to zero. This indicates jump_fcontext + that this is a special context. */ + mvghi GR_OFFSET(%r2),0 + + /* Save the floating point control register. */ + stfpc FPC_OFFSET(%r2) + + /* Store the address of the target function as new pc. */ + stg %r4,PC_OFFSET(%r2) + + /* Store a pointer to the finish routine as r14. If a function + called via context routines just returns that value will be + loaded and used as return address. Hence the program will + just exit. */ + larl %r1,finish + stg %r1,R14_OFFSET(%r2) + + /* Return as usual with the new context returned in r2. */ + br %r14 + +finish: + /* In finish tasks, you load the exit code and exit the + make_fcontext This is called when the context-function is + entirely executed. */ + lghi %r2,0 + brasl %r14,_exit@PLT + +.size make_fcontext,.-make_fcontext +.section .note.GNU-stack,"",%progbits diff --git a/configure.ac b/configure.ac index 5237a59f16ccc..f484ad282cfe4 100644 --- a/configure.ac +++ b/configure.ac @@ -1191,6 +1191,7 @@ AS_CASE([$host_cpu], [arm*], [fiber_cpu="arm32"], [ppc64*], [fiber_cpu="ppc64"], [powerpc*], [fiber_cpu="ppc32"], + [s390x*], [fiber_cpu="s390x"], [mips64*], [fiber_cpu="mips64"], [mips*], [fiber_cpu="mips32"], [fiber_cpu="unknown"] @@ -1208,6 +1209,7 @@ AS_CASE([$fiber_cpu], [arm32], [fiber_asm_file_prefix="arm_aapcs"], [ppc64], [fiber_asm_file_prefix="ppc64_sysv"], [ppc32], [fiber_asm_file_prefix="ppc_sysv"], + [s390x], [fiber_asm_file_prefix="s390x_sysv"], [mips64], [fiber_asm_file_prefix="mips64_n64"], [mips32], [fiber_asm_file_prefix="mips32_o32"], [fiber_asm_file_prefix="unknown"] From 4df21bb766fefc7f2e31572317cc958dcb2cdc88 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Sun, 25 Apr 2021 12:18:25 -0500 Subject: [PATCH 40/43] Remove unneeded prototypes --- Zend/zend_fibers.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 69af0ff6e6b4e..dcfdaf1e38fef 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -44,9 +44,6 @@ static zend_class_entry *zend_ce_fiber_error; static zend_object_handlers zend_fiber_handlers; -static zend_object *zend_fiber_object_create(zend_class_entry *ce); -static void zend_fiber_object_destroy(zend_object *object); - typedef void *fcontext_t; typedef struct _transfer_t { @@ -492,7 +489,7 @@ ZEND_METHOD(Fiber, suspend) zend_fiber_suspend(fiber); if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { - // This occurs when the fiber is GC'ed while suspended, do not add a ref. + // This occurs when the fiber is GC'ed while suspended. if (EG(fiber_error)) { // Throw UnwindExit so finally blocks are not executed on fatal error. zend_throw_unwind_exit(); From 09688b20b283bfbabe057c41fda400b19341e10d Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Sun, 25 Apr 2021 12:18:38 -0500 Subject: [PATCH 41/43] Add test for suspend after throw --- Zend/tests/fibers/catch-then-suspend.phpt | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Zend/tests/fibers/catch-then-suspend.phpt diff --git a/Zend/tests/fibers/catch-then-suspend.phpt b/Zend/tests/fibers/catch-then-suspend.phpt new file mode 100644 index 0000000000000..62d33f9ab0f39 --- /dev/null +++ b/Zend/tests/fibers/catch-then-suspend.phpt @@ -0,0 +1,25 @@ +--TEST-- +Catch exception thrown into fiber, then suspend again +--FILE-- +start()); + +var_dump($fiber->throw(new Exception)); + +var_dump($fiber->resume()); + +?> +--EXPECT-- +string(6) "in try" +string(11) "after catch" +NULL From 6f4a1fa25a89aa8b5bf13d6cb16dbdb4e02192f9 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Sun, 25 Apr 2021 14:01:51 -0500 Subject: [PATCH 42/43] Fix indenting and comment --- Zend/zend_fibers.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index dcfdaf1e38fef..5548177979fc7 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -30,13 +30,13 @@ #include "zend_fibers_arginfo.h" #ifdef HAVE_VALGRIND -#include +# include #endif #ifndef PHP_WIN32 -#include -#include -#include +# include +# include +# include #endif ZEND_API zend_class_entry *zend_ce_fiber; @@ -416,7 +416,7 @@ ZEND_METHOD(Fiber, __construct) Z_PARAM_FUNC(fiber->fci, fiber->fci_cache) ZEND_PARSE_PARAMETERS_END(); - // Keep a reference to closures or callable objects until the fiber is started. + // Keep a reference to closures or callable objects while the fiber is running. Z_TRY_ADDREF(fiber->fci.function_name); } @@ -428,7 +428,7 @@ ZEND_METHOD(Fiber, start) zend_array *named_params; ZEND_PARSE_PARAMETERS_START(0, -1) - Z_PARAM_VARIADIC_WITH_NAMED(params, param_count, named_params); + Z_PARAM_VARIADIC_WITH_NAMED(params, param_count, named_params); ZEND_PARSE_PARAMETERS_END(); if (fiber->status != ZEND_FIBER_STATUS_INIT) { @@ -461,8 +461,8 @@ ZEND_METHOD(Fiber, suspend) zval *exception, *value = NULL; ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(value); + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(value); ZEND_PARSE_PARAMETERS_END(); if (UNEXPECTED(!fiber)) { From a7e1f269649c54b64832cdfdc86206e4012461f2 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Sun, 25 Apr 2021 14:25:54 -0500 Subject: [PATCH 43/43] Add notes in UPGRADING and CONTRIBUTING --- CONTRIBUTING.md | 1 + UPGRADING | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 476b5689db543..21c6d21650ad6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -135,6 +135,7 @@ locations. ├─ .git/ # Git configuration and source directory ├─ TSRM/ # Thread Safe Resource Manager └─ Zend/ # Zend Engine + ├─ asm/ # Bundled from src/asm in https://github.com/boostorg/context ├─ zend_vm_execute.h # Generated by `Zend/zend_vm_gen.php` ├─ zend_vm_opcodes.c # Generated by `Zend/zend_vm_gen.php` ├─ zend_vm_opcodes.h # Generated by `Zend/zend_vm_gen.php` diff --git a/UPGRADING b/UPGRADING index fa3dcffb37799..a69170a1a63a6 100644 --- a/UPGRADING +++ b/UPGRADING @@ -153,6 +153,8 @@ PHP 8.1 UPGRADE NOTES RFC: https://wiki.php.net/rfc/enumerations . Added support for never return type RFC: https://wiki.php.net/rfc/noreturn_type + . Added support for fibers. + RFC: https://wiki.php.net/rfc/fibers - Curl: . Added CURLOPT_DOH_URL option.