Is Parameter Reference Optimization still a thing since Zig 0.16?

It looks the issue says PRO is gone.

But the official docs still says:

… When these types are passed as parameters, Zig may choose to copy and pass by value, or pass by reference, whichever way Zig decides will be faster. This is made possible, in part, by the fact that parameters are immutable.

LLVM can still decide to promote a pointer argument into a by-value argumemt. Big struct arguments/return values that don’t fit in register are technically passed by pointer even if you pass them by value

That being said i did see a measurable speedup in my chess engine when i changed a big struct being passed by value vs by *const… (the const pointer was faster) so i’m not sure what the best advice here is. I would have hoped that by value is always better and LLVM will do the right thing but I have been burned by this in the past :smiley:

I used to rely on parameter reference optimization, but since learning about *const T , I’ve started choosing between T , *T , and *const T to make my intent explicit.

But what is the difference between T and *const T in terms of intent? IMO they are equivalent “in” parameters and the compiler should reliably do the right decision between them, whatever the programmer wrote. (especially because in generic code where you don’t know T you can’t make the right decision)

I mean intent at the API level, rather than how the compiler passes the parameter.

With *const T , I mean “I’m passing a const reference to this object.”
With T , I mean “I’m passing a copy of this value.”

That distinction is useful to me even if the compiler ultimately uses the same calling convention.

The big struct may hold self-references, causing odd issues if copied.

The pointer may be held, and in threaded code that could be unsafe while a true copy could be safe.

Just two examples, but there’s definitely differences.

If there’s no inlining, technically it’s usually done like this: the caller copies the value into the ABI-defined stack argument area and the callee reads it directly from there (Linux x64), or: the caller copies it into memory and passes a pointer to that copy in a register (Windows).

Pass-by-value semantics require an independent object to avoid aliasing issues (which is also the aliasing issue that led to Zig’s PRO being removed). LLVM can sometimes prove that the copy is unobservable and eliminate it. The Windows ABI makes that optimization much easier because the argument is already passed indirectly as a pointer. The Linux x64 ABI can sometimes be optimized this way, but the required stack-layout constraints are fairly stringent, so this optimization is unlikely to apply in the general case.