-
Notifications
You must be signed in to change notification settings - Fork 3
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
Portability checking and target_feature
#17
Comments
Sounds good to me.
So right now using fn foo() {
if is_x86_feature_detected!("avx") {
unsafe { avx_function() } // unsafe REQUIRED
} else {
fallback_function()
}
} The Removing the need for the fn foo() {
if is_x86_feature_detected!("avx") {
avx_function() // OK and sound
} else if is_x86_feature_detected("sse4.2") {
avx_function() // ERROR:
// ^ call to AVX function from non-AVX context is unsafe
} else if is_x86_feature_detected("sse3") {
unsafe { avx_function() } // OK, but portability warning
} else {
fallback_function()
}
} I would suppose that the intent of the portability lint is just to provide portability warnings, which I think is a good first step. In particular, those macros expand to a One thing we could do, would be to expand those macros to something like: fn foo() {
if is_x86_feature_detected!("avx") {
avx_function() // OK and sound
} else {
fallback_function()
}
}
fn foo_expanded() {
if std::arch::__detect(...) #[target_feature("avx")] {
avx_function() // OK and sound
} else {
fallback_function()
}
} Or similar that enables the feature statically for a block. I don't think we can do this change in a backwards compatible way because code like
fn foo() {
feature_match!() {
"avx" => avx_function(),
_ => fallback_function(),
}
}
fn foo_expanded() {
if is_x86_feature_detected("avx") {
#[target_feature(enable = "avx")] {
avx_function() // OK: can be statically verified
}
} else {
fallback_function(),
}
} So maybe we could try to pursue a fn foo() {
feature_match!() {
"avx" | "avx2" => avx_function(),
_ => fallback_function(),
}
}
fn foo_expanded() {
if is_x86_feature_detected("avx") {
#[cfg_attr(not(cfg(feature = "avx2")), target_feature(enable = "avx")]
#[cfg_attr(not(cfg(feature = "avx")), target_feature(enable = "avx2")]
{
avx_function() // OK: can be statically verified
}
} else {
fallback_function(),
}
} and similar things. |
|
I've mentioned here: rust-lang/rust#53069 (comment) an example that would be hard to check. That is: fn foo(x: bool) { // SSE2
avx(); // WARNING
if is_x86_feature_detected!("avx") {
avx(); // OK (NO WARNING)
}
let b = is_x86_feature_detected!("avx");
if b { avx(); } // OK (NO WARNING)
if x { avx() } // ??? MIGHT BE OK
} The problems I see are that here: if x { avx() } // ??? MIGHT BE OK the Also, here: let b = is_x86_feature_detected!("avx");
if b { avx(); } // OK (NO WARNING) we need to do a fairly complex analysis to determine that this piece of code is "ok". We would probably need to introduce a newer API that returns "better types" to be able to improve analysis quality for these cases. We just have to think that API through and make sure that it works correctly with the portability lint. |
Yeah any old |
|
The portability lint as planned just lints the code that did not get
cfg
'd away. But an extension (raised many time including by me in #8 (comment)) would have it be done on everything. This entails delaying the pruning ofcfg
-dead code until after name resolution, or even type checking.I always assumed this would be a long ways off, but I just realized something vary similar has been proposed with with inlining and
target_feature
. For the static#[target_feature]
, the inline rules are just a hard-error version of the compatibility lint, which is great! (I hope in a future epoch the compatibility lint can be made a hard error.). But forcfg!(target_feature)
, we have some very interesting things going on:cfg!(target_feature)
we can look at the portability per mode of the CFG instead of per item. We'd probably want a difference syntax but the short story is that different branches of anif cfg!(...)
have statically known portability, the dynamism is just the choice of edge. Forcfg_feature_enabled!
it's just the same except no branches must be pruned in the end.More broadly, the concept of compiling for multiple variations of a platforms neatly extends into compiling for multiple platforms.
#[target_feature]
is just#[cfg(target_feature ...)]
where thetarget_feature
is neither true nor false in all current platforms. There is very little conceptual overhead in extending this to interpreting arbitrarycfg
formulae over arbitrary sets of "active" (concurrently targeted) models. Given that one general system of "delayedcfg
resolution" can implement both features, I'd strongly consider planning that.As an aside, I guess we might as well track over areas of overlap between these two features in this issue too.
CC @gnzlbg
The text was updated successfully, but these errors were encountered: