+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.

Reply via email to