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

[WIP] Implement RFC2574 for FFI declarations #59238

Closed
wants to merge 9 commits into from
Closed
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
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,13 @@ matrix:
- env: IMAGE=arm-android
if: branch = auto
- env: IMAGE=armhf-gnu
if: branch = auto
#if: branch = auto
- env: IMAGE=dist-various-1 DEPLOY=1
if: branch = auto
- env: IMAGE=dist-various-2 DEPLOY=1
if: branch = auto
- env: IMAGE=dist-aarch64-linux DEPLOY=1
if: branch = auto
#if: branch = auto
- env: IMAGE=dist-android DEPLOY=1
if: branch = auto
- env: IMAGE=dist-arm-linux DEPLOY=1
Expand Down Expand Up @@ -153,7 +153,7 @@ matrix:
- env: IMAGE=dist-powerpc64-linux DEPLOY=1
if: branch = auto
- env: IMAGE=dist-powerpc64le-linux DEPLOY=1
if: branch = auto
#if: branch = auto
- env: IMAGE=dist-s390x-linux DEPLOY=1
if: branch = auto
- env: IMAGE=dist-x86_64-freebsd DEPLOY=1
Expand Down
213 changes: 186 additions & 27 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ use rustc::hir::Node;
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc::hir::GenericParamKind;
use rustc::hir::{self, CodegenFnAttrFlags, CodegenFnAttrs, Unsafety};
use rustc::hir::{self, CodegenFnAttrFlags, CodegenFnAttrs, /*Unsafety*/};

use std::iter;

Expand Down Expand Up @@ -1599,7 +1599,7 @@ fn fn_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::PolyFnSig

let icx = ItemCtxt::new(tcx, def_id);

match tcx.hir().get_by_hir_id(hir_id) {
let sig = match tcx.hir().get_by_hir_id(hir_id) {
TraitItem(hir::TraitItem {
node: TraitItemKind::Method(sig, _),
..
Expand Down Expand Up @@ -1669,7 +1669,18 @@ fn fn_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::PolyFnSig
x => {
bug!("unexpected sort of node in fn_sig(): {:?}", x);
}
};

let attrs = tcx.codegen_fn_attrs(def_id);
if !attrs.target_features.is_empty() {
if sig.unsafety() == Unsafety::Normal {
let msg = "#[target_feature(..)] can only be applied to \
`unsafe` function";
let span = tcx.def_span(def_id);
tcx.sess.span_err(span, msg);
}
}
sig
}

fn impl_trait_ref<'a, 'tcx>(
Expand Down Expand Up @@ -2256,6 +2267,175 @@ fn predicates_from_bound<'tcx>(
}
}

/// Returns the minimum required target-feature name to use a SIMD type on C FFI:
fn simd_ffi_min_target_feature(
target: &str, simd_width: usize, simd_elem_width: usize
) -> Option<&'static str> {
// FIXME: this needs to be architecture dependent and
// should probably belong somewhere else:
// * on mips: 16 => msa,
// * wasm: 16 => simd128
match target {
t if t.contains("x86") => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to deduplicate this block up here with the block down below?

match simd_width {
8 => Some("mmx"),
16 => Some("sse"),
32 => Some("avx"),
64 => Some("avx512f"),
_ => None,
}
},
t if t.contains("arm") => {
match simd_width {
// 32-bit arm does not support vectors with 64-bit wide elements
8 | 16 if simd_elem_width < 8 => Some("neon"),
_ => None,
}
},
t if t.contains("aarch64") => {
match simd_width {
8 | 16 => Some("neon"),
_ => None,
}
},
t if t.contains("powerpc") => {
match simd_width {
// 64-bit wide elements are only available in VSX:
16 if simd_elem_width == 8 => Some("vsx"),
16 if simd_elem_width < 8 => Some("altivec"),
_ => None,
}
},
t if t.contains("mips") => {
match simd_width {
16 => Some("msa"),
_ => None,
}
},
_ => None,
}
}

/// Returns true if the target-feature allows using the SIMD type on C FFI:
fn simd_ffi_feature_check(
target: &str, simd_width: usize, simd_elem_width: usize, feature: &'static str
) -> bool {
match target {
t if t.contains("x86") => {
// FIXME: see simd_ffi_min_target_feature
match simd_width {
8 => feature.contains("mmx")
|| feature.contains("sse")
|| feature.contains("ssse")
|| feature.contains("avx"),
16 => feature.contains("sse")
|| feature.contains("ssse")
|| feature.contains("avx"),
32 => feature.contains("avx"),
64 => feature.contains("avx512"),
_ => false,
}

},
t if t.contains("arm") => {
match simd_width {
// 32-bit arm does not support vectors with 64-bit wide elements
8 | 16 if simd_elem_width < 8 => feature.contains("neon"),
_ => false,
}
},
t if t.contains("aarch64") => {
match simd_width {
8 | 16 => feature.contains("neon"),
_ => false,
}
},
t if t.contains("powerpc") => {
match simd_width {
// 64-bit wide elements are only available in VSX:
16 if simd_elem_width == 8 => feature.contains("vsx"),
16 if simd_elem_width < 8 => feature.contains("altivec"),
_ => false,
}
},
t if t.contains("mips") => {
match simd_width {
16 => feature.contains("msa"),
_ => false,
}
},
_ => false,
}
}

