Files
Jonathan Brouwer 8e9fb78173 Rollup merge of #150732 - kulst:cpu_is_target_modifier, r=bjorn3
Convert `-Ctarget-cpu` into a target-modifier for AVR, AMDGCN and NVPTX

Crates built for AVR, AMDGCN and NVPTX that specify different values for `-Ctarget-cpu` cannot be soundly linked together.
This PR attempts to make `rustc` ensure that no crates with disagreeing values for `-Ctarget-cpu` are linked together. This is achieved by converting `-Ctarget-cpu` into a target-modifier depending on `--target`.
To do this, the consistency check for `-Ctarget-cpu` considers mismatching values as inconsistent only for targets for which the new flag `requires_consistent_cpu` is set in their target spec.

**Why should `-Ctarget-cpu` be a target-modifier for `nvptx`?**
<details><summary>PTX is a single-module contract</summary>
<p>
PTX requires a binary to start with .version (`ptx$$`) then .target (`sm_$$`). If the ptx contains instructions that are not supported by either .version or .target, the binary is ill-formed and will be rejected by ptxas. The concept of features that can be mixed and matched in a binary does not exist for nvptx and is therefore not supported by LLVM.
</p>
</details>

<details><summary>It prevents the production of bitcode that cannot be codegen'd after linking</summary>
<p>
A target modifier should prevent configurations that are not composable across crates when those crates are linked together. The most prominent example is when enabling a target feature changes the ABI, making cross-crate calls inherently unsound.

In the case of nvptx, ABI mismatch is (at least for now) not the core problem motivating target modifiers. NVIDIA’s documented PTX calling convention has [remained stable since ptx20](https://docs.nvidia.com/cuda/ptx-writers-guide-to-interoperability/index.html#function-calling-sequence).

However, in the current state it is possible to produce bitcode that cannot be codegen'd after linking, because some operations are only lowerable for sufficiently new SM/PTX levels. In the best case this results in an LLVM error during the final llc step, but this is not something we should rely on for correctness.

nvptx has a special compilation pipeline where instead of linking the final PTX object, instead LLVM bitcode is linked. The resulting artifact is then compiled in one invocation.
Now consider crate A which is independently compiled into bitcode with the following rustc arguments:

```Rust
//@ compile-flags: --target nvptx64-nvidia-cuda -C target-cpu=sm_70 --crate-type=rlib
#[cfg(target_feature = "sm_70")]
fn foo() {
    // cannot be lowered to ptx before "sm_70: so currently produces an LLVM error
}
#[cfg(not(target_feature = "sm_70"))]
fn foo() {
    // can be lowered to ptx before "sm_70"
}
pub fn bar() {
    foo()
}
```
Crate A is a dependency of crate B. In the *rustc* invocation of crate B
1. crate B is compiled into bitcode, too
2. both bitcode artifacts are bitcode-linked by *llvm-link*
3. the resulting bitcode artifact is compiled by *llc -mcpu=sm_60*

This should now ideally create an LLVM error, because the linked bitcode contains code paths that were selected under `sm_70` assumptions but the final NVPTX codegen is targeting `sm_60`, where those operations are not lowerable. An LLVM error here is better than silent miscompilation, but it’s not a promise we should rely on.

A real example where this could happen is the lowering of atomic loads and stores with non-relaxed orderings, which is known to depend on the selected SM level.
</p>
</details>

**Why should `-Ctarget-cpu` be a target-modifier for `amdgcn` and `avr`?**
- In case of AVR the target-cpu defines the ISA, which is encoded in the ELF header flags, amdgcn also encodes the cpu directly into those flags
- To not rely on *lld* which currently prevents it for both by looking at those flags [AVR](https://github.com/llvm/llvm-project/blob/597ffbe09d5f774f861ee55e50022bf84d7f98e2/lld/test/ELF/avr-flags.s) and [amdgcn](https://github.com/llvm/llvm-project/blob/597ffbe09d5f774f861ee55e50022bf84d7f98e2/lld/test/ELF/amdgpu-elf-flags-err.s)

Previous discussions about the topic can be found [here](https://github.com/rust-lang/rust/issues/131799#issuecomment-3390563329) and [here](https://github.com/rust-lang/rust/issues/141468).

I also created a [Zulip discussion](https://rust-lang.zulipchat.com/#narrow/channel/131828-t-compiler/topic/Making.20-Ctarget-cpu.20a.20target-modifier.20on.20NVPTX/with/566622679).

I am unsure if a MCP is needed before proceeding. If you think so please let me know.

Creating *target-modifiers* for NVPTX *target-features* is to be done in a follow-up.

cc @kjetilkjeka as target maintainer for NVPTX
cc @flakebi as target maintainer for amdgcn
cc @Patryk27 as target maintainer for AVR
cc @RalfJung you were very involved in the discussions so far

Target modifier tracking issue: https://github.com/rust-lang/rust/issues/136966
2026-07-19 23:26:25 +02:00
..