-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Changes from all commits
7834abd
685b46a
f8b1944
b69935d
db932d8
08b0f76
1d34fbe
83e9e1e
8477a04
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
||
|
@@ -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, _), | ||
.. | ||
|
@@ -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>( | ||
|
@@ -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") => { | ||
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() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
|
@@ -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()) | ||
} | ||
} | ||
|
||
|
@@ -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, | ||
|
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think nowadays we have |
||
|
||
// 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(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,4 +8,3 @@ | |
#![no_std] | ||
|
||
extern crate cortex_m; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// ignore-tidy-linelength | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please move all tests that relate to |
||
// 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); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
a |
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.
Would it be possible to deduplicate this block up here with the block down below?