fn simd_ffi_check<'a, 'tcx, 'b: 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, ast_ty: &hir::Ty, ty: Ty<'b>,
) {
if !ty.is_simd() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that this won't handle I think is:

#[repr(transparent)]
struct MyType(__m128i);

but is that perhaps best left for a future implementation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also not even sure if this is something we properly gate today

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, and no, we don't gate this today:

use std::arch::x86_64::__m128;

#[allow(improper_ctypes)]
extern "C" {
    //fn e(x: __m128); // ERROR
    pub fn a(x: A);
    pub fn b(x: B);
}

#[repr(transparent)] pub struct A(__m128);
#[repr(C)] pub struct B(__m128);

both of these are allowed on stable Rust, and both should be rejected.

Copy link
Contributor Author

@gnzlbg gnzlbg Mar 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also should gate foreign function definitions:

use std::arch::x86_64::__m128;

pub extern "C" fn foo(x: __m128) -> __m128 { x }

These also work on stable Rust.

I'll try to fix all of these issues as part of this PR. We can do a crater run afterwards, and see if we need to change any of these to warn instead of error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me!

return;
}

// The use of SIMD types in FFI is feature-gated:
if !tcx.features().simd_ffi {
tcx.sess
.struct_span_err(
ast_ty.span,
&format!(
"use of SIMD type `{}` in FFI is unstable",
tcx.hir().hir_to_pretty_string(ast_ty.hir_id)
),
)
.help("add #![feature(simd_ffi)] to the crate attributes to enable")
.emit();
return;
}

// If rustdoc, then we don't type check SIMD on FFI because rustdoc requires
// being able to compile a target, with features of other targets enabled
// (e.g. `x86+neon`, yikes).
if tcx.sess.opts.actually_rustdoc {
return;
}

let attrs = tcx.codegen_fn_attrs(def_id);

// Skip LLVM intrinsics:
if let Some(link_name) = attrs.link_name {
if link_name.as_str().get().starts_with("llvm.") {
return;
}
}

let features = &attrs.target_features;
let simd_len = tcx.layout_of(ty::ParamEnvAnd{
param_env: ty::ParamEnv::empty(),
value: ty,
}).unwrap().details.size.bytes() as usize;
let simd_elem_width = simd_len / ty.simd_size(tcx);
let target: &str = &tcx.sess.target.target.arch;
if !features.iter().any(|f| simd_ffi_feature_check(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this condition get inverted to return early to de-indent the error generation code?

target, simd_len, simd_elem_width, f.as_str().get())
) {
let type_str = tcx.hir().hir_to_pretty_string(ast_ty.hir_id);
let error_msg = if let Some(min_feature) = simd_ffi_min_target_feature(
target, simd_len, simd_elem_width
) {
format!(
"use of SIMD type `{}` in FFI requires `#[target_feature(enable = \"{}\")]`",
type_str, min_feature,
)
} else {
format!(
"use of SIMD type `{}` in FFI not supported by any target features",
type_str
)
};
tcx.sess
.struct_span_err(ast_ty.span, &error_msg)
.emit();
}
}

fn compute_sig_of_foreign_fn_decl<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
Expand All @@ -2269,32 +2449,16 @@ fn compute_sig_of_foreign_fn_decl<'a, 'tcx>(
};
let fty = AstConv::ty_of_fn(&ItemCtxt::new(tcx, def_id), unsafety, abi, decl);

// feature gate SIMD types in FFI, since I (huonw) am not sure the
// ABIs are handled at all correctly.
// Using SIMD types in FFI signatures requires the
// signature to have appropriate `#[target_feature]`s enabled.
if abi != abi::Abi::RustIntrinsic
&& abi != abi::Abi::PlatformIntrinsic
&& !tcx.features().simd_ffi
{
let check = |ast_ty: &hir::Ty, ty: Ty<'_>| {
if ty.is_simd() {
tcx.sess
.struct_span_err(
ast_ty.span,
&format!(
"use of SIMD type `{}` in FFI is highly experimental and \
may result in invalid code",
tcx.hir().hir_to_pretty_string(ast_ty.hir_id)
),
)
.help("add #![feature(simd_ffi)] to the crate attributes to enable")
.emit();
}
};
for (input, ty) in decl.inputs.iter().zip(*fty.inputs().skip_binder()) {
check(&input, ty)
simd_ffi_check(tcx, def_id, &input, ty)
}
if let hir::Return(ref ty) = decl.output {
check(&ty, *fty.output().skip_binder())
simd_ffi_check(tcx, def_id, &ty, *fty.output().skip_binder())
}
}

