August 21, 2020
On Thursday, 20 August 2020 at 20:29:31 UTC, Araq wrote:
> On Thursday, 20 August 2020 at 17:31:17 UTC, IGotD- wrote:
>> This is interesting on a general level as well and true for several programming languages. Let the compiler optimize the parameter passing unless the programmer explicitly ask for a certain way (copy object, pointer/reference etc.). This is very unusual and if you have a language that optimizes the parameter passing by default, please mention it because it would be interesting.
>>
>
> Nim does this and I took the feature from Ada. You can override the behavior with pragmas but I've only seen that done for C interop, not for optimizations as the compiler always seems to get it right.

Interesting, how does it work? As mentioned previously, the issue with choosing by-val vs by-ref passing solely based on the type is that it doesn't take into consideration the ABI and how many registers are available. E.g. if you have more than X parameters some will need to be spilled on the stack anyway (even if they can fit in a GPR).

Can the Nim compiler (I'm guessing `extccomp`) query the C/C++ compiler backend for e.g. the number of registers available for parameters? Or you have this logic built into the frontend (say `ccgtypes`)?
August 21, 2020
On Friday, 21 August 2020 at 13:59:30 UTC, Petar Kirov [ZombineDev] wrote:
> Interesting, how does it work? As mentioned previously, the issue with choosing by-val vs by-ref passing solely based on the type is that it doesn't take into consideration the ABI and how many registers are available. E.g. if you have more than X parameters some will need to be spilled on the stack anyway (even if they can fit in a GPR).
>
> Can the Nim compiler (I'm guessing `extccomp`) query the C/C++ compiler backend for e.g. the number of registers available for parameters? Or you have this logic built into the frontend (say `ccgtypes`)?

It's built into cctypes indeed. The logic is mostly "pass by pointer if sizeof(T) > 3 machine words". ABI and registers are not relevant all that much, what you want to prevent are copies of large sizes.
August 21, 2020
On Friday, 21 August 2020 at 09:48:16 UTC, Kagamin wrote:
>
> I mean things like
> int atomicLoad(in ref shared int n);
> int loadAligned(in ref byte[4] n);
> When the argument should be passed by ref by programmer's intent and should be communicated to the compiler, because the compiler isn't that smart.

The first example is pretty good: We probably need to specify the interaction with `shared`. AFAICS it boils down to "when do we want to read a `shared` value ?".
If we pass by value, it means the input parameter will only have a single value, while if we pass by ref, a function can "listen" to changes.
I don't really have an answer for this at the moment, I would need to try out some options before I make up my mind.

The second example is pretty simple: the backend will decide whether to pass it by ref or not. Since it's a small type, it might make more sense to pass it in registers. Whether or not it's ref does not matter to the programmer, because the programmer cannot change the input anyway, only read it.

>>> 2. Dependence on calling convention. AIU ref semantics depends on parameter position?
>>
>> Yes. Originally didn't, but that was the main feedback I got, that it should be done at the function level instead of the parameter (type) level.
>
> Doesn't this defeat your optimization when passing by value is expensive?

I don't see how ?
August 21, 2020
On 8/21/20 1:20 AM, Araq wrote:
> On Thursday, 20 August 2020 at 22:19:16 UTC, Andrei Alexandrescu wrote:
>> On 8/20/20 1:31 PM, IGotD- wrote:
>>> This is interesting on a general level as well and true for several programming languages. Let the compiler optimize the parameter passing unless the programmer explicitly ask for a certain way (copy object, pointer/reference etc.).
>>
>> This has been discussed a few times. If mutation is allowed, aliasing is a killer:
>>
>> void fun(ref S a, const compiler_chooses S b) {
>>     ... mutate a, read b ...
>> }
>>
>> S x;
>> fun(x, x); // oops
>>
>> The problem now is that the semantics of fun depends on whether the compiler chose pass by value vs. pass by reference.
> 
> True but in practice it doesn't happen very often. The benefits far outweigh this minor downside.

That seems quite worrisome. A bug rare and subtle that can become devastating. Something the Hindenburg captain might have said.

> Plus there are known ways to prevent this form of aliasing at compile-time.

Not if you have globals and/or separate compilation.
August 21, 2020
On Friday, 21 August 2020 at 19:21:37 UTC, Andrei Alexandrescu wrote:
> On 8/21/20 1:20 AM, Araq wrote:
>> On Thursday, 20 August 2020 at 22:19:16 UTC, Andrei Alexandrescu wrote:
>>> On 8/20/20 1:31 PM, IGotD- wrote:
>>> void fun(ref S a, const compiler_chooses S b) {
>>>     ... mutate a, read b ...
>>> }
>>>
>>> S x;
>>> fun(x, x); // oops
>>>
>>> The problem now is that the semantics of fun depends on whether the compiler chose pass by value vs. pass by reference.
>> 
>> True but in practice it doesn't happen very often. The benefits far outweigh this minor downside.
>
> That seems quite worrisome. A bug rare and subtle that can become devastating. Something the Hindenburg captain might have said.
>
>> Plus there are known ways to prevent this form of aliasing at compile-time.
>
> Not if you have globals and/or separate compilation.

The risk of aliasing-related bugs comes from the availability of pointers/references though, which will still be present in D with or without this optimization. People who want to rule out aliasing problems can just explicitly specify pass-by-value using `scope const` instead of `in`.

Let's not cripple the future of the language out of fear of pitfalls that are already present, and cannot be entirely removed as long as D remains a systems programming language.
August 22, 2020
On Friday, 21 August 2020 at 19:21:37 UTC, Andrei Alexandrescu wrote:
> On 8/21/20 1:20 AM, Araq wrote:
>> On Thursday, 20 August 2020 at 22:19:16 UTC, Andrei Alexandrescu wrote:
>>> On 8/20/20 1:31 PM, IGotD- wrote:
>>>> [...]
>>>
>>> This has been discussed a few times. If mutation is allowed, aliasing is a killer:
>>>
>>> void fun(ref S a, const compiler_chooses S b) {
>>>     ... mutate a, read b ...
>>> }
>>>
>>> S x;
>>> fun(x, x); // oops
>>>
>>> The problem now is that the semantics of fun depends on whether the compiler chose pass by value vs. pass by reference.
>> 
>> True but in practice it doesn't happen very often. The benefits far outweigh this minor downside.
>
> That seems quite worrisome. A bug rare and subtle that can become devastating. Something the Hindenburg captain might have said.
>
>> Plus there are known ways to prevent this form of aliasing at compile-time.
>
> Not if you have globals and/or separate compilation.

Wrong. You can simply compile it to 'fun(x, copy(x))' if an alias analysis cannot disambiguate the locations and the alias analysis is restricted to the callsite, separate compilation continues to work.
August 22, 2020
On Friday, 21 August 2020 at 23:16:53 UTC, tsbockman wrote:
>
> The risk of aliasing-related bugs comes from the availability of pointers/references though, which will still be present in D with or without this optimization. People who want to rule out aliasing problems can just explicitly specify pass-by-value using `scope const` instead of `in`.
>
> Let's not cripple the future of the language out of fear of pitfalls that are already present, and cannot be entirely removed as long as D remains a systems programming language.

Most of the parameters can actually be 'const', which also the case for 'in' parameters with the current definition. With const parameters, aliasing is no problem unless you do something with pointers further into the parameters.

'in' should be const by default like it is today and if not use inout or ref. With a language like D it is up to the programmer ensure there is no aliasing, much like the 'restrict' qualifier in C. I'm for that the compiler should detect any aliasing but this can be gradually improved.

The new 'in' qualifier is interesting enough not to through it away because of the aliasing problems.
August 22, 2020
On 8/22/20 2:07 AM, Araq wrote:
> On Friday, 21 August 2020 at 19:21:37 UTC, Andrei Alexandrescu wrote:
>> On 8/21/20 1:20 AM, Araq wrote:
>>> On Thursday, 20 August 2020 at 22:19:16 UTC, Andrei Alexandrescu wrote:
>>>> On 8/20/20 1:31 PM, IGotD- wrote:
>>>>> [...]
>>>>
>>>> This has been discussed a few times. If mutation is allowed, aliasing is a killer:
>>>>
>>>> void fun(ref S a, const compiler_chooses S b) {
>>>>     ... mutate a, read b ...
>>>> }
>>>>
>>>> S x;
>>>> fun(x, x); // oops
>>>>
>>>> The problem now is that the semantics of fun depends on whether the compiler chose pass by value vs. pass by reference.
>>>
>>> True but in practice it doesn't happen very often. The benefits far outweigh this minor downside.
>>
>> That seems quite worrisome. A bug rare and subtle that can become devastating. Something the Hindenburg captain might have said.
>>
>>> Plus there are known ways to prevent this form of aliasing at compile-time.
>>
>> Not if you have globals and/or separate compilation.
> 
> Wrong. You can simply compile it to 'fun(x, copy(x))' if an alias analysis cannot disambiguate the locations and the alias analysis is restricted to the callsite, separate compilation continues to work.

Fair enough, thanks.
August 23, 2020
On 8/5/20 3:27 AM, Fynn Schröder wrote:
> On Friday, 31 July 2020 at 21:49:25 UTC, Mathias LANG wrote:
>> I hope this will generate interest with people hitting the same problem.
> 
> I've literally yesterday written some new code with `const scope ref` in almost every function to pass large, complex structs. Occasionally, I had to store rvalues temporarily to pass as lvalues (non-templated code). I would rather simply put `in` on those parameters :-) It's a lot easier to grasp function signatures only using `in` and `out` on parameters (and their effect/purpose being immediately obvious to new D programmers!)

Sorry to interject d.D.learn material in this thread, but where is "scope ref" documented? I found https://dlang.org/spec/function.html#scope-parameters which discusses the use of `scope` with ref type parameters, but the example given is pointer-based. Is it correct that `scope ref T` behaves the same as `scope T*` ?

DIP1000 shows as "superseded"

I am glad D is iterating and improving on safety, but I have found that documentation may not well recent changes in this area.

August 25, 2020
On Friday, 21 August 2020 at 18:23:08 UTC, Mathias LANG wrote:
> The second example is pretty simple: the backend will decide whether to pass it by ref or not. Since it's a small type, it might make more sense to pass it in registers. Whether or not it's ref does not matter to the programmer, because the programmer cannot change the input anyway, only read it.

The backend doesn't know how to load the data. It matters to the programmer, because it affects performance.

Another example:
void log(in ref int n)
{
  write(fd, &n, n.sizeof);
}
If the argument here is passed by value, it will need to fiddle with stack and the code will be less optimal.

>> Doesn't this defeat your optimization when passing by value is expensive?
>
> I don't see how ?

If a non-POD object is passed by value, it will have extra calls to postblit and destructor.