• I’m researching how different ABIs work in relation to implementing tail calls in the Rust compiler. • One thing that I’m currently interested in is if there are ABIs which prohibit the callee from modifying the arguments passed to it by-ref/by-stack. • I tried reading through various ABI specifications, but found them incredibly confusing and hard to find the information I’m interested in… I did some experimentation using the following code: typedef struct { uint64_t a; uint64_t b; uint64_t c; uint64_t d; } Big; void f(Big x); void g(Big x) { f(x); f(x); } void h(Big x) { x.a = 1; f(x); } And for all ABIs I tried: g has to make a copy of x since the first call to f might modify the argument passed to it h doesn’t have to make a copy of x since it’s allowed to modify the argument it receives But is this true for all ABIs (supported by llvm)? • arsenm 2 “by-ref” is the a particularly confusing word choice here. • There are 2 related IR attributes, byval and byref. • byval has an implicit copy performed in the caller, and the pointer visible to the callee is theoretically writable (I’m not aware of anywhere taking advantage of that).

Article Summaries:

  • A Rust developer is investigating how different Application Binary Interfaces (ABIs) handle argument passing, specifically whether any ABI forbids a callee from modifying arguments passed by reference. The inquiry centers on tail‑call implementation in the Rust compiler and involves experimenting with a Big struct and functions f, g, and h. The developer notes that under most ABIs, g must copy the argument to protect against potential modification by f, while h can modify its argument directly. The question also touches on LLVM’s byval and byref attributes, which control implicit copying and mutability of passed values.

Sources: