Skip to content

Convert -Ctarget-cpu into a target-modifier for AVR, AMDGCN and NVPTX #150732

Open
kulst wants to merge 2 commits into
rust-lang:mainfrom
kulst:cpu_is_target_modifier
Open

Convert -Ctarget-cpu into a target-modifier for AVR, AMDGCN and NVPTX #150732
kulst wants to merge 2 commits into
rust-lang:mainfrom
kulst:cpu_is_target_modifier

Conversation

@kulst

@kulst kulst commented Jan 6, 2026

Copy link
Copy Markdown
Contributor

View all comments

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?

PTX is a single-module contract

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.

It prevents the production of bitcode that cannot be codegen'd after linking

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.

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:

//@ 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.

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 and amdgcn

Previous discussions about the topic can be found here and here.

I also created a Zulip discussion.

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: #136966

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jan 6, 2026
@kulst kulst changed the title Making -Ctarget-cpu a target-modifier on NVPTX Make -Ctarget-cpu a target-modifier on NVPTX Jan 6, 2026
@kulst

kulst commented Jan 6, 2026

Copy link
Copy Markdown
Contributor Author

@rustbot label: +O-NVPTX

@rustbot rustbot added the O-NVPTX Target: the NVPTX LLVM backend for running rust on GPUs, https://llvm.org/docs/NVPTXUsage.html label Jan 6, 2026
@kulst kulst changed the title Make -Ctarget-cpu a target-modifier on NVPTX Convert -Ctarget-cpu into a target-modifier on NVPTX Jan 7, 2026
@kulst kulst changed the title Convert -Ctarget-cpu into a target-modifier on NVPTX Convert -Ctarget-cpu into a target-modifier for NVPTX Jan 7, 2026
@kulst kulst force-pushed the cpu_is_target_modifier branch from 7164067 to a8f5e96 Compare January 7, 2026 21:26
@kulst kulst marked this pull request as ready for review January 7, 2026 21:34
@rustbot

rustbot commented Jan 7, 2026

Copy link
Copy Markdown
Collaborator

Some changes occurred in src/doc/rustc/src/platform-support

cc @Noratrieb

These commits modify compiler targets.
(See the Target Tier Policy.)

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 7, 2026
@rustbot rustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Jan 7, 2026
@rustbot

rustbot commented Jan 7, 2026

Copy link
Copy Markdown
Collaborator

r? @nnethercote

rustbot has assigned @nnethercote.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@kulst

kulst commented Jan 7, 2026

Copy link
Copy Markdown
Contributor Author

r? @bjorn3 as you suggested this change.

@rustbot rustbot assigned bjorn3 and unassigned nnethercote Jan 7, 2026
Comment thread compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs Outdated
@kjetilkjeka

Copy link
Copy Markdown
Contributor

It works out really nice with -CTarget-cpu being able to be a target modifier.

It would be nice to know what the plan was with the ptx isa version as well. I asked that question here

@RalfJung

RalfJung commented Jan 8, 2026

Copy link
Copy Markdown
Member

I can't vouch for the implementation, but the approach sounds good. :)

Cc @Darksonn

@kulst

kulst commented Jan 10, 2026

Copy link
Copy Markdown
Contributor Author

I have added some explanation of why -Ctarget-cpu should be a target-modifier for nvptx to the PR. Please let me know if something is missing or if there are other assumptions about the topic.

@Darksonn Darksonn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There might already be relevant tests, but I want to see tests that this only makes -Ctarget-cpu into a target modifier in this case, and not for all targets.

View changes since this review

@Darksonn Darksonn added the F-target_modifiers `#![feature(target_modifiers)]` label Jan 10, 2026
Comment thread compiler/rustc_session/src/options.rs Outdated
Comment on lines +128 to +137
pub(super) fn target_cpu(
sess: &Session,
l: &TargetModifier,
r: Option<&TargetModifier>,
) -> bool {
if sess.target.cpu_is_target_modifier {
if let Some(r) = r {
return l.extend().tech_value == r.extend().tech_value;
} else {
return false;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if -Ctarget-cpu appears multiple times on the command line? Please add a test.

@kulst kulst Jan 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value of the last appearance in matches is used. In practice this is probably the value of -Ctarget-cpus last appearance in the command line arguments of the rustc invocation.

Will add a test soon, but I am asking myself what is the intended behavior? I assume we should error if a target-modifier appears more than once, would you agree?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the very least we have to ensure that whatever values rustc actually uses and what is recorded as target modifier are the same.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last argument taking precedence is the desired functionality:
Corresponding MCP

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Behavior as per MCP is fine. Whichever value ends up being used should be tracked and verified.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is checked now by tests/run-make/target-cpu-is-target-modifier/rmake.rs
Does that match what you had in mind?

Comment thread tests/ui/target_modifiers/incompatible_target_cpu.error_generated.stderr Outdated
@kulst kulst force-pushed the cpu_is_target_modifier branch from a8f5e96 to e5cbf49 Compare January 11, 2026 00:43
@rustbot

rustbot commented Jan 11, 2026

Copy link
Copy Markdown
Collaborator

Some changes occurred to the intrinsics. Make sure the CTFE / Miri interpreter
gets adapted for the changes, if necessary.

cc @rust-lang/miri, @RalfJung, @oli-obk, @lcnr

The reflection data structures are tied exactly to the implementation
in the compiler. Make sure to also adjust `rustc_const_eval/src/const_eval/type_info.rs

cc @oli-obk

triagebot.toml has been modified, there may have been changes to the review queue.

cc @davidtwco, @wesleywiser

Some changes occurred in tests/ui/stack-protector

cc @rust-lang/project-exploit-mitigations, @rcvalle

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

@rustbot rustbot added A-CI Area: Our Github Actions CI A-meta Area: Issues & PRs about the rust-lang/rust repository itself labels Jan 11, 2026
@kulst

kulst commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

I have updated the PR and rebased it on top of the current main branch.

The PR is now split into two commits:

  1. The first commit improves the readability of error messages for target modifiers in general.
  2. The second commit contains the functional change that makes -Ctarget-cpu a target modifier which depends on --target.

For this to work, I added a new target-spec flag, requires_consistent_cpu, separate from need_explicit_cpu.

When requires_consistent_cpu is set, -Ctarget-cpu is evaluated as a target modifier. This means it must be consistent across all crates that are linked together. By contrast, need_explicit_cpu only requires each compilation to specify a CPU explicitly.

requires_consistent_cpu is enabled for AVR, AMDGCN, and NVPTX. need_explicit_cpu remains disabled for NVPTX because NVPTX has a usable default CPU, sm_70, while AVR and AMDGCN do not have similarly suitable defaults.

The reason for keeping the flags separate is to preserve the ability to compile for NVPTX with a stable compiler. Since NVPTX is a Tier 2 target, its sysroot crates are built in CI using the target’s default CPU implicitly. The consistency check treats an omitted -Ctarget-cpu as equivalent to explicitly specifying the target’s default CPU. Therefore, on stable, NVPTX code can still be compiled either by omitting -Ctarget-cpu or by specifying -Ctarget-cpu=sm_70.

Since sm_70 is no family- or architecture-specific SM-variant, it can run on later SM architectures as well. Also, producing kernel code already requires at least one unstable feature, so this restriction should not be too invasive in practice.

requires_consistent_cpu is incompatible with -Ctarget-cpu=native, since the consistency check compares the literal values and not the resolved CPU. To address this, the patch adds an early check for this case and excludes native from the target-cpus list for targets that set requires_consistent_cpu.

The PR adds tests covering the following cases:

  • An omitted -Ctarget-cpu compares equal to an explicitly specified default CPU.
  • native is rejected for targets that set requires_consistent_cpu.
  • native is included in the host target’s target-cpus list when requires_consistent_cpu is false or absent, and omitted when it is true.
  • The last occurrence of -Ctarget-cpu is used both for target-modifier comparison and for LLVM IR emission.

Additional tests explicitly cover that -Ctarget-cpu is a target-dependent target modifier:

  • requires-consistent-cpu is only set to true in target-spec JSON for AVR, AMDGCN, and NVPTX.
  • The target-spec JSON for x86_64-unknown-linux-gnu omits requires-consistent-cpu, or sets it to false; the target accepts mismatching -Ctarget-cpu values across crates.
  • Using the same target-spec JSON such mismatches are rejected when requires-consistent-cpu is set to true.

This does not fully prove that all other targets avoid treating -Ctarget-cpu as a target modifier, but it should catch the most likely regressions. This test setup also avoids failures caused by unrelated issues such as LLVM target-machine errors.

@rustbot label -A-CI -A-meta -A-testsuite -T-infra
@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed A-CI Area: Our Github Actions CI A-testsuite Area: The testsuite used to check the correctness of rustc A-meta Area: Issues & PRs about the rust-lang/rust repository itself T-infra Relevant to the infrastructure team, which will review and decide on the PR/issue. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jun 18, 2026
@RalfJung

Copy link
Copy Markdown
Member

I added a new target-spec flag, requires_consistent_cpu, separate from need_explicit_cpu.

Can you add a hack in Target::check_consistency that if need_explicit_cpu is set then requires_consistent_cpu is also set?

Comment thread compiler/rustc_interface/src/util.rs Outdated
// modifier. To not accidentally link two crates built with different `native`
// cpus we reject `native` completely.
if sess.target.requires_consistent_cpu
&& sess.opts.cg.target_cpu.as_deref().unwrap_or(sess.target.cpu.as_ref()) == "native"

@RalfJung RalfJung Jun 25, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of adding a separate out-of-band check, can you insert this check in the place where we are actually handling "native"? Currently you are effectively "parsing" target_cpu twice, that's generally a bad idea.

View changes since the review

@kulst kulst Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"native" is currently handled separately in each codegen backend. Performing the check there would result in duplication.

Instead, I moved the parsing into its own function: rustc_session::config::is_native_cpu. It is called by all codegen backends and also by the new check.
The check itself now lives in rustc_session::session::validate_commandline_args_with_session_available, which seems more appropriate.

Is that in line with what you had in mind?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"native" is currently handled separately in each codegen backend.

Ah, bummer. Well I guess the code already is a mess so you're not making it significantly worse. ;)

@bjorn3 bjorn3 added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 30, 2026
@kulst kulst force-pushed the cpu_is_target_modifier branch from 3c8a444 to e5a16e9 Compare July 1, 2026 17:38
@rustbot

rustbot commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

rustc_codegen_cranelift is developed in its own repository. If possible, consider making this change to rust-lang/rustc_codegen_cranelift instead.

cc @bjorn3

rustc_codegen_gcc is developed in its own repository. If possible, consider making this change to rust-lang/rustc_codegen_gcc instead.

cc @antoyo, @GuillaumeGomez

@rustbot

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@kulst kulst force-pushed the cpu_is_target_modifier branch from e5a16e9 to 02d2121 Compare July 1, 2026 19:30
@kulst

kulst commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

I've made the requested changes and rebased on top of the current main branch.
@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jul 1, 2026
@rust-bors

This comment has been minimized.

@kulst kulst force-pushed the cpu_is_target_modifier branch from 02d2121 to 6736669 Compare July 14, 2026 08:56
@rustbot

rustbot commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@rust-log-analyzer

This comment has been minimized.

pub flag_name: String,
pub flag_name_prefixed: String,
pub extern_value: String,
pub value_len: usize,

@bjorn3 bjorn3 Jul 14, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe do extern_value: Option<String> if fluent supports that? Otherwise has_extern_value: bool.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a boolean works. I wasn't aware of this, since it does not work on the fluent playground.

Comment thread compiler/rustc_session/src/config.rs Outdated
/// Returns whether `cpu_name` requests the host CPU.
pub fn is_native_cpu(cpu_name: &str) -> bool {
cpu_name == "native"
}

@bjorn3 bjorn3 Jul 14, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this is an improvement. If you want to avoid duplicating the "native" string, adding a const for it would also be an option.

View changes since the review

// must succeed.
//
// It then repeats the same compilation using a copy of that target spec with
// `requires-consistent-cpu` set to true. This time the compilation must fail.

@bjorn3 bjorn3 Jul 14, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires a //@ needs-llvm-component: x86 I think given that it forces x86_64 as target arch.

View changes since the review

use serde_json::{Value, json, to_string};

fn main() {
let target = "x86_64-unknown-linux-gnu";

@bjorn3 bjorn3 Jul 14, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe using avr-none would be simpler? Would require skipping this test when AVR if LLVM is built without AVR support or if a different codegen backend without AVR support is used.

View changes since the review

@bjorn3 bjorn3 Jul 14, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this test as written is useful.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention was to verify, that for all targets other than avr, amdgcn and nvptx -Ctarget-cpu is not interpreted as a target modifier.

tests/run-make/requires-consistent-cpu-behavior/rmake.rs verifies that requires_consistent_cpu is the flag that controls the behavior and this test verifies that the flag is only set for the mentioned targets.

I found this to be more stable than my previous attempt:

  1. Querying rustc for its supported targets and for their target-cpus.
  2. Compiling for each target using two cpus from the queried list.
  3. Exactly the three targets must reject

It relied on the llvm target machine to be available.
But after giving it a second thought I could compile with --emit metadata and --Zcodegen-backend=dummy.
This should trigger the target modifier check but be way more stable.

}

fn is_native_cpu_line(line: &str) -> bool {
line.trim_start().starts_with("native ")

@bjorn3 bjorn3 Jul 14, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe

Suggested change
line.trim_start().starts_with("native ")
line.contains("native")

and inline it as closure?

View changes since the review

@kulst kulst Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inlining is a good idea, but I would like to stick to the previous string recognition: It would still work even if LLVM or other codegen backends decide to introduce a CPU name containing alternative or something similar.

Comment thread tests/run-make/target-specs/require-explicit-cpu.json

@bjorn3 bjorn3 Jul 14, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is testing behavior of the argument parser that is independent of -Ctarget-cpu. Feels a bit unnecessary.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention was for this test to verify that the last-mentioned CPU takes precedence as a target modifier, and for tests/codegen-llvm/target-cpu-precedence.rs to verify that it also takes precedence as the codegened target CPU. I was following this review, but perhaps I misinterpreted it?

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 14, 2026
@kulst kulst force-pushed the cpu_is_target_modifier branch from 6736669 to cf747da Compare July 14, 2026 09:39
kulst and others added 2 commits July 14, 2026 12:16
- Boolean target modifiers are now mentioned without a trailing `=` in the
messages.
- Wording improved for unset target modifiers.
…and NVPTX

For AVR, AMDGCN, and NVPTX, crates built with different target CPU values are
not generally link-compatible.

Add a `requires_consistent_cpu` flag to the target spec and enable it for these
targets. When the flag is set, treat `-Ctarget-cpu` as a target modifier and
require all linked crates to agree on its value.

Reject `-Ctarget-cpu=native` before codegen for targets that set
`requires_consistent_cpu` to true. Also do not include `native` in the printed
`target-cpus` list for such targets.

Add tests covering:
- which built-in targets set `requires-consistent-cpu`
- cross-crate behavior with and without `requires-consistent-cpu`
- that an omitted `-Ctarget-cpu` compares equal to an explicitly specified
  default CPU
- rejection and printing behavior for `native`
- precedence of repeated `-Ctarget-cpu` flags in metadata comparison and LLVM IR
@kulst kulst force-pushed the cpu_is_target_modifier branch from cf747da to e67d6fb Compare July 14, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. A-run-make Area: port run-make Makefiles to rmake.rs F-target_modifiers `#![feature(target_modifiers)]` O-amdgcn Target: the Radeon 9001XT and such O-AVR Target: AVR processors (ATtiny, ATmega, etc.) O-NVPTX Target: the NVPTX LLVM backend for running rust on GPUs, https://llvm.org/docs/NVPTXUsage.html PG-exploit-mitigations Project group: Exploit mitigations S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.