Skip to content

Commit f387030

Browse files
committed
Implement SIMD FFI checks
1 parent 4a22c0a commit f387030

File tree

4 files changed

+203
-31
lines changed

4 files changed

+203
-31
lines changed

compiler/rustc_typeck/src/collect.rs

Lines changed: 172 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2479,43 +2479,187 @@ fn compute_sig_of_foreign_fn_decl<'tcx>(
24792479
None,
24802480
);
24812481

2482-
// Feature gate SIMD types in FFI, since I am not sure that the
2483-
// ABIs are handled at all correctly. -huonw
2484-
if abi != abi::Abi::RustIntrinsic
2485-
&& abi != abi::Abi::PlatformIntrinsic
2486-
&& !tcx.features().simd_ffi
2487-
{
2488-
let check = |ast_ty: &hir::Ty<'_>, ty: Ty<'_>| {
2489-
if ty.is_simd() {
2490-
let snip = tcx
2491-
.sess
2492-
.source_map()
2493-
.span_to_snippet(ast_ty.span)
2494-
.map_or_else(|_| String::new(), |s| format!(" `{}`", s));
2495-
tcx.sess
2496-
.struct_span_err(
2497-
ast_ty.span,
2498-
&format!(
2499-
"use of SIMD type{} in FFI is highly experimental and \
2500-
may result in invalid code",
2501-
snip
2502-
),
2503-
)
2504-
.help("add `#![feature(simd_ffi)]` to the crate attributes to enable")
2505-
.emit();
2506-
}
2507-
};
2482+
// Using SIMD types in FFI signatures requires the signature
2483+
// to have appropriate `#[target_feature]`'s enabled.
2484+
if abi != abi::Abi::RustIntrinsic && abi != abi::Abi::PlatformIntrinsic {
25082485
for (input, ty) in iter::zip(decl.inputs, fty.inputs().skip_binder()) {
2509-
check(&input, ty)
2486+
simd_ffi_check(tcx, def_id, &input, ty)
25102487
}
25112488
if let hir::FnRetTy::Return(ref ty) = decl.output {
2512-
check(&ty, fty.output().skip_binder())
2489+
simd_ffi_check(tcx, def_id, &ty, fty.output().skip_binder())
25132490
}
25142491
}
25152492

25162493
fty
25172494
}
25182495

