Skip to content

Reference to ZST occupies slot in function calling convention #141049

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

Closed
chadaustin opened this issue May 15, 2025 · 6 comments
Closed

Reference to ZST occupies slot in function calling convention #141049

chadaustin opened this issue May 15, 2025 · 6 comments
Labels
A-zst Area: Zero-sized types (ZSTs). C-discussion Category: Discussion or questions that doesn't represent real issues. T-opsem Relevant to the opsem team

Comments

@chadaustin
Copy link

Please forgive if redundant - I did a search but could not find relevant discussion.

I work on embedded Rust and we use ZSTs and traits for hardware abstraction layers (HAL).

If we change call sites to accept HAL objects by reference (to support unit tests with stateful HALs), I noticed Rust will occupy an argument passing slot even though &ZST could also be zero-sized.

Notice the differences in https://gcc.godbolt.org/z/s6ocP1bds

Reproducing example here:

#![no_std]

struct MyDevice;

#[unsafe(no_mangle)]
pub fn zst_by_copy(z: MyDevice, a: usize, b: usize, c: usize) -> usize {
    a + b + c
}

#[unsafe(no_mangle)]
pub fn zst_by_reference(z: &MyDevice, a: usize, b: usize, c: usize) -> usize {
    a + b + c
}
zst_by_copy:
        add     a0, a0, a1
        add     a0, a0, a2
        ret

zst_by_reference:
        add     a0, a2, a1
        add     a0, a0, a3
        ret

Is there a fundamental reason or is this simply a missed optimization opportunity?

@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label May 15, 2025
@hanna-kruppe
Copy link
Contributor

Making reference-to-ZST zero-sized instead of a pointer has been discussed and rejected in the past because it’s a breaking change in several ways. See rust-lang/rfcs#2040 (comment) for example.

@chadaustin
Copy link
Author

Thanks for the context and previous discussion. If we limited this optimization to Rust functions, leaving extern "C" and struct representations alone, I wonder whether the optimization is feasible.

@saethlin
Copy link
Member

The optimization is only feasible under Stacked Borrows, and it is illegal under Tree Borrows, so I think it is unlikely that this optimization will exist. The use pattern is casting that &ZST to a raw pointer to a non-ZST, then using that to do reads.

@lolbinarycat lolbinarycat added C-discussion Category: Discussion or questions that doesn't represent real issues. A-zst Area: Zero-sized types (ZSTs). T-opsem Relevant to the opsem team and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels May 16, 2025
@hanna-kruppe
Copy link
Contributor

See also rust-lang/unsafe-code-guidelines#256 for more discussions on this usage pattern.

@chadaustin
Copy link
Author

Happy to close as duplicate. Alas.

@erikdesjardins
Copy link
Contributor

Note that LLVM will optimize out such arguments, but only for non-exported functions. For example: https://godbolt.org/z/3Mo8xbYz6. (Depending on complexity, you may need LTO to ensure this happens.)

The functions in the original Godbolt example are exported (due to no_mangle), which inhibits LLVM from changing the signature.

So depending on your use-case (do you need these arguments in the toplevel of interrupt handlers?) it may not be an issue in practice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-zst Area: Zero-sized types (ZSTs). C-discussion Category: Discussion or questions that doesn't represent real issues. T-opsem Relevant to the opsem team
Projects
None yet
Development

No branches or pull requests

6 participants