Skip to content
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

rustc: SIMD types use pointers in Rust's ABI #47743

Merged
merged 1 commit into from
Jan 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/librustc_trans/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,31 @@ impl<'a, 'tcx> FnType<'tcx> {

match arg.layout.abi {
layout::Abi::Aggregate { .. } => {}

// This is a fun case! The gist of what this is doing is
// that we want callers and callees to always agree on the
// ABI of how they pass SIMD arguments. If we were to *not*
// make these arguments indirect then they'd be immediates
// in LLVM, which means that they'd used whatever the
// appropriate ABI is for the callee and the caller. That
// means, for example, if the caller doesn't have AVX
// enabled but the callee does, then passing an AVX argument
// across this boundary would cause corrupt data to show up.
//
// This problem is fixed by unconditionally passing SIMD
// arguments through memory between callers and callees
// which should get them all to agree on ABI regardless of
// target feature sets. Some more information about this
// issue can be found in #44367.
//
// Note that the platform intrinsic ABI is exempt here as
// that's how we connect up to LLVM and it's unstable
// anyway, we control all calls to it in libstd.
layout::Abi::Vector { .. } if abi != Abi::PlatformIntrinsic => {
arg.make_indirect();
return
}

_ => return
}

Expand Down
4 changes: 1 addition & 3 deletions src/test/codegen/x86_mmx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ pub struct i8x8(u64);

#[no_mangle]
pub fn a(a: &mut i8x8, b: i8x8) -> i8x8 {
// CHECK-LABEL: define x86_mmx @a(x86_mmx*{{.*}}, x86_mmx{{.*}})
// CHECK: store x86_mmx %b, x86_mmx* %a
// CHECK: ret x86_mmx %b
// CHECK-LABEL: define void @a(x86_mmx*{{.*}}, x86_mmx*{{.*}}, x86_mmx*{{.*}})
*a = b;
return b
}
181 changes: 181 additions & 0 deletions src/test/run-pass/simd-target-feature-mixup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(repr_simd, target_feature, cfg_target_feature)]

use std::process::{Command, ExitStatus};
use std::env;

fn main() {
if let Some(level) = env::args().nth(1) {
return test::main(&level)
}

let me = env::current_exe().unwrap();
for level in ["sse", "avx", "avx512"].iter() {
let status = Command::new(&me).arg(level).status().unwrap();
if status.success() {
println!("success with {}", level);
continue
}

// We don't actually know if our computer has the requisite target features
// for the test below. Testing for that will get added to libstd later so
// for now just asume sigill means this is a machine that can't run this test.
if is_sigill(status) {
println!("sigill with {}, assuming spurious", level);
continue
}
panic!("invalid status at {}: {}", level, status);
}
}

#[cfg(unix)]
fn is_sigill(status: ExitStatus) -> bool {
use std::os::unix::prelude::*;
status.signal() == Some(4)
}

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[allow(bad_style)]
mod test {
// An SSE type
#[repr(simd)]
#[derive(PartialEq, Debug, Clone, Copy)]
struct __m128i(u64, u64);

// An AVX type
#[repr(simd)]
#[derive(PartialEq, Debug, Clone, Copy)]
struct __m256i(u64, u64, u64, u64);

// An AVX-512 type
#[repr(simd)]
#[derive(PartialEq, Debug, Clone, Copy)]
struct __m512i(u64, u64, u64, u64, u64, u64, u64, u64);

pub fn main(level: &str) {
unsafe {
main_normal(level);
main_sse(level);
if level == "sse" {
return
}
main_avx(level);
if level == "avx" {
return
}
main_avx512(level);
}
}

macro_rules! mains {
($(
$(#[$attr:meta])*
unsafe fn $main:ident(level: &str) {
...
}
)*) => ($(
$(#[$attr])*
unsafe fn $main(level: &str) {
let m128 = __m128i(1, 2);
let m256 = __m256i(3, 4, 5, 6);
let m512 = __m512i(7, 8, 9, 10, 11, 12, 13, 14);
assert_eq!(id_sse_128(m128), m128);
assert_eq!(id_sse_256(m256), m256);
assert_eq!(id_sse_512(m512), m512);

if level == "sse" {
return
}
assert_eq!(id_avx_128(m128), m128);
assert_eq!(id_avx_256(m256), m256);
assert_eq!(id_avx_512(m512), m512);

if level == "avx" {
return
}
assert_eq!(id_avx512_128(m128), m128);
assert_eq!(id_avx512_256(m256), m256);
assert_eq!(id_avx512_512(m512), m512);
}
)*)
}

mains! {
unsafe fn main_normal(level: &str) { ... }
#[target_feature(enable = "sse2")]
unsafe fn main_sse(level: &str) { ... }
#[target_feature(enable = "avx")]
unsafe fn main_avx(level: &str) { ... }
#[target_feature(enable = "avx512bw")]
unsafe fn main_avx512(level: &str) { ... }
}


#[target_feature(enable = "sse2")]
unsafe fn id_sse_128(a: __m128i) -> __m128i {
assert_eq!(a, __m128i(1, 2));
a.clone()
}

#[target_feature(enable = "sse2")]
unsafe fn id_sse_256(a: __m256i) -> __m256i {
assert_eq!(a, __m256i(3, 4, 5, 6));
a.clone()
}

#[target_feature(enable = "sse2")]
unsafe fn id_sse_512(a: __m512i) -> __m512i {
assert_eq!(a, __m512i(7, 8, 9, 10, 11, 12, 13, 14));
a.clone()
}

#[target_feature(enable = "avx")]
unsafe fn id_avx_128(a: __m128i) -> __m128i {
assert_eq!(a, __m128i(1, 2));
a.clone()
}

#[target_feature(enable = "avx")]
unsafe fn id_avx_256(a: __m256i) -> __m256i {
assert_eq!(a, __m256i(3, 4, 5, 6));
a.clone()
}

#[target_feature(enable = "avx")]
unsafe fn id_avx_512(a: __m512i) -> __m512i {
assert_eq!(a, __m512i(7, 8, 9, 10, 11, 12, 13, 14));
a.clone()
}

#[target_feature(enable = "avx512bw")]
unsafe fn id_avx512_128(a: __m128i) -> __m128i {
assert_eq!(a, __m128i(1, 2));
a.clone()
}

#[target_feature(enable = "avx512bw")]
unsafe fn id_avx512_256(a: __m256i) -> __m256i {
assert_eq!(a, __m256i(3, 4, 5, 6));
a.clone()
}

#[target_feature(enable = "avx512bw")]
unsafe fn id_avx512_512(a: __m512i) -> __m512i {
assert_eq!(a, __m512i(7, 8, 9, 10, 11, 12, 13, 14));
a.clone()
}
}

#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
mod test {
pub fn main(level: &str) {}
}