-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
I tried this code:
#![feature(s390x_target_feature, portable_simd, simd_ffi, link_llvm_intrinsics)]
use std::simd::*;
#[allow(improper_ctypes)]
extern "C" {
#[link_name = "llvm.smax.v2i64"]
fn vmxg(a: i64x2, b: i64x2) -> i64x2;
}
#[no_mangle]
#[target_feature(enable = "vector")]
pub unsafe fn bar(a: i64x2, b: i64x2) -> i64x2 {
vmxg(a, b)
}
I expected to see this happen: the vmxg
instruction is emitted
Instead, this happened: the vmxb
instruction is emitted
I 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
declare <2 x i64> @llvm.smax.v2i64(<16 x i8>, <16 x i8>) unnamed_addr #1
(we would expect this instead)
declare <2 x i64> @llvm.smax.v2i64(<2 x i64>, <2 x i64>) unnamed_addr #1
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
impl LlvmType for Reg { | |
fn llvm_type<'ll>(&self, cx: &CodegenCx<'ll, '_>) -> &'ll Type { | |
match self.kind { | |
RegKind::Integer => cx.type_ix(self.size.bits()), | |
RegKind::Float => match self.size.bits() { | |
16 => cx.type_f16(), | |
32 => cx.type_f32(), | |
64 => cx.type_f64(), | |
128 => cx.type_f128(), | |
_ => bug!("unsupported float: {:?}", self), | |
}, | |
RegKind::Vector => cx.type_vector(cx.type_i8(), self.size.bytes()), | |
} | |
} | |
} |
The Reg
type does not have enough information to recompute the element type of the vector, and hence it defaults to type_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
let size = arg.layout.size; | |
if size.bits() <= 128 && arg.layout.is_single_vector_element(cx, size) { | |
arg.cast_to(Reg { kind: RegKind::Vector, size }); | |
return; |
The PassMode
for the arguments will be PassMode::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 using PassMode::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
@rustbot label O-SystemZ