Expand Down Expand Up @@ -2488,11 +2652,6 @@ fn codegen_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> Codegen
codegen_fn_attrs.export_name = Some(s);
}
} else if attr.check_name("target_feature") {
if tcx.fn_sig(id).unsafety() == Unsafety::Normal {
let msg = "#[target_feature(..)] can only be applied to \
`unsafe` function";
tcx.sess.span_err(attr.span, msg);
}
from_target_feature(
tcx,
id,
Expand Down
2 changes: 1 addition & 1 deletion src/llvm-project
25 changes: 25 additions & 0 deletions src/test/codegen/target-feature-on-foreign-function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ignore-emscripten
// ignore-s390x
// ignore-powerpc
// ignore-powerpc64
// ignore-powerpc64le
// ignore-sparc
// ignore-sparc64
// ignore-mips
// ignore-mips64
// ignore-aarch64
// ignore-arm
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think nowadays we have // only-x86


// compile-flags: -C no-prepopulate-passes

#![crate_type = "lib"]

pub unsafe fn bar() { foo() }

extern "C" {
// CHECK-LABEL: declare void @foo()
// CHECK-SAME: [[ATTRS:#[0-9]+]]
// CHECK-DAG: attributes [[ATTRS]] = { {{.*}}"target-features"="+avx2"{{.*}} }
#[target_feature(enable = "avx2")]
pub fn foo();
}
4 changes: 2 additions & 2 deletions src/test/ui/feature-gates/feature-gate-simd-ffi.stderr
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
error: use of SIMD type `LocalSimd` in FFI is highly experimental and may result in invalid code
error: use of SIMD type `LocalSimd` in FFI is unstable
--> $DIR/feature-gate-simd-ffi.rs:9:17
|
LL | fn baz() -> LocalSimd;
| ^^^^^^^^^
|
= help: add #![feature(simd_ffi)] to the crate attributes to enable

error: use of SIMD type `LocalSimd` in FFI is highly experimental and may result in invalid code
error: use of SIMD type `LocalSimd` in FFI is unstable
--> $DIR/feature-gate-simd-ffi.rs:10:15
|
LL | fn qux(x: LocalSimd);
Expand Down
1 change: 0 additions & 1 deletion src/test/ui/issues/issue-49851/compiler-builtins-error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,3 @@
#![no_std]

extern crate cortex_m;

57 changes: 57 additions & 0 deletions src/test/ui/simd-ffi-aarch64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// ignore-tidy-linelength
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move all tests that relate to simd_ffi into: ui/rfc-2574-simd-ffi/$file.

// ignore-s390x
// ignore-emscripten
// ignore-powerpc
// ignore-powerpc64
// ignore-powerpc64le
// ignore-sparc
// ignore-sparc64
// ignore-mips
// ignore-mips64
// ignore-arm
// ignore-x86
// ignore-x86_64

#![feature(repr_simd)]
#![feature(simd_ffi)]
#![allow(non_camel_case_types)]
#![cfg(target_arch = "aarch64")]

#[repr(simd)]
struct v1024(i128, i128, i128, i128, i128, i128, i128, i128);

extern {
fn foo(x: v1024); //~ ERROR use of SIMD type `v1024` in FFI not supported by any target features
fn bar(x: i32, y: v1024); //~ ERROR use of SIMD type `v1024` in FFI not supported by any target features
fn baz(x: i32) -> v1024; //~ ERROR use of SIMD type `v1024` in FFI not supported by any target features

fn qux_fail(x: v64); //~ ERROR use of SIMD type `v64` in FFI requires `#[target_feature(enable = "neon")]`
#[target_feature(enable = "neon")]
fn qux(x: v64);

fn quux_fail(x: v64i); //~ ERROR use of SIMD type `v64i` in FFI requires `#[target_feature(enable = "neon")]`
#[target_feature(enable = "neon")]
fn quux(x: v64i);

fn quuux_fail(x: v128); //~ ERROR use of SIMD type `v128` in FFI requires `#[target_feature(enable = "neon")]`
#[target_feature(enable = "neon")]
fn quuux(x: v128);

fn quuuux_fail(x: v128i); //~ ERROR use of SIMD type `v128i` in FFI requires `#[target_feature(enable = "neon")]`
#[target_feature(enable = "neon")]
fn quuuux(x: v128); //~ ERROR use of SIMD type `v128i` in FFI not supported by any target features
}

fn main() {}

#[repr(simd)]
struct v128(i32, i32, i32, i32);

#[repr(simd)]
struct v64(i32, i32);

#[repr(simd)]
struct v128i(i64, i64);

#[repr(simd)]
struct v64i(i64);
1 change: 1 addition & 0 deletions src/test/ui/simd-ffi-aarch64.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a
Loading