-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Add alignment parameter to simd_masked_{load,store}
#147355
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
base: master
Are you sure you want to change the base?
Conversation
The Miri subtree was changed cc @rust-lang/miri Portable SIMD is developed in its own repository. If possible, consider making this change to rust-lang/portable-simd instead. cc @calebzulawski, @programmerjake Some changes occurred to the platform-builtins intrinsics. Make sure the cc @antoyo, @GuillaumeGomez, @bjorn3, @calebzulawski, @programmerjake Some changes occurred in compiler/rustc_codegen_ssa Some changes occurred to the intrinsics. Make sure the CTFE / Miri interpreter |
This comment has been minimized.
This comment has been minimized.
let default = i32x4::splat(0); | ||
let mask = i32x4::from_array([!0, !0, !0, 0]); | ||
let vals = unsafe { intrinsics::simd_masked_load(mask, buf.as_ptr(), default) }; | ||
let vals = unsafe { intrinsics::simd_masked_load(mask, buf.as_ptr(), default, 4) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i32
doesn't always have alignment 4
, so this should use align_of::<i32>()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh! Didn't know that, is that true for all primitive types too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
basically all primitive types, though 1 byte types must have alignment 1 due to rust's rule that size is always a multiple of alignment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g. on avr-none
, integers/floats have alignment 1, and on msp430-none-elf
most types have alignment 2.
// The fourth argument is the alignment, must be a power of two integer constant | ||
let alignment = bx | ||
.const_to_opt_u128(args[3].immediate(), false) | ||
.expect("typeck should have ensure that this is a const"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I'm missing it because I'm not familiar with typeck's handling of intrinsics, but I do not see where you actually add a typeck check that this is actually a constant. It looks like you're just telling typeck that it's a parameter of u32.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the expect
message here is wrong.
For simd_insert/extract/shuffle, we have some ad-hoc checks in typeck that ensure this. But for intrinsics it's also fine to just ICE when they are used wrong. Rust has no concept of const arguments so this is a bad hack anyway.
// CHECK: call void @llvm.masked.store.v4p0.p0(<4 x ptr> {{.*}}, ptr {{.*}}, i32 {{.*}}, <4 x i1> [[B]]) | ||
simd_masked_store(mask, pointer, values) | ||
// CHECK: call void @llvm.masked.store.v4p0.p0(<4 x ptr> {{.*}}, ptr {{.*}}, i32 8, <4 x i1> [[B]]) | ||
simd_masked_store(mask, pointer, values, 8) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looks like it assumes the alignment of a pointer is 8, but it should be fine even if that alignment is wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not assuming anything here, just trying to test if the alignment is propagated to LLVM
If we only need normally-aligned and unaligned loads, IMO it'd be better to just have a const generic boolean indicating which of them we want for any particular operation. That avoids ad-hoc hacks such as const parameters in intrinsics. |
|
||
// The fourth argument is the alignment, must be a power of two integer constant | ||
let alignment = bx | ||
.const_to_opt_u128(args[3].immediate(), false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use a const generic? That is a lot easier to implement in some other codegen backends.
for portable-simd I think we should default to element-level-alignment since I expect that to be more efficient than unaligned ops on some targets (GPUs? maybe RISC-V V?) |
Yes, so...? IIUC we either want element-level alignment or no alignment, so we can just have a |
I thought you meant full-simd-type alignment or unaligned, since that's what x86 uses for simd instructions it calls aligned. |
This is about
|
As a summary, we need 3 types of alignments
So a bool flag won't cut it, at best we can use a const generic parameter, with 0 meaning element size aligned (because that is the most used, and can't be specified using const generics (requires gce)) |
Now you are expanding the scope of the PR. So far the motivation has been, we'd like an unaligned version of the existing intrinsics. If you also want SIMD type aligned variants, the PR description needs to be expanded to argue for this. IIRC, last time this was looked into, the SIMD type alignment option wasn't necessary -- LLVM was more than able to use surrounding info on reference types to deduce the right alignment for the desired codegen. So please show some concrete undesirable codegen if you want to motivate a form of this intrinsic that requires SIMD type alignment. |
I apologise if I was unclear, but the motivation was always adding these 3 types of loads. LLVM will always generate an unaligned (byte-aligned) load/store if we pass any alignment less that the vector type size (because it is guaranteed to be safe). But for |
I don't think the "always" here is correct. If we are loading from an However, I guess stdarch uses raw pointers in its API. So yeah this definitely needs to be explained properly in the PR description, currently it is at best confusing. |
If we need 3 different alignment modes (Cc @Amanieu for the stdarch part here), that can still be done using const generics with a new 3-variant enum (similar to the enum we have for atomic memory access orderings). |
Yes, std::arch tries to reflect the type signatures used by the C vendor functions... it's not exactly just "bindgen for vendor functions", but it kinda is bindgen for vendor functions. |
I'm happy with the current API that takes a constant (either as an argument or a const generic). An enum doesn't really provide much of an advantage when the desired alignment can just be explicitly provided. |
If everyone agrees, I can substitute the const argument for a const-generic |
The enum provides the big advantage that we don't need more ad-hock "constant argument" hacks. What I was hoping to get from you is confirmation on which forms of the intrinsic are needed for stdarch. |
I would prefer that over the "constant argument" hack. Not sure if it's better than a 3-value enum but 🤷 . |
r? @RalfJung though feel free to reassigned |
This PR adds an
alignment
parameter to the SIMD intrinsicssimd_masked_load
andsimd_masked_store
. This parameter is the (byte) alignment of theptr
parameter, so this is kind of a generalization from the previous signature to allow under-aligned and over-aligned pointers.The main motive for this is stdarch - most vector loads are either fully aligned (to the vector size) or unaligned (byte-aligned), so the previous signature doesn't cut it.
I introduced a const parameter instead of a const-generic parameter because portable-simd uses pointers aligned to the element type, and so needs to pass
align_of::<T>()
as the alignment, but this isn't possible with const-generic parameters without GCE.cg_llvm
cg_clif
(not planned in this PR)miri
/const_eval
(will implement once Implement most SIMD intrinsics in const-eval #146568 is merged, otherwise there will be nasty merge conflicts)Alternatives
Using a const-generic parameter, with 0 having the special meaning of using the element type's alignment. This will be useful in the common case of using the element type's alignment, and also offer enough flexibility to use in stdarch
cc @workingjubilee @RalfJung @BoxyUwU