Skip to content

Commit 016e9b5

Browse files
committed
Auto merge of #84988 - alexcrichton:safe-target-feature-wasm, r=joshtriplett
rustc: Allow safe #[target_feature] on wasm This commit updates the compiler's handling of the `#[target_feature]` attribute when applied to functions on WebAssembly-based targets. The compiler in general requires that any functions with `#[target_feature]` are marked as `unsafe` as well, but this commit relaxes the restriction for WebAssembly targets where the attribute can be applied to safe functions as well. The reason this is done is that the motivation for this feature of the compiler is not applicable for WebAssembly targets. In general the `#[target_feature]` attribute is used to enhance target CPU features enabled beyond the basic level for the rest of the compilation. If done improperly this means that your program could execute an instruction that the CPU you happen to be running on does not understand. This is considered undefined behavior where it is unknown what will happen (e.g. it's not a deterministic `SIGILL`). For WebAssembly, however, the target is different. It is not possible for a running WebAssembly program to execute an instruction that the engine does not understand. If this were the case then the program would not have validated in the first place and would not run at all. Even if this were allowed in some hypothetical future where engines have some form of runtime feature detection (which they do not right now) any implementation of such a feature would generate a trap if a module attempts to execute an instruction the module does not understand. This deterministic trap behavior would still not fall into the category of undefined behavior because the trap is deterministic. For these reasons the `#[target_feature]` attribute is now allowed on safe functions, but only for WebAssembly targets. This notably enables the wasm-SIMD intrinsics proposed for stabilization in #74372 to be marked as safe generally instead of today where they're all `unsafe` due to the historical implementation of `#[target_feature]` in the compiler.
2 parents 19579c6 + 7fed92b commit 016e9b5

File tree

4 files changed

+75
-8
lines changed

4 files changed

+75
-8
lines changed

compiler/rustc_mir/src/transform/check_unsafety.rs

+6
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,12 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
376376
/// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
377377
/// the called function has target features the calling function hasn't.
378378
fn check_target_features(&mut self, func_did: DefId) {
379+
// Unsafety isn't required on wasm targets. For more information see
380+
// the corresponding check in typeck/src/collect.rs
381+
if self.tcx.sess.target.options.is_like_wasm {
382+
return;
383+
}
384+
379385
let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
380386
let self_features = &self.tcx.codegen_fn_attrs(self.body_did).target_features;
381387

compiler/rustc_mir_build/src/check_unsafety.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,16 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
166166
self.requires_unsafe(expr.span, CallToUnsafeFunction);
167167
} else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
168168
// If the called function has target features the calling function hasn't,
169-
// the call requires `unsafe`.
170-
if !self
171-
.tcx
172-
.codegen_fn_attrs(func_did)
173-
.target_features
174-
.iter()
175-
.all(|feature| self.body_target_features.contains(feature))
169+
// the call requires `unsafe`. Don't check this on wasm
170+
// targets, though. For more information on wasm see the
171+
// is_like_wasm check in typeck/src/collect.rs
172+
if !self.tcx.sess.target.options.is_like_wasm
173+
&& !self
174+
.tcx
175+
.codegen_fn_attrs(func_did)
176+
.target_features
177+
.iter()
178+
.all(|feature| self.body_target_features.contains(feature))
176179
{
177180
self.requires_unsafe(expr.span, CallToFunctionWith);
178181
}

compiler/rustc_typeck/src/collect.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -2770,7 +2770,21 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
27702770
}
27712771
} else if tcx.sess.check_name(attr, sym::target_feature) {
27722772
if !tcx.is_closure(id) && tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
2773-
if !tcx.features().target_feature_11 {
2773+
if tcx.sess.target.is_like_wasm {
2774+
// The `#[target_feature]` attribute is allowed on
2775+
// WebAssembly targets on all functions, including safe
2776+
// ones. Other targets require that `#[target_feature]` is
2777+
// only applied to unsafe funtions (pending the
2778+
// `target_feature_11` feature) because on most targets
2779+
// execution of instructions that are not supported is
2780+
// considered undefined behavior. For WebAssembly which is a
2781+
// 100% safe target at execution time it's not possible to
2782+
// execute undefined instructions, and even if a future
2783+
// feature was added in some form for this it would be a
2784+
// deterministic trap. There is no undefined behavior when
2785+
// executing WebAssembly so `#[target_feature]` is allowed
2786+
// on safe functions (but again, only for WebAssembly)
2787+
} else if !tcx.features().target_feature_11 {
27742788
let mut err = feature_err(
27752789
&tcx.sess.parse_sess,
27762790
sym::target_feature_11,
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// only-wasm32
2+
// check-pass
3+
4+
#![feature(wasm_target_feature)]
5+
#![allow(dead_code)]
6+
7+
#[target_feature(enable = "nontrapping-fptoint")]
8+
fn foo() {}
9+
10+
#[target_feature(enable = "nontrapping-fptoint")]
11+
extern "C" fn bar() {}
12+
13+
trait A {
14+
fn foo();
15+
fn bar(&self);
16+
}
17+
18+
struct B;
19+
20+
impl B {
21+
#[target_feature(enable = "nontrapping-fptoint")]
22+
fn foo() {}
23+
#[target_feature(enable = "nontrapping-fptoint")]
24+
fn bar(&self) {}
25+
}
26+
27+
impl A for B {
28+
#[target_feature(enable = "nontrapping-fptoint")]
29+
fn foo() {}
30+
#[target_feature(enable = "nontrapping-fptoint")]
31+
fn bar(&self) {}
32+
}
33+
34+
fn no_features_enabled_on_this_function() {
35+
bar();
36+
foo();
37+
B.bar();
38+
B::foo();
39+
<B as A>::foo();
40+
<B as A>::bar(&B);
41+
}
42+
43+
#[target_feature(enable = "nontrapping-fptoint")]
44+
fn main() {}

0 commit comments

Comments
 (0)