<type_traits>: std::aligned_storage<8, 8> doesn't force stack realignment on MSVC x86
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 11.1k
- Forks
- 1.7k
- Avg merge
- 4d 15h
- Merged PRs (30d)
- 22
Description
If a user declares a local variable with the type std::aligned_union_t<1, double> or std::aligned_storage<8, 8>, the local variable may not in practice be 8-byte aligned. While accessing the memory will not cause crashes, it can lead to issues if the program assumes that the low bits of the variable's address are zero. LLVM does this often, and that is where I encountered the issue:
https://reviews.llvm.org/D92509#2452929
The MSVC x86 compiler only maintains 4 byte stack alignment. If a user declares a variable with 8 byte alignment, such as a double or __int64, it will not realign the stack. If the user explicitly requests 8 byte alignment with alignas or __declspec(align), then MSVC will realign the stack.
Arguably, this is a Visual C++ compiler bug: the compiler should realign the stack for doubles or any other type with 8 byte alignment. However, the STL could help the user out here. If the user is bothering to use a std::aligned* type, they probably care about the alignment more than any old double local variable, and the STL should go the extra mile to satisfy that alignment by applying the alignas attribute to the double specialization of the _Aligned template.
Consider the assembly generated for the test case on godbolt:
https://gcc.godbolt.org/z/TxEo7b
Command-line test case
C:\Temp>type repro.cpp
#include <type_traits>
struct Foo {
std::aligned_union_t<1, double> data;
};
void escape(Foo*);
void declareAndEscape() {
#ifdef REALIGN
alignas(double)
#endif
Foo o;
escape(&o);
}
C:\Temp>cl -c -GS- repro.cpp -Facl.asm
Microsoft (R) C/C++ Optimizing Compiler Version 19.28.29334 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
repro.cpp
C:\Temp>type cl.asm
; Listing generated by Microsoft (R) Optimizing Compiler Version 19.28.29334.0
TITLE C:\src\llvm-project\build\repro.cpp
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC ?declareAndEscape@@YAXXZ ; declareAndEscape
EXTRN ?escape@@YAXPAUFoo@@@Z:PROC ; escape
; Function compile flags: /Odtp
_TEXT SEGMENT
_o$ = -8 ; size = 8
?declareAndEscape@@YAXXZ PROC ; declareAndEscape
; File C:\src\llvm-project\build\repro.cpp
; Line 6
push ebp
mov ebp, esp
sub esp, 8
; Line 11
lea eax, DWORD PTR _o$[ebp]
push eax
call ?escape@@YAXPAUFoo@@@Z ; escape
add esp, 4
; Line 12
mov esp, ebp
pop ebp
ret 0
?declareAndEscape@@YAXXZ ENDP ; declareAndEscape
_TEXT ENDS
END
Expected behavior
Note the lack of AND instructions to realign ESP. Recompile with -DREALIGN to see the desired stack realignment prologue.
STL version
19.28
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in <type_traits> at the _Aligned implementation, then reproduce the aligned_union_t/aligned_storage_t case with the provided MSVC x86 command and inspect the generated assembly. Done means the local object receives the requested 8-byte alignment and the compiler output shows stack realignment, while the existing behavior remains unchanged for other targets.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100