+1, I've been thinking about similar concepts as well.
This would be particularly useful for Win64. The Win64 ABI
requires all structs > 64 bits (or of a size which is not a power
of 2) to be passed by reference. So if a function foo(BigStruct
s) is invoked, the caller first allocates a copy of s for the
callee on its own stack (16-bytes aligned) and then passes the
callee a pointer to the copy (in a register or on the parameters
stack), instead of passing a copy of s directly on the parameters
stack. It seems to me that the whole idea of this is to encourage
byref passing and copying arguments only if required, otherwise
it doesn't make much sense performance-wise (higher stack memory
usage due to the additional pointer and need for dereferencing
the pointer parameter).
I'd propose a small change so that suited structs are passed
transparently byref only if the parameter is not mutable, e.g.,
for a function foo(const BigStruct s). The compiler would
therefore not need to analyze the code flow in the callee to
determine if the parameter is modified and hence a copy is needed.
The compiler would nevertheless need to be quite smart though:
---
struct MyBigStruct { double a, b, c; }
double foo(const MyBigStruct s, double* x)
{
*x = s.a;
return s.b;
}
// naive optimization by byref passing
double foo_ref(const ref MyBigStruct s, double* x)
{
*x = s.a;
return s.b;
}
MyBigStruct s = { 1, 2, 3 };
double* x = &s.b;
auto bla = foo(s, x); // returns 2; now s.b = 1
s.b = 2; // reset s.b to 2
bla = foo_ref(s, x); // returns 1! (the new s.b = 1)
---
So the compiler would need to prove there is no way the argument
can be modified and handle tricky aliasing issues accordingly.