From 2ca2cf29af95e22db89852b9755fc7eca757bf17 Mon Sep 17 00:00:00 2001 From: Khalil Estell Date: Fri, 24 Apr 2026 15:22:48 -0700 Subject: [PATCH 1/2] :bug: Fix stack word alignment on x86_64 builds Costs an additional word (8-bytes total) on Arm builds --- modules/coroutine.cppm | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/modules/coroutine.cppm b/modules/coroutine.cppm index e4be93a..dbf7dfc 100644 --- a/modules/coroutine.cppm +++ b/modules/coroutine.cppm @@ -50,7 +50,11 @@ namespace async::inline v0 { * satisfy the alignment contract of operator new. See initialize_stack_memory() * for details. */ -export using stack_word = std::uintptr_t; + +export struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) stack_word +{ + uintptr_t data = 0; +}; /** * @brief Bit mask for pointer alignment checking @@ -403,7 +407,7 @@ public: // NOTE: subtract 1 because we use the end of the stack for holding the // length of the stack. auto const capacity = p_stack_memory.size() - 1uz; - p_stack_memory.back() = capacity; + p_stack_memory.back().data = capacity; m_stack_pointer = &p_stack_memory.front(); m_stack_end = &p_stack_memory.back(); } @@ -663,7 +667,7 @@ public: */ [[nodiscard]] constexpr auto capacity() const noexcept { - return *m_stack_end; + return m_stack_end->data; } /** @@ -850,7 +854,8 @@ private: // Put the address of the stack pointer member on the stack, before the // coroutine frame, such that the delete operation can find the address and // update it. - *m_stack_pointer = std::bit_cast(&m_stack_pointer); + m_stack_pointer->data = + std::bit_castdata)>(&m_stack_pointer); // Address of the coroutine frame will be the current position of the stack // pointer + 1 to avoid overwriting the stack pointer address. @@ -1031,7 +1036,7 @@ public: } private: - alignas(std::max_align_t) std::array m_stack{}; + std::array m_stack{}; }; // ============================================================================= @@ -1084,13 +1089,16 @@ public: */ static constexpr void operator delete(void* p_promise) noexcept { - // Acquire the pointer to the context stack memory from behind the coroutine - // frame's memory. - auto** stack_pointer_address = *(static_cast(p_promise) - 1); - - // Update the stack pointer's address to be equal where it was before the - // promise was allocated. Or said another way, the - *stack_pointer_address = (static_cast(p_promise) - 1); + // The stack that this promise is allocated on is in stack_word chunks. + // Behind the promise's address is a stack_word that has the stack pointer's + // address within its `data` member. + auto* frame_header = static_cast(p_promise) - 1uz; + // The data field contains the address of the stack pointer, making it a + // double pointer. + auto** stack_pointer_ptr = std::bit_cast(frame_header->data); + // De-reference the stack_pointer_ptr to modify the stack_pointer_ptr and + // assign it to the address of the frame_header + *stack_pointer_ptr = frame_header; } /** From 8e8361d8ccfdbe98fb32d77637d4c55c411a5479 Mon Sep 17 00:00:00 2001 From: Khalil Estell Date: Fri, 24 Apr 2026 15:40:50 -0700 Subject: [PATCH 2/2] [skip ci] remove extra newline between docs of stack_wrod --- modules/coroutine.cppm | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/coroutine.cppm b/modules/coroutine.cppm index dbf7dfc..0424323 100644 --- a/modules/coroutine.cppm +++ b/modules/coroutine.cppm @@ -50,7 +50,6 @@ namespace async::inline v0 { * satisfy the alignment contract of operator new. See initialize_stack_memory() * for details. */ - export struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) stack_word { uintptr_t data = 0;