Hi! I am one of the folk currently working on `rustc_codegen_gcc`, and I thought that I will provide some more context.
> So I looked into this further, it is not rust that specifies sret but rather rust transformation into llvm code generation that does that. > So you need to explain what exactly what you need. The problem here is as follows: The Rust ABI is specified in terms of "PassMode"s. The `PassMode::Indirect` should *always* correspond to LLVM's `sret` attribute when applied to a return value. The Rust frontend is free to use `sret` however it pleases. It can choose to use it in cases where C would not, or not use it where C would. As an example: ```rust #[repr(C)] // Same layout as in C struct Foo { a: u128, } #[unsafe(no_mangle)] // Function with the Rust ABI: extern "Rust" fn rust_func() -> Foo { Foo { a: 1 } } ``` This struct will be passed via `sret` by Rust: ```arm rust_func: mov w9, #1 stp x9, xzr, [x8] ret ``` However, GCC(following the C ABI) will pass the struct in a register: ```c struct Foo{ __uint128_t a }; struct Foo c_func(){ struct Foo res = {1}; return res; } ``` ```arm c_func: mov x0, 1 mov x1, 0 ret ``` We need some way to force GCC to pass the return value in `sret`, to match the Rust ABI. Otherwise, we will get subtle ABI bugs. We need to do so both on the calle and the caller side. I believe `CALL_EXPR_RETURN_SLOT_OPT` is applied on the caller side only, which is insufficient for our purposes. Is there something we could use to achieve this effect? If no, I'd be willing to work on adding such an attribute. Do you have a rough idea about where I should start? Thank you for taking the time to help us with this issue! On Mon, 30 Jun 2025 at 19:31, Antoni Boucher <boua...@zoho.com> wrote: > Hi. > Let me introduce you Fractal Fir who's a student working on > rustc_codegen_gcc for the Google Summer of Code. > He found some ABI issues (one related to sret) in rustc_codegen_gcc and > wanted to join this discussion in order to share more details about what > we want to achieve here. > Thanks. > > Le 2025-06-30 à 10 h 51, Andrew Pinski a écrit : > > On Mon, Jun 30, 2025 at 7:25 AM Antoni Boucher <boua...@zoho.com> wrote: > >> > >> > >> > >> Le 2025-06-29 à 19 h 42, Andrew Pinski a écrit : > >>> > >>> > >>> On Sun, Jun 29, 2025, 4:36 PM Antoni Boucher <boua...@zoho.com > >>> <mailto:boua...@zoho.com>> wrote: > >>> > >>> > >>> > >>> Le 2025-06-29 à 10 h 46, Andrew Pinski a écrit : > >>> > > >>> > > >>> > On Sun, Jun 29, 2025, 7:43 AM Andrew Pinski <pins...@gmail.com > >>> <mailto:pins...@gmail.com> > >>> > <mailto:pins...@gmail.com <mailto:pins...@gmail.com>>> wrote: > >>> > > >>> > On Sun, Jun 29, 2025, 7:36 AM Antoni Boucher via Gcc > >>> > <gcc@gcc.gnu.org <mailto:gcc@gcc.gnu.org> > >>> <mailto:gcc@gcc.gnu.org <mailto:gcc@gcc.gnu.org>>> wrote: > >>> > > >>> > Hi. > >>> > Is there a way in GENERIC to specify that a parameter > will be > >>> > passed in > >>> > "sret", or is this solely controlled by the hook > >>> struct_value_rtx? > >>> > > >>> > > >>> > It is only controlled by the hook. > >>> > What exactly are trying to do? > >>> > You could set the return slot optimization bit on the call > >>> > expression if you want the lhs of a call not to be copied > and > >>> just > >>> > passed as the address via sret. > >>> > >>> I'm trying to follow the Rust ABI for rustc_codegen_gcc: they > manually > >>> specify whether a param is "sret". > >>> > >>> > >>> Not all ABI/targets have a sret specific register. So this is even more > >>> confusing. > >>> > >>> > >>> > >>> > > >>> > That is if you have: > >>> > StructVar = func(...); > >>> > > >>> > You set the return slot optimization bit on the call expr > in > >>> generic > >>> > and which will copy that bit to the gimple GIMPLE_CALL and > then > >>> > during expand will again copy it back to the generic > >>> call_expr and > >>> > expand will use the target for the address. > >>> > > >>> > > >>> > CALL_EXPR_RETURN_SLOT_OPT is the macro. > >>> > >>> Is this a guaranteed optimization? I'm asking because this is for > ABI > >>> correctness and I need a solution that will always work. > >>> If not, would there be another way to do this? > >>> Thanks. > >>> > >>> > >>> Yes it is guaranteed that if the return type is returned via memory > >>> reference to the other function it will use that memory location. > >> > >> How does this work on the side of the function declaration? Do we need > >> to set something so that it follows the correct convention? > > > > So there are 2 separate things here. > > First there is an ABI of having struct return in memory. > > Note if TREE_ADDRESSABLE is set on a struct type, then return value is > > always through memory: > > ``` > > In ..._TYPE nodes, it means that objects of this type must be fully > > addressable. This means that pieces of this object cannot go into > > register parameters, for example. If this a function type, this > > means that the value must be returned in memory. > > ``` > > The ABI part is independent of the CALL_EXPR and dependent on the > > FUNCTION_TYPE that is being used and the target. > > > > Second is specifying if you can reuse the memory of the lhs of a > > modify_expr with a call_expr when the ABI says the memory is return in > > memory. > > This is specified via CALL_EXPR_RETURN_SLOT_OPT on the CALL_EXPR. > > > > I am trying to understand how the rustc front-end sets up this. Does > > it implement the ABI of each target as that is needed for LLVM? > > This is the major difference here as GCC's front-ends normally don't > > care much about the ABI except in specific cases (e.g. returns this > > and a few others). GCC's front-end don't handle the call argument ABI > > at all; rather it is more like you build call_expr which act like > > function calls in C (well with the addition of reference types but > > those are really just pointers at that point). And the middle-end > > (with help from the backend) which implements the full ABI. > > > > The reason why this is done this way is an abstraction layer and > > allows new front-ends for backends that are known to it at the time > > without additional work (e.g. gfortran adding support for riscv or > > aarch64; no changes to the front-end were made). This seems like the > > opposite of LLVM where there is no tight coupling of the 2 and there > > is no abstraction for most of the ABI call work and each front-end > > needs to implement that again. > > > > Hope this helps explain GCC front-end interactions with the GCC's > > middle-end better. > > > >> > >> Thanks a lot for your help. > >> > >>> > >>> Even for things like a->b = func(...)[RSO]. It is even used by the c++ > >>> frontend that way. > >>> > >>> > >>> > >>> > > >>> > > >>> > > >>> > Is that what you are looking for? > >>> > > >>> > > >>> > Thanks, > >>> > Andrew > >>> > > >>> > Thanks. > >>> > > >>> > >> > >