-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MSVC on x86-32 Windows fails to align variables to their required alignment #112480
Comments
Let me substantiate this:
compiles to:
Actually, if the stack pointer is 8-bytes aligned when entering the function, the pointer passed to hoge is going to be unaligned (because there's a push first). |
If you can pass unstable flags, currently, you can say |
I think your report here is a duplicate of https://developercommunity.visualstudio.com/t/visual-c-alignof-is-not-conformant-on-x86/1258506 That is, this appears to be a known MSVC bug. |
The MSVC bug would be the value returned by alignof. That doesn't change anything for the alignment check in rust. |
|
It's complicated. See https://learn.microsoft.com/en-us/cpp/c-language/alignment-c?view=msvc-170
So a type with a preferred alignment of 8 bytes can be 4-byte aligned on 32-bit targets. |
Ah, but
|
But see also https://learn.microsoft.com/en-us/cpp/build/reference/zp-struct-member-alignment?view=msvc-170
Where |
It's true that from the perspective that in |
Right. But it matters in terms of how this is addressed. It would be wrong to change For the short term, skipping this check on i686-windows might be the only decent options (which is unfortunate). But longer term it would be better to have a proper fix. I'm not really what that would look like tbh. As you say, it's breaking a core assumption. |
There is no assumption made by the alignment check. We unambiguously tell LLVM that a dereference of a pub unsafe extern "C" fn read_u64(ptr: *const u64) -> u64 {
*ptr
} define i64 @_ZN7example8read_u6417hbc6f74c29a983155E(ptr %ptr) unnamed_addr #0 !dbg !5 {
%0 = load i64, ptr %ptr, align 8, !dbg !10, !noundef !9
ret i64 %0, !dbg !11
} If you are saying that this alignment assumption is wrong, then we have a soundness issue. The bug is not in the alignment check, we either have invalid codegen from MSVC because it does not sufficiently align its |
#103830 / #112157 is basically the same issue but for byval parameters. The unsoundness is immediately visible there because if you assume a byval argument will be 8 byte aligned you'll load from Whereas here, for loads of pointer parameters, it looks like we've been generating code that's unsound when linked to MSVC, but it happened to work in most cases. Here's a comparison of what rustc, Clang, and MSVC generate: https://godbolt.org/z/56h6Md1rd This means we can't reduce our alignment to 4 in all cases, because then we wouldn't have sufficient alignment for FFI calls to Clang-compiled code. Unless we patch Clang at the same time, we'd have to use the higher alignment for our stack/heap allocations, but then use align 4 for loads/stores/etc. |
Issue was discussed during compiler team meeting on Zulip (notes). The actionable for now will be to add a toggle to disable the check introduced in #98112 and backport to the next beta. Next, T-opsem / T-lang will look into it and will define a long-term strategy. @rustbot label -I-compiler-nominated |
… r=wesleywiser Disable alignment checks on i686-pc-windows-msvc r? `@wesleywiser` Because you were in the Zulip discussion of this: https://rust-lang.zulipchat.com/#narrow/stream/238009-t-compiler.2Fmeetings/topic/.5Bweekly.5D.202023-06-15 cc rust-lang#112480
According to llvm/llvm-project#34563 (comment), this bug does not imply an ABI incompatibility... is that something clang somehow achieves? Does rustc need to do anything to get the same ABI? |
I think that simply means we tell LLVM is i64 is 4-byte aligned but still have the Rust i64 8-byte aligned. |
IIUC #112157 fixes the ABI issues, marking |
#112157 fixes the ABI incompatibility where by-value arguments passed on the stack get incorrect stack offsets (meaning we read/write incorrect memory). It does not fix the ABI incompatibility with by-ref/pointer arguments getting incorrect align attributes.
I assume it's been edited since then, but the first word of that comment is "most", so I don't think it's saying that all ABI incompatibilities have been fixed... :P By "parameter passing isn't affected by the ABI alignment here" they mean the stack offsets of parameters, i.e. the problem fixed by #112157 (which was already implemented in Clang). That problem is easier to fix since byval argument stack slots aren't directly exposed to user code, so they can be underaligned without breaking language semantics. |
Right, I didn't consider these I hadn't seen #112157, good to know we have that work-around like clang does. |
Further discussion from T-compiler on weekly triage meeting (notes). Team will follow up for more discussion |
More discussion in the T-compiler triage meeting (on Zulip), this time floating the idea of a downgrade of the platform in the tier support. Or at least document the peculiarities/quirks of this specific target in our tier support list. |
rust-lang/rfcs#3594 proposes to expose an attribute akin to |
Related discussion at the GCC bug tracker https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69560 |
That's not quite the same. There GCC chose to give a higher alignment to This here is the opposite situation: we have a type that must be 8-aligned (and is 8-aligned in struct fields!), but MSVC decides to make it only 4-aligned when it occurs on the stack. That's just fundamentally broken and there is no sane way to deal with it. We would need a fundamentally new concept, separating a type's alignment for stack layout purposes from its minimal alignment requirement. We decided not to do that; all that remains is doing damage control for this fundamentally broken target.
That doesn't help as there will still be code compiled with MSVC that creates pointers that get passed to Rust code. I think what we should do is
Then this issue can be closed. We can even re-enable @saethlin's UB checks for alignment with the same cap of 4, to ensure coherence of the LLVM IR we emit and the UB we check. But Rust crates may still make assumptions based on |
@CAD97 @ChrisDenton I realized I am actually unclear about one aspect of this issue: what exactly is the set of types where MSVC reports a higher alignment than what it will use when putting such a variable on the stack? We know |
Warning The experimental results referenced below are only my recollection of the behavior I got originally. This description is accurate both to that recollection and the documentation, but I or someone else should triple-check it before trusting it. I hope to do so sometime this week once I have a chance. @RalfJung: Based on previous experimental results and some fresh crawling of Microsoft Learn, MSVC alignment works basically as so:
This indicates that 128-bit primitive data types will have the same alignment issue on x86_64 that 64-bit primitive data types do on x86. Is the abi-cafe checking simd vector type ABI/layout? |
What does this mean for
There doesn't seem to be a notion of a type having alignment, only a size and the global alignment flag, so I don't know what this part means. |
|
Yes.
No. You can see this on godbolt. The Also note that SIMD types are special and will be properly aligned. |
What about structs containing SIMD types? |
That is aligned. I updated the godbolt link. |
Okay so it's basically entirely behaving as-if u64 and u128 had alignment 4, except that their offset in a struct will be divisible by 8 (also recursively): Each type has a "required alignment" and a "layout alignment". The former is used to determine which alignment accesses must have (or else UB) and how much variables on the stack will be aligned, the latter is used for struct layout. The required alignment of a struct is the largest required alignment of its fields; the layout alignment of a struct is the largest layout alignment of its fields. Most types have the same value for both kinds of alignment (including the SIMD types), except integer/float primitives larger than 4 have required alignment 4, but layout alignment matching their size. |
I tried testing this but |
Right, only SIMD has 128-bit integers for MSVC. There isn't an issue for 64-bit because all "natural" types are <= 8 byte aligned (except for SIMD types which are special). Here's a list of all the built-in types I know of (minus some aliases):
So all stack alignments for basic types on x86 are capped at 4. There are two exceptions I know of:
So oddly enough these two structs have different stack alignments but will otherwise be identical: struct A {
bool b;
int64_t x;
};
struct B {
bool b;
alignas(int64_t) int64_t x;
}; |
This is a regression from #98112. I suppose it's not possible to disable this specific check only while preserving debug assertions...
The core problem is that the x86 ABI on Windows doesn't guarantee the stack alignment above 4. See for example https://developercommunity.visualstudio.com/t/vs2017-64-bit-int-alignment-problem/294259
And while some types have an alignment reported of 8 (e.g. UINT64), in practice, the C compiler will happily not align them on the stack.
So for example, this C code, compiled by MSVC for 32-bits:
will produce this assembly:
(on godbolt)
If the stack pointer is not 8-bytes aligned when entering the function, the pointer passed to
hoge
is not going to be 8-bytes aligned.As mentioned in the linked community post above, adding
alignas(8)
to the type definition makes the compiler align the stack:becomes
(on godbolt)
Now, what this means is that if that
hoge
function is a rust FFI function, and it uses that pointer, the "misaligned pointer dereference" check is hit and panic ensues.Real life case, for the curious:
https://github.com/servo/dwrote-rs/blob/master/src/font_file_loader_impl.rs#L116-L123
That function is called from dwrite.dll (which comes with Windows).
t-opsem FCP comment
Summary of the MSVC alignment rules
The text was updated successfully, but these errors were encountered: