-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
vector types are passed incorrectly on s390x
#135744
Comments
Ah, that does indeed look wrong. You're right that vector types themselves could be passed "Direct". But this case also handles the case of an aggregate that has a single element which is of vector type - such aggregates should be transparently passed as their single element according to our ABI. @taiki-e is there some way to extract the original type of that element and use it to perform the cast? |
Currently you can do slightly better by passing the non-wrapped vector types directly: // pass vectors (that are not wrapped in another structure) directly.
// Using the `cast_to` approach below loses type information that is important to LLVM.
if let BackendRepr::Vector { .. } = arg.layout.backend_repr {
return;
}
let size = arg.layout.size;
if size.bits() <= 128 && arg.layout.is_single_vector_element(cx, size) {
// handles non-transparant wrappers around a vector
arg.cast_to(Reg { kind: RegKind::Vector, size });
return;
} That solves the problem with the LLVM intrinsics. However, a wrapped vector would still have its element type erased. That still seems technically incorrect, but I'm not sure that would be observable (maybe when stitching LLVM together manually? sounds cursed). In any case, there is currently insufficient information to recover the element type, but the element type could be added to |
|
Cool, I didn't know about that trick. We could still special-case the vectors though to get rid of some casting noise if let BackendRepr::Vector { .. } = arg.layout.backend_repr {
return;
} which would get rid of the bitcasts here (and presumable at the call site too) define <2 x i64> @bar(<16 x i8> %0, <16 x i8> %1) unnamed_addr {
start:
%2 = bitcast <16 x i8> %0 to <2 x i64>
%3 = bitcast <16 x i8> %1 to <2 x i64>
%_0 = call <2 x i64> @llvm.smax.v2i64(<2 x i64> %2, <2 x i64> %3)
ret <2 x i64> %_0
} LLVM cleans that right up of course, but why not make rustc's and LLVM's job a little easier. |
let size = arg.layout.size;
if size.bits() <= 128 && arg.layout.is_single_vector_element(cx, size) {
// handles non-transparant wrappers around a vector
arg.cast_to(Reg { kind: RegKind::Vector, size });
return;
} should be enough. FWIW clang also uses |
It handles it, and it's not incorrect, but will emit a cast (and all of the noise that that brings, the llvm IR that rustc emits for it is quite verbose). My suggestion is to special-case non-wrapped vector types (where let size = arg.layout.size;
if size.bits() <= 128 {
if let BackendRepr::Vector { .. } = arg.layout.backend_repr {
// pass non-wrapped vector types using `PassMode::Direct`
return;
}
if arg.layout.is_single_vector_element(cx, size) {
// pass non-transparant wrappers around a vector as `PassMode::Cast`
arg.cast_to(Reg { kind: RegKind::Vector, size });
return;
}
} Currently, passing everything caught by E.g. vector_arg_small:
177: vlgvg %r2, %v24, 0
178: br %r14
vector_wrapper_arg_small:
213: lg %r2, 0(%r2)
214: br %r14 #[repr(C)]
pub struct Wrapper<T>(T);
// CHECK-LABEL: vector_arg_small:
// CHECK: vlgvg %r2, %v24, 0
// CHECK-NEXT: br %r14
#[cfg_attr(no_vector, target_feature(enable = "vector"))]
#[no_mangle]
unsafe extern "C" fn vector_arg_small(x: i8x8) -> i64 {
unsafe { *(&x as *const i8x8 as *const i64) }
}
// CHECK-LABEL: vector_wrapper_arg_small:
// CHECK: vlgvg %r2, %v24, 0
// CHECK-NEXT: br %r14
#[cfg_attr(no_vector, target_feature(enable = "vector"))]
#[no_mangle]
unsafe extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 {
unsafe { *(&x as *const Wrapper<i8x8> as *const i64) }
} |
I guess it is fine to add a special case. |
…irect, r=bjorn3 use `PassMode::Direct` for vector types on `s390x` closes rust-lang#135744 tracking issue: rust-lang#130869 Previously, all vector types were type erased to `Ni8`, now we pass non-wrapped vector types directly. That skips emitting a bunch of casting logic in rustc, that LLVM then has to clean up. The initial LLVM IR is also a bit more readable. This calling convention is tested extensively in `tests/assembly/s390x-vector-abi.rs`, showing that this change has no impact on the ABI in practice. r? `@taiki-e`
…irect, r=bjorn3 use `PassMode::Direct` for vector types on `s390x` closes rust-lang#135744 tracking issue: rust-lang#130869 Previously, all vector types were type erased to `Ni8`, now we pass non-wrapped vector types directly. That skips emitting a bunch of casting logic in rustc, that LLVM then has to clean up. The initial LLVM IR is also a bit more readable. This calling convention is tested extensively in `tests/assembly/s390x-vector-abi.rs`, showing that this change has no impact on the ABI in practice. r? ``@taiki-e``
…irect, r=bjorn3 use `PassMode::Direct` for vector types on `s390x` closes rust-lang#135744 tracking issue: rust-lang#130869 Previously, all vector types were type erased to `Ni8`, now we pass non-wrapped vector types directly. That skips emitting a bunch of casting logic in rustc, that LLVM then has to clean up. The initial LLVM IR is also a bit more readable. This calling convention is tested extensively in `tests/assembly/s390x-vector-abi.rs`, showing that this change has no impact on the ABI in practice. r? ```@taiki-e```
Rollup merge of rust-lang#135785 - folkertdev:s390x-vector-passmode-direct, r=bjorn3 use `PassMode::Direct` for vector types on `s390x` closes rust-lang#135744 tracking issue: rust-lang#130869 Previously, all vector types were type erased to `Ni8`, now we pass non-wrapped vector types directly. That skips emitting a bunch of casting logic in rustc, that LLVM then has to clean up. The initial LLVM IR is also a bit more readable. This calling convention is tested extensively in `tests/assembly/s390x-vector-abi.rs`, showing that this change has no impact on the ABI in practice. r? ````@taiki-e````
I tried this code:
I expected to see this happen: the
vmxg
instruction is emittedInstead, this happened: the
vmxb
instruction is emittedI did some digging, and I think I have the problem, and maybe a solution.
The problem
https://godbolt.org/z/YxK3Eo66x
in the godbolt, we see that the
llvm.smax.v2i64
function gets an incorrect signature(we would expect this instead)
So, somewhere along the way, type information is lost.
Where the problem shows up
We first see the incorrect argument type here
rust/compiler/rustc_codegen_llvm/src/abi.rs
Lines 121 to 135 in 39dc268
The
Reg
type does not have enough information to recompute the element type of the vector, and hence it defaults totype_i8
. That is wrong!Why
Reg
That
Reg
(with insufficient type information) is used because of this logic in the calling convention code:rust/compiler/rustc_target/src/callconv/s390x.rs
Lines 40 to 43 in 39dc268
The
PassMode
for the arguments will bePassMode::Cast
.Solutions
I'm not (yet) familiar with the details of the s390x calling convention, so maybe this
Cast
has an essential function. But I think it would be fine to pass vector arguments usingPassMode::Direct
. That works for the example at hand anyway.If not, then the element type information must be stored somewhere so that it can be retrieved later
cc @taiki-e @uweigand
@rustbot label O-SystemZ
The text was updated successfully, but these errors were encountered: