Why are forbidden target features disallowed in cfg(target_feature)?

As per title, I was looking at how to add missing target features and I noticed that there isn't a way of adding them without also making them togglable.

Looking at target_features.rs features are either stable/unstable or forbidden, forbidden is reserved for features which cannot be enabled safely without breaking something (features that change ABI for example).

Could this be relaxed to allow for features which might break when enabled, but that are at least queryable through cfg(target_feature = "...")?

2 Likes

I've wanted something like this to handle the ptx* features for NVPTX. These define the ISA and thus can't be enabled using #[target_feature(enable = "ptx80")], but they need to be settable using -Ctarget-feature=+ptx80 (the primary effect of which is to imply a minimum required driver). Anyway, :+1: to providing some nuance here. In my case, for features that are global, not per-function.

I think this is a question for @RalfJung who was involved in the whole ABI breaking target features system, but I haven't see them on this forum recently. Hopefully the mention will help direct this towards the right people.

In fact I saw this thread in the weekly summary Discourse sends just before you pinged me. :wink:

The answer to the immediate question "why do forbidden target features not show up in cfg" is that when we introduced the concept of forbidden target features, we did not want to expose anything new on stable, so we didn't want the target features that we now marked as forbidden to show up in cfg. Most of them previously had not been in Rust's target feature list at all.

But there's a broader question here, about what to do with target features that cause ABI incompatibility. If e.g. ptx80 is a forbidden target feature, it is also forbidden in -Ctarget-feature=+ptx80, so there's no way to set it from rustc at all. This is because we explicitly support mixing code built with different -Ctarget-feature in the same binary -- in particular, if you set RUSTFLAGS=-Ctarget-feature=foo, that does not apply to the standard library, so it is crucial that linking code with and without that flag is sound.

I think LLVM target features like ptx80 should not be Rust target features at all. They should become a new thing that works under the umbrella of target modifiers, which ensures that the compiler will detect and prevent accidentally mixing code with incompatible target modifiers. -Ctarget-features should only be used for those target features that can be soundly toggled within a single binary.

However, there's also the option of saying that some target features are known to actually be "target modifiers" can thus are subject to the target modifier consistency check, but otherwise they use the same interface as normal target features regarding -Ctarget-feature and cfg(target_feature). That means we don't have to invent a new term, but it also could be easily confusing to mix two very different kind of flags ("locally enable some ISA extensions" vs "make a global choice for the entire binary") in the same -C option. LLVM chose to make them both target features, but arguably that's a hack.

Cc @alice with whom I also briefly discussed this last week.

2 Likes

There is an a argument to be made that you should be able to query the state of these forbidden modifiers, even if you can't turn them on or off. Same as you can query the target architecture or the OS, you should be able to query other things set by the target spec. Otherwise people will start querying for specific triplets as a proxy, and that risks getting out of date and out of sync.

It is always better to measure the thing of interest instead of a proxy for the thing of interest.

1 Like

Oh, absolutely. But if we end up toggling them with -Ctarget-modifier=foo, they should probably be queried with cfg(target_modifier), not cfg(target_feature).

That's exactly the original intent of my question! My specific case is that I was working on adding MIPS support in unwinding for a personal project. The problem is that MIPS has a lot of flags and configurations regarding floating point support, some target do not support them, some targets support double precision, some targets even support double precision despite not having 64bit registers (which if you are wondering, the presence of 64bit FPU registers is the only feature currently exposed by rustc, so right now you can't even query double precision support properly).

Also, another point to make is that these are specifically LLVM target features, I've never interacted much with the internals teams, but to me it seems that rustc is increasingly distancing itself from LLVM, so it might not be the wisest choice to expose them verbatim?

1 Like