From f011d226c72127310127f693cfb9b3075b7daafe Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 9 Sep 2026 18:15:03 -0400 Subject: [PATCH 1/2] Add fcontext support for Alpha (SysV/ELF) Alpha had no fcontext assembly, so a build on that architecture found no matching asm_sources alternative. The Alpha calling standard returns a 16 byte struct such as transfer_t through a hidden pointer passed in $16, which shifts the declared arguments to $17 and $18. That pointer belongs to the frame that made the call, so it has to travel with the context: each context records the pointer its own suspending call was given, and whoever resumes that context stores the transfer_t through it. Among the existing ports only ppc32-sysv-linux returns a context through memory this way. transfer_t is also passed by value in a register pair, so $16 and $17 are loaded with it as well. A context entered for the first time by make_fcontext reads its argument that way; for an ordinary resumption those are argument registers the resumed call may clobber, so setting them unconditionally is harmless. The resumption PC is loaded into $27 so that it doubles as the procedure value a context-function entered for the first time computes its own gp from. The transfer at the end of jump_fcontext and ontop_fcontext uses the ret hint rather than jmp. Those functions are entered by a bsr, which pushes onto the return prediction stack, and the resumption PC is where that call appears to return, so a jmp hint would orphan an entry per switch; the ret hint pops it. Worth about 3% on a ping-pong switch benchmark on an EV68AL UP1500. jsr_coroutine does not fit despite the name: it pushes PC+4, and resume points live in the context-data. --- build/Jamfile.v2 | 24 +++++ doc/architectures.qbk | 1 + src/asm/jump_alpha_sysv_elf_gas.S | 143 +++++++++++++++++++++++++++++ src/asm/make_alpha_sysv_elf_gas.S | 118 ++++++++++++++++++++++++ src/asm/ontop_alpha_sysv_elf_gas.S | 139 ++++++++++++++++++++++++++++ 5 files changed, 425 insertions(+) create mode 100644 src/asm/jump_alpha_sysv_elf_gas.S create mode 100644 src/asm/make_alpha_sysv_elf_gas.S create mode 100644 src/asm/ontop_alpha_sysv_elf_gas.S diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 3b04ce90..8c9d0f3b 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -528,6 +528,30 @@ alias asm_sources gcc ; +# ALPHA +# ALPHA/SYSV/ELF +alias asm_sources + : asm/make_alpha_sysv_elf_gas.S + asm/jump_alpha_sysv_elf_gas.S + asm/ontop_alpha_sysv_elf_gas.S + : sysv + 64 + alpha + elf + gcc + ; + +alias asm_sources + : asm/make_alpha_sysv_elf_gas.S + asm/jump_alpha_sysv_elf_gas.S + asm/ontop_alpha_sysv_elf_gas.S + : sysv + 64 + alpha + elf + clang + ; + # S390X # S390X/SYSV/ELF alias asm_sources diff --git a/doc/architectures.qbk b/doc/architectures.qbk index b17e1f22..df2dea2d 100644 --- a/doc/architectures.qbk +++ b/doc/architectures.qbk @@ -12,6 +12,7 @@ architectures: [table Supported architectures () [[Architecture] [LINUX (UNIX)] [Windows] [MacOS X] [iOS]] + [[alpha] [SYSV|ELF] [-] [-] [-]] [[arm (aarch32)] [AAPCS|ELF] [AAPCS|PE] [-] [AAPCS|MACH-O]] [[arm (aarch64)] [AAPCS|ELF] [-] [AAPCS|MACH-O] [AAPCS|MACH-O]] [[i386] [SYSV|ELF] [MS|PE] [SYSV|MACH-O] [-]] diff --git a/src/asm/jump_alpha_sysv_elf_gas.S b/src/asm/jump_alpha_sysv_elf_gas.S new file mode 100644 index 00000000..fd52df41 --- /dev/null +++ b/src/asm/jump_alpha_sysv_elf_gas.S @@ -0,0 +1,143 @@ +/* + 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 | * + * --------------------------------------------------- * + * | 0x0 | 0x8 | 0x10 | 0x18 | 0x20 | * + * --------------------------------------------------- * + * | $f2 | $f3 | $f4 | $f5 | $f6 | * + * --------------------------------------------------- * + * | 5 | 6 | 7 | 8 | 9 | * + * --------------------------------------------------- * + * | 0x28 | 0x30 | 0x38 | 0x40 | 0x48 | * + * --------------------------------------------------- * + * | $f7 | $f8 | $f9 | $9 | $10 | * + * --------------------------------------------------- * + * | 10 | 11 | 12 | 13 | 14 | * + * --------------------------------------------------- * + * | 0x50 | 0x58 | 0x60 | 0x68 | 0x70 | * + * --------------------------------------------------- * + * | $11 | $12 | $13 | $14 | $15 | * + * --------------------------------------------------- * + * | 15 | 16 | 17 | 18 | 19 | * + * --------------------------------------------------- * + * | 0x78 | 0x80 | 0x88 | 0x90 | 0x98 | * + * --------------------------------------------------- * + * | $29 | $26 | PC | hidden | pad | * + * | (gp) | (ra) | | ptr | | * + * --------------------------------------------------- * + * * + ********************************************************/ + +/* The Alpha calling standard returns a 16 byte struct such as transfer_t + via a hidden pointer in $16, shifting the declared arguments to $17 and + $18. That pointer belongs to the frame that made the call, so it has to + travel with the context: each context records the pointer its own + suspending call was given, and whoever resumes it stores through it. */ + +.file "jump_alpha_sysv_elf_gas.S" +.text +.align 4 +.globl jump_fcontext +.hidden jump_fcontext +.type jump_fcontext, @function +.ent jump_fcontext +jump_fcontext: + .frame $30, 0, $26, 0 + .prologue 0 + + # reserve space for context-data on the current stack + lda $30, -0xa0($30) + + # save callee-saved floating point registers $f2 - $f9 + stt $f2, 0x00($30) + stt $f3, 0x08($30) + stt $f4, 0x10($30) + stt $f5, 0x18($30) + stt $f6, 0x20($30) + stt $f7, 0x28($30) + stt $f8, 0x30($30) + stt $f9, 0x38($30) + + # save callee-saved integer registers $9 - $15 + stq $9, 0x40($30) + stq $10, 0x48($30) + stq $11, 0x50($30) + stq $12, 0x58($30) + stq $13, 0x60($30) + stq $14, 0x68($30) + stq $15, 0x70($30) + + # save gp and return address + stq $29, 0x78($30) + stq $26, 0x80($30) + + # save the return address as the resumption PC + stq $26, 0x88($30) + + # save the hidden transfer_t pointer this call was given + stq $16, 0x90($30) + + # $1 = fcontext_t of the context we are suspending + bis $31, $30, $1 + + # switch to the stack of the context we are resuming + bis $31, $17, $30 + + # restore callee-saved floating point registers $f2 - $f9 + ldt $f2, 0x00($30) + ldt $f3, 0x08($30) + ldt $f4, 0x10($30) + ldt $f5, 0x18($30) + ldt $f6, 0x20($30) + ldt $f7, 0x28($30) + ldt $f8, 0x30($30) + ldt $f9, 0x38($30) + + # restore callee-saved integer registers $9 - $15 + ldq $9, 0x40($30) + ldq $10, 0x48($30) + ldq $11, 0x50($30) + ldq $12, 0x58($30) + ldq $13, 0x60($30) + ldq $14, 0x68($30) + ldq $15, 0x70($30) + + # restore gp and return address + ldq $29, 0x78($30) + ldq $26, 0x80($30) + + # load the resumption PC into $27; it doubles as the procedure value + # for a context-function entered for the first time, which computes + # its own gp from it + ldq $27, 0x88($30) + + # load the hidden transfer_t pointer of the resumed context + ldq $2, 0x90($30) + + # release the context-data + lda $30, 0xa0($30) + + # return transfer_t{ $1, $18 } through the hidden pointer + stq $1, 0x00($2) + stq $18, 0x08($2) + bis $31, $2, $0 + + # and pass it by value, for a context entered via make_fcontext + bis $31, $1, $16 + bis $31, $18, $17 + + # the ret hint pops the return prediction stack entry pushed by the + # bsr that called us; the resumption PC is where that call appears to + # return. A jmp hint would orphan one entry per switch. + ret $31, ($27), 0 +.end jump_fcontext +.size jump_fcontext, .-jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",@progbits diff --git a/src/asm/make_alpha_sysv_elf_gas.S b/src/asm/make_alpha_sysv_elf_gas.S new file mode 100644 index 00000000..2fd2eb77 --- /dev/null +++ b/src/asm/make_alpha_sysv_elf_gas.S @@ -0,0 +1,118 @@ +/* + 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 | * + * --------------------------------------------------- * + * | 0x0 | 0x8 | 0x10 | 0x18 | 0x20 | * + * --------------------------------------------------- * + * | $f2 | $f3 | $f4 | $f5 | $f6 | * + * --------------------------------------------------- * + * | 5 | 6 | 7 | 8 | 9 | * + * --------------------------------------------------- * + * | 0x28 | 0x30 | 0x38 | 0x40 | 0x48 | * + * --------------------------------------------------- * + * | $f7 | $f8 | $f9 | $9 | $10 | * + * --------------------------------------------------- * + * | 10 | 11 | 12 | 13 | 14 | * + * --------------------------------------------------- * + * | 0x50 | 0x58 | 0x60 | 0x68 | 0x70 | * + * --------------------------------------------------- * + * | $11 | $12 | $13 | $14 | $15 | * + * --------------------------------------------------- * + * | 15 | 16 | 17 | 18 | 19 | * + * --------------------------------------------------- * + * | 0x78 | 0x80 | 0x88 | 0x90 | 0x98 | * + * --------------------------------------------------- * + * | $29 | $26 | PC | hidden | pad | * + * | (gp) | (ra) | | ptr | | * + * --------------------------------------------------- * + * | 20 | 21 | | | | * + * --------------------------------------------------- * + * | 0xa0 | 0xa8 | | | | * + * --------------------------------------------------- * + * | transfer_t scratch | * + * --------------------------------------------------- * + * * + ********************************************************/ + +.file "make_alpha_sysv_elf_gas.S" +.text +.align 4 +.globl make_fcontext +.hidden make_fcontext +.type make_fcontext, @function +.ent make_fcontext +make_fcontext: + .frame $30, 0, $26, 0 + ldgp $29, 0($27) + .prologue 1 + + # $16: top address of the context-stack + # $17: size of the context-stack + # $18: address of the context-function + + # shift the address in $16 down to a 16 byte boundary + bic $16, 0xF, $0 + + # reserve space for the context-data and for the transfer_t that + # jump_fcontext writes through the hidden pointer when it enters the + # context-function + lda $0, -0xc0($0) + + # the context-function is the resumption PC + stq $18, 0x88($0) + + # jump_fcontext restores gp before entering the context-function; the + # context-function computes its own, but leave a valid value here + stq $29, 0x78($0) + + # point the hidden pointer at the scratch transfer_t + lda $1, 0xa0($0) + stq $1, 0x90($0) + + # start the context with cleared callee-saved registers + stt $f31, 0x00($0) + stt $f31, 0x08($0) + stt $f31, 0x10($0) + stt $f31, 0x18($0) + stt $f31, 0x20($0) + stt $f31, 0x28($0) + stt $f31, 0x30($0) + stt $f31, 0x38($0) + stq $31, 0x40($0) + stq $31, 0x48($0) + stq $31, 0x50($0) + stq $31, 0x58($0) + stq $31, 0x60($0) + stq $31, 0x68($0) + stq $31, 0x70($0) + + # save the address of finish as the return address of the + # context-function; it is entered after the context-function returns + ldq $1, finish($29) !literal + stq $1, 0x80($0) + + # return the pointer to the context-data in $0 + ret $31, ($26), 1 + +finish: + # the context-function returned; gp is not live here, so recompute it + br $29, 1f +1: ldgp $29, 0($29) + + # exit code is zero + bis $31, $31, $16 + + # exit application + ldq $27, _exit($29) !literal + jsr $26, ($27), _exit +.end make_fcontext +.size make_fcontext, .-make_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",@progbits diff --git a/src/asm/ontop_alpha_sysv_elf_gas.S b/src/asm/ontop_alpha_sysv_elf_gas.S new file mode 100644 index 00000000..54d6f405 --- /dev/null +++ b/src/asm/ontop_alpha_sysv_elf_gas.S @@ -0,0 +1,139 @@ +/* + 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 | * + * --------------------------------------------------- * + * | 0x0 | 0x8 | 0x10 | 0x18 | 0x20 | * + * --------------------------------------------------- * + * | $f2 | $f3 | $f4 | $f5 | $f6 | * + * --------------------------------------------------- * + * | 5 | 6 | 7 | 8 | 9 | * + * --------------------------------------------------- * + * | 0x28 | 0x30 | 0x38 | 0x40 | 0x48 | * + * --------------------------------------------------- * + * | $f7 | $f8 | $f9 | $9 | $10 | * + * --------------------------------------------------- * + * | 10 | 11 | 12 | 13 | 14 | * + * --------------------------------------------------- * + * | 0x50 | 0x58 | 0x60 | 0x68 | 0x70 | * + * --------------------------------------------------- * + * | $11 | $12 | $13 | $14 | $15 | * + * --------------------------------------------------- * + * | 15 | 16 | 17 | 18 | 19 | * + * --------------------------------------------------- * + * | 0x78 | 0x80 | 0x88 | 0x90 | 0x98 | * + * --------------------------------------------------- * + * | $29 | $26 | PC | hidden | pad | * + * | (gp) | (ra) | | ptr | | * + * --------------------------------------------------- * + * * + ********************************************************/ + +.file "ontop_alpha_sysv_elf_gas.S" +.text +.align 4 +.globl ontop_fcontext +.hidden ontop_fcontext +.type ontop_fcontext, @function +.ent ontop_fcontext +ontop_fcontext: + .frame $30, 0, $26, 0 + .prologue 0 + + # $16: hidden transfer_t pointer + # $17: fcontext_t to + # $18: void * vp + # $19: transfer_t (*fn)(transfer_t) + + # reserve space for context-data on the current stack + lda $30, -0xa0($30) + + # save callee-saved floating point registers $f2 - $f9 + stt $f2, 0x00($30) + stt $f3, 0x08($30) + stt $f4, 0x10($30) + stt $f5, 0x18($30) + stt $f6, 0x20($30) + stt $f7, 0x28($30) + stt $f8, 0x30($30) + stt $f9, 0x38($30) + + # save callee-saved integer registers $9 - $15 + stq $9, 0x40($30) + stq $10, 0x48($30) + stq $11, 0x50($30) + stq $12, 0x58($30) + stq $13, 0x60($30) + stq $14, 0x68($30) + stq $15, 0x70($30) + + # save gp and return address + stq $29, 0x78($30) + stq $26, 0x80($30) + + # save the return address as the resumption PC + stq $26, 0x88($30) + + # save the hidden transfer_t pointer this call was given + stq $16, 0x90($30) + + # $1 = fcontext_t of the context we are suspending + bis $31, $30, $1 + + # switch to the stack of the context we are resuming + bis $31, $17, $30 + + # restore callee-saved floating point registers $f2 - $f9 + ldt $f2, 0x00($30) + ldt $f3, 0x08($30) + ldt $f4, 0x10($30) + ldt $f5, 0x18($30) + ldt $f6, 0x20($30) + ldt $f7, 0x28($30) + ldt $f8, 0x30($30) + ldt $f9, 0x38($30) + + # restore callee-saved integer registers $9 - $15 + ldq $9, 0x40($30) + ldq $10, 0x48($30) + ldq $11, 0x50($30) + ldq $12, 0x58($30) + ldq $13, 0x60($30) + ldq $14, 0x68($30) + ldq $15, 0x70($30) + + # restore gp; the resumed context's return address becomes the return + # address of the ontop-function, so that when it returns it lands where + # the resumed context suspended + ldq $29, 0x78($30) + ldq $26, 0x80($30) + + # load the hidden transfer_t pointer of the resumed context; the + # ontop-function returns its transfer_t through it + ldq $2, 0x90($30) + + # skip the resumption PC + # release the context-data + lda $30, 0xa0($30) + + # transfer_t{ $1, $18 } is passed by value in $17/$18; $18 already + # holds vp + bis $31, $2, $16 + bis $31, $1, $17 + + # jump to the ontop-function; $27 is its procedure value. The + # ontop-function returns to the resumed context rather than to our + # caller, so the ret hint pops the entry the bsr that called us + # pushed, which would otherwise be orphaned. + bis $31, $19, $27 + ret $31, ($27), 0 +.end ontop_fcontext +.size ontop_fcontext, .-ontop_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",@progbits From 81a8e50b6a687c2d4def0f0a82699476e3929e69 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 9 Sep 2026 21:44:06 -0400 Subject: [PATCH 2/2] Add alpha to the CMake architecture list The CMake build selects the fcontext assembly by interpolating BOOST_CONTEXT_ARCHITECTURE into the source name, and alpha was not among the recognized values, so a CMake build on Alpha fell through to the 64-bit default of x86_64. Listing alpha in _all_archs is enough: CMAKE_SYSTEM_PROCESSOR is "alpha" there, so the IN_LIST branch matches it, as it already does for riscv64, s390x and loongarch64. The remaining defaults are sysv, elf and gas. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e0448b1f..2d96606c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,7 @@ unset(_default_abi) ## Arch-and-model -set(_all_archs arm arm64 e2k loongarch64 mips32 mips64 ppc32 ppc64 riscv64 s390x i386 x86_64 combined) +set(_all_archs alpha arm arm64 e2k loongarch64 mips32 mips64 ppc32 ppc64 riscv64 s390x i386 x86_64 combined) # Try at start to auto determine arch from CMake. if(CMAKE_SYSTEM_PROCESSOR IN_LIST _all_archs) @@ -80,7 +80,7 @@ else() endif() endif() -set(BOOST_CONTEXT_ARCHITECTURE "${_default_arch}" CACHE STRING "Boost.Context architecture (arm, arm64, e2k, loongarch64, mips32, mips64, ppc32, ppc64, riscv64, s390x, i386, x86_64, combined)") +set(BOOST_CONTEXT_ARCHITECTURE "${_default_arch}" CACHE STRING "Boost.Context architecture (alpha, arm, arm64, e2k, loongarch64, mips32, mips64, ppc32, ppc64, riscv64, s390x, i386, x86_64, combined)") set_property(CACHE BOOST_CONTEXT_ARCHITECTURE PROPERTY STRINGS ${_all_archs}) unset(_all_archs)