2496+
/// Returns `Ok()` if the target-feature allows using the SIMD type on C FFI.
2497+
/// Otherwise, returns `Err(Some())` if the target_feature needs to be enabled or
2498+
/// or `Err(None)` if it's unsupported.
2499+
fn simd_ffi_feature_check(
2500+
target: &str,
2501+
simd_width: u64,
2502+
simd_elem_width: u64,
2503+
feature: String,
2504+
) -> Result<(), Option<&'static str>> {
2505+
match target {
2506+
t if t.contains("x86") => {
2507+
// FIXME: this needs to be architecture dependent and
2508+
// should probably belong somewhere else:
2509+
// * on mips: 16 => msa,
2510+
// * wasm: 16 => simd128
2511+
match simd_width {
2512+
8 if feature.contains("mmx")
2513+
|| feature.contains("sse")
2514+
|| feature.contains("ssse")
2515+
|| feature.contains("avx") =>
2516+
{
2517+
Ok(())
2518+
}
2519+
8 => Err(Some("mmx")),
2520+
16 if feature.contains("sse")
2521+
|| feature.contains("ssse")
2522+
|| feature.contains("avx") =>
2523+
{
2524+
Ok(())
2525+
}
2526+
16 => Err(Some("sse")),
2527+
32 if feature.contains("avx") => Ok(()),
2528+
32 => Err(Some("avx")),
2529+
64 if feature.contains("avx512") => Ok(()),
2530+
64 => Err(Some("acx512")),
2531+
_ => Err(None),
2532+
}
2533+
}
2534+
t if t.contains("arm") => {
2535+
match simd_width {
2536+
// 32-bit arm does not support vectors with 64-bit wide elements
2537+
8 | 16 if simd_elem_width < 8 => {
2538+
if feature.contains("neon") {
2539+
Ok(())
2540+
} else {
2541+
Err(Some("neon"))
2542+
}
2543+
}
2544+
_ => Err(None),
2545+
}
2546+
}
2547+
t if t.contains("aarch64") => match simd_width {
2548+
8 | 16 => {
2549+
if feature.contains("neon") {
2550+
Ok(())
2551+
} else {
2552+
Err(Some("neon"))
2553+
}
2554+
}
2555+
_ => Err(None),
2556+
},
2557+
t if t.contains("powerpc") => {
2558+
match simd_width {
2559+
// 64-bit wide elements are only available in VSX:
2560+
16 if simd_elem_width == 8 => {
2561+
if feature.contains("vsx") {
2562+
Ok(())
2563+
} else {
2564+
Err(Some("vsx"))
2565+
}
2566+
}
2567+
16 if simd_elem_width < 8 => {
2568+
if feature.contains("altivec") {
2569+
Ok(())
2570+
} else {
2571+
Err(Some("altivec"))
2572+
}
2573+
}
2574+
_ => Err(None),
2575+
}
2576+
}
2577+
t if t.contains("mips") => match simd_width {
2578+
16 => {
2579+
if feature.contains("msa") {
2580+
Ok(())
2581+
} else {
2582+
Err(Some("msa"))
2583+
}
2584+
}
2585+
_ => Err(None),
2586+
},
2587+
_ => Err(None),
2588+
}
2589+
}
2590+
2591+
fn simd_ffi_check<'tcx>(
2592+
tcx: TyCtxt<'tcx>,
2593+
def_id: DefId,
2594+
ast_ty: &hir::Ty<'_>,
2595+
ty: Ty<'tcx>,
2596+
) {
2597+
if !ty.is_simd() {
2598+
return;
2599+
}
2600+
2601+
// The use of SIMD types in FFI is feature-gated:
2602+
if !tcx.features().simd_ffi {
2603+
tcx.sess
2604+
.struct_span_err(
2605+
ast_ty.span,
2606+
&format!(
2607+
"use of SIMD type `{}` in FFI is unstable",
2608+
tcx.hir().node_to_string(ast_ty.hir_id)
2609+
),
2610+
)
2611+
.help("add #![feature(simd_ffi)] to the crate attributes to enable")
2612+
.emit();
2613+
return;
2614+
}
2615+
2616+
// If rustdoc, then we don't type check SIMD on FFI because rustdoc requires
2617+
// being able to compile a target, with features of other targets enabled
2618+
// (e.g. `x86+neon`, yikes).
2619+
if tcx.sess.opts.actually_rustdoc {
2620+
return;
2621+
}
2622+
2623+
let attrs = tcx.codegen_fn_attrs(def_id);
2624+
2625+
// Skip LLVM intrinsics.
2626+
if let Some(link_name) = attrs.link_name {
2627+
if link_name.to_ident_string().starts_with("llvm.") {
2628+
return;
2629+
}
2630+
}
2631+
2632+
let features = &attrs.target_features;
2633+
let simd_len = tcx
2634+
.layout_of(ty::ParamEnvAnd { param_env: ty::ParamEnv::empty(), value: ty })
2635+
.unwrap()
2636+
.layout
2637+
.size
2638+
.bytes();
2639+
let (simd_size, _) = ty.simd_size_and_type(tcx);
2640+
let simd_elem_width = simd_len / simd_size;
2641+
let target: &str = &tcx.sess.target.arch;
2642+
2643+
for f in features {
2644+
if let Err(v) = simd_ffi_feature_check(target, simd_len, simd_elem_width, f.to_ident_string()) {
2645+
let type_str = tcx.hir().node_to_string(ast_ty.hir_id);
2646+
let msg = if let Some(f) = v {
2647+
format!(
2648+
"use of SIMD type `{}` in FFI requires `#[target_feature(enable = \"{}\")]`",
2649+
type_str, f,
2650+
)
2651+
} else {
2652+
format!(
2653+
"use of SIMD type `{}` in FFI not supported by any target features",
2654+
type_str
2655+
)
2656+
};
2657+
tcx.sess.struct_span_err(ast_ty.span, &msg).emit();
2658+
return;
2659+
}
2660+
}
2661+
}
2662+
25192663
fn is_foreign_item(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
25202664
match tcx.hir().get_if_local(def_id) {
25212665
Some(Node::ForeignItem(..)) => true,

src/test/ui/rfcs/rfc-2574-simd-ffi/simd-ffi-x86.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// only-x86
1+
// only-x86_64
22

33
#![feature(repr_simd)]
44
#![feature(simd_ffi)]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0391]: cycle detected when computing codegen attributes of `qux`
2+
--> $DIR/simd-ffi-x86.rs:19:5
3+
|
4+
LL | fn qux(x: v128);
5+
| ^^^^^^^^^^^^^^^^
6+
|
7+
note: ...which requires computing function signature of `qux`...
8+
--> $DIR/simd-ffi-x86.rs:19:5
9+
|
10+
LL | fn qux(x: v128);
11+
| ^^^^^^^^^^^^^^^^
12+
= note: ...which again requires computing codegen attributes of `qux`, completing the cycle
13+
note: cycle used when checking attributes in top-level module
14+
--> $DIR/simd-ffi-x86.rs:3:1
15+
|
16+
LL | / #![feature(repr_simd)]
17+
LL | | #![feature(simd_ffi)]
18+
LL | | #![feature(avx512_target_feature)]
19+
LL | | #![allow(non_camel_case_types)]
20+
... |
21+
LL | | #[repr(simd)]
22+
LL | | struct v512(i128, i128, i128, i128);
23+
| |____________________________________^
24+
25+
error: aborting due to previous error
26+
27+
For more information about this error, try `rustc --explain E0391`.

src/test/ui/rfcs/rfc-2574-simd-ffi/target-feature-on-foreign-function.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
// only-x86
2-
// compile-flags: -C no-prepopulate-passes
1+
// only-x86_64
2+
// run-pass
3+
// compile-flags: -C no-prepopulate-passes -C passes=name-anon-globals
34

45
#![crate_type = "lib"]
56

0 commit comments

Comments
 (0)