Skip to content

Conversation

@nagisa
Copy link
Member

@nagisa nagisa commented Jan 13, 2026

This is the conceptual opposite of the rust-cold calling convention and is particularly useful in combination with the new explicit_tail_calls feature.

For relatively tight loops implemented with tail calling (become) each of the function with the regular calling convention is still responsible for restoring the initial value of the preserved registers. So it is not unusual to end up with a situation where each step in the tail call loop is spilling and reloading registers, along the lines of:

foo:
    push r12
    ; do things
    pop r12
    jmp next_step

This adds up quickly, especially when most of the clobberable registers are already used to pass arguments or other uses.

I was thinking of making the name of this ABI a little less LLVM-derived and more like a conceptual inverse of rust-cold, but could not come with a great name (rust-cold is itself not a great name: cold in what context? from which perspective? is it supposed to mean that the function is rarely called?)

@rustbot
Copy link
Collaborator

rustbot commented Jan 13, 2026

This PR changes rustc_public

cc @oli-obk, @celinval, @ouz-a, @makai410

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

cc @rust-lang/rust-analyzer

@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rust-analyzer Relevant to the rust-analyzer team, which will review and decide on the PR/issue. labels Jan 13, 2026
@rustbot
Copy link
Collaborator

rustbot commented Jan 13, 2026

r? @jackh726

rustbot has assigned @jackh726.
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

@nagisa
Copy link
Member Author

nagisa commented Jan 13, 2026

Note well: I'm planning to create a tracking issue and perhaps update other relevant projects (e.g. rust analyzer cares about abis) once we establish that there are no fundamental blockers to move forward with the approach.

Does that sound reasonable?

This is the conceptual opposite of the rust-cold calling convention and
is particularly useful in combination with the new `explicit_tail_calls`
feature.

For relatively tight loops implemented with tail calling (`become`) each
of the function with the regular calling convention is still responsible
for restoring the initial value of the preserved registers. So it is not
unusual to end up with a situation where each step in the tail call loop
is spilling and reloading registers, along the lines of:

    foo:
        push r12
        ; do things
        pop r12
        jmp next_step

This adds up quickly, especially when most of the clobberable registers
are already used to pass arguments or other uses.

I was thinking of making the name of this ABI a little less LLVM-derived
and more like a conceptual inverse of `rust-cold`, but could not come
with a great name (`rust-cold` is itself not a great name: cold in what
context? from which perspective? is it supposed to mean that the
function is rarely called?)
@nagisa nagisa force-pushed the add-preserve-none-abi branch from ecf300a to 6d27ba9 Compare January 13, 2026 15:05
#[no_mangle]
#[inline(never)]
pub extern "rust-preserve-none" fn peach(x: u16) {
panic!("unwinding works too")
Copy link
Member Author

Choose a reason for hiding this comment

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

This probably wants a run-test too...

@rust-log-analyzer
Copy link
Collaborator

The job tidy failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
[TIMING:end] tool::Tidy { compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu } -- 0.000
fmt check
Diff in /checkout/tests/codegen-llvm/preserve-none.rs:12:
 
 pub fn quince(x: u16) {
     if x == 12345 {
-// CHECK: call preserve_nonecc void @peach(i16
+        // CHECK: call preserve_nonecc void @peach(i16
         peach(54321);
     }
 }
fmt: checked 6636 files
Bootstrap failed while executing `test src/tools/tidy tidyselftest --extra-checks=py,cpp,js,spellcheck`
Build completed unsuccessfully in 0:00:53

@folkertdev
Copy link
Contributor

This seems somewhat related: I believe that there is no way to make tail calls work with ABIs that pass values using PassMode::Indirect { on_stack: false } #144855 (comment).

This calling convention is probably still useful, but adding another calling convention that uses the (unstable!) rust ABI won't actually help with moving tail call support forward.

So maybe there is a possible design of an extern "tail-call" that would combine this preserve-none feature with a guarantee that PassMode::Indirect { on_stack: false } will not be used.

Also for completeness, using PassMode::Indirect { on_stack: true } is currently buggy on many platforms, notably x86 and riscv. So that will be a blocker anyhow for guaranteed tail calls, for at least another LLVM release cycle.

@nagisa
Copy link
Member Author

nagisa commented Jan 13, 2026

I don't intend this PR to be a solution to the indirect argument passing problem, although I see how having a calling convention with more registers available would help with indirectly passed arguments.

My primary incentive is really just performance – spilling registers in the top level caller once is much more efficient than spilling and reloading on every jump. Normally I would probably look for a solution that is transparent to the user, but unfortunately in this case the decision is callee's and it cannot know if it'll be become'd or called regularly in the general case.

That reminds me, I guess I'll have to verify if I don't need to change the abi computation code here...

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. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rust-analyzer Relevant to the rust-analyzer team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants