September 26, 2012
On 09/26/2012 12:13 PM, Don Clugston wrote:
> On 26/09/12 01:31, H. S. Teoh wrote:
>> On Wed, Sep 26, 2012 at 01:10:00AM +0200, Timon Gehr wrote:
>>> On 09/26/2012 12:58 AM, Iain Buclaw wrote:
>> [...]
>>>> string abc;
>>>>
>>>> float[] A()
>>>> {
>>>>      abc ~= "A";
>>>>      return [];
>>>> }
>>>>
>>>> float[] B()
>>>> {
>>>>      abc ~= "B";
>>>>      return [];
>>>> }
>>>>
>>>> float[] C()
>>>> {
>>>>      abc ~= "C";
>>>>      return [];
>>>> }
>>>>
>>>> void main()
>>>> {
>>>>      A()[] = B()[] + C()[];
>>>>      assert(abc == "???");
>>>> }
>>>> ----
>>>>
>>>> Without cheating, I invite people to have a good guess what 'abc' is
>>>> equal to, but just to narrow it down.
>>>>
>>>> 1)  It isn't "ABC".
>>>> 2)  On x86/x86_64, it isn't "ACB".
>>>> 3)  On everything else, it's the reverse of what you'd expect on
>>>> x86/x86_64.
>>>>
>>>> The problem here is that the array operation A[] = B[] + C[] gets
>>>> transformed into an extern(C) call.  And because there's no strict
>>>> rules
>>>> in place over the order of which it's parameters are evaluated, it
>>>> could
>>>> go either way (LTR, or RTL).
>>>>
>>>> Serious note: This test is bogus as this and similar other failing
>>>> tests
>>>> on non-x86 platforms are not at all obvious to the users who get
>>>> issues.
>>>> So what are we to do about it?
>> [...]
>>> As far as my understanding of the issue goes, the correct answer is
>>> "ABC", and compilers that do not produce "ABC" are buggy.
>>>
>>> The solution is to transform the code to code that evaluates the
>>> arguments to the extern(C) call in the correct order and eg. stores
>>> them in temporaries before passing them on.
>> [...]
>>
>> Shouldn't it be BCA? I'd expect the assignment operation to take place
>> last, so the corresponding expression should be evaluated last.
>
> I expected "BCA" too. Left associative expressions evaluated
> left-to-right, right associative expressions evaluated right-to-left.
>

I don't think that makes any sense. Why would that be the case?

a = b = c;

=>

a.opAssign(b.opAssign(c));

foo() ^^ bar() ^^ qux()

=>

pow(foo(), pow(bar(), qux()));





September 26, 2012
On 09/26/2012 10:11 AM, Walter Bright wrote:
> On 9/26/2012 12:36 AM, Iain Buclaw wrote:
>> On Tuesday, 25 September 2012 at 23:39:39 UTC, Walter Bright wrote:
>>> On 9/25/2012 3:58 PM, Iain Buclaw wrote:
>>>> The problem here is that the array operation A[] = B[] + C[] gets
>>>> transformed
>>>> into an extern(C) call.  And because there's no strict rules in
>>>> place over the
>>>> order of which it's parameters are evaluated, it could go either way
>>>> (LTR, or
>>>> RTL).
>>>>
>>>> Serious note: This test is bogus as this and similar other failing
>>>> tests on
>>>> non-x86 platforms are not at all obvious to the users who get
>>>> issues. So what
>>>> are we to do about it?
>>>
>>> D needs to move towards a defined order of evaluation. I understand that
>>> there's a problem when using parts of a C compiler that feels free to
>>> reorder
>>> within the C rules. It's something we have to deal with sooner or
>>> later, by
>>> either:
>>>
>>> 1. adjusting the C optimizer so it follows D rules for D code
>>>
>>> 2. assigning terms to temporaries that are executed in a specific
>>> order by C
>>> rules
>>
>>
>> Indeed, but where does that leave code that gets compiled down to a
>> extern(C) call?
>
> C functions all seem to evaluate their args right-to-left, even though
> the C Standard doesn't specify that.

On x86. It is unspecified, therefore different compilers on different
architectures do it in different ways.

> So we should be all right by simply
> defining that D do it that way for C functions.
>

I disagree. Why should the calling convention have an impact on code
semantics? The code reads LTR, therefore evaluation should consistently
be LTR.

> It doesn't actually matter what order D does things, we just have to
> pick one. And so we might as well pick one that C compilers naturally do
> anyway.
> ...

There is no such thing. C and C++ get evaluation order thoroughly wrong.

September 26, 2012
On Tuesday, 25 September 2012 at 22:58:11 UTC, Iain Buclaw wrote:
> Pop quiz!
>
> Without cheating, I invite people to have a good guess what 'abc' is equal to, but just to narrow it down.
>
> 1)  It isn't "ABC".
> 2)  On x86/x86_64, it isn't "ACB".
> 3)  On everything else, it's the reverse of what you'd expect on x86/x86_64.

I'd say abc's value is "unspecified", and any attempt at predicting it would be bogus. That's my answer anyways

> The problem here is that the array operation A[] = B[] + C[] gets transformed into an extern(C) call.  And because there's no strict rules in place over the order of which it's parameters are evaluated, it could go either way (LTR, or RTL).

I don't see how the "extern(C)" is involved here, since it is the D compiler that first evaluates A(), B() and C() before passing the making the C function call. Or did I miss something?

September 26, 2012
On 26 September 2012 13:07, monarch_dodra <monarchdodra@gmail.com> wrote:
> On Tuesday, 25 September 2012 at 22:58:11 UTC, Iain Buclaw wrote:
>>
>> Pop quiz!
>>
>> Without cheating, I invite people to have a good guess what 'abc' is equal to, but just to narrow it down.
>>
>> 1)  It isn't "ABC".
>> 2)  On x86/x86_64, it isn't "ACB".
>> 3)  On everything else, it's the reverse of what you'd expect on
>> x86/x86_64.
>
>
> I'd say abc's value is "unspecified", and any attempt at predicting it would be bogus. That's my answer anyways
>
>
>> The problem here is that the array operation A[] = B[] + C[] gets transformed into an extern(C) call.  And because there's no strict rules in place over the order of which it's parameters are evaluated, it could go either way (LTR, or RTL).
>
>
> I don't see how the "extern(C)" is involved here, since it is the D compiler
> that first evaluates A(), B() and C() before passing the making the C
> function call. Or did I miss something?
>

There is no physical code generation from the frontend that says "evaluate this".  What it passes to be backend for this operation is a function call. So the backend determines the order of evaluation depending on the order of parameters.

-- 
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';
September 26, 2012
On 09/26/2012 02:07 PM, monarch_dodra wrote:
> On Tuesday, 25 September 2012 at 22:58:11 UTC, Iain Buclaw wrote:
>> Pop quiz!
>>
>> Without cheating, I invite people to have a good guess what 'abc' is
>> equal to, but just to narrow it down.
>>
>> 1)  It isn't "ABC".
>> 2)  On x86/x86_64, it isn't "ACB".
>> 3)  On everything else, it's the reverse of what you'd expect on
>> x86/x86_64.
>
> I'd say abc's value is "unspecified", and any attempt at predicting it
> would be bogus. That's my answer anyways
>...

Why? This is not useful behaviour.


September 26, 2012
On Wednesday, 26 September 2012 at 12:48:19 UTC, Timon Gehr wrote:
> On 09/26/2012 02:07 PM, monarch_dodra wrote:
>> On Tuesday, 25 September 2012 at 22:58:11 UTC, Iain Buclaw wrote:
>>> Pop quiz!
>>>
>>> Without cheating, I invite people to have a good guess what 'abc' is
>>> equal to, but just to narrow it down.
>>>
>>> 1)  It isn't "ABC".
>>> 2)  On x86/x86_64, it isn't "ACB".
>>> 3)  On everything else, it's the reverse of what you'd expect on
>>> x86/x86_64.
>>
>> I'd say abc's value is "unspecified", and any attempt at predicting it
>> would be bogus. That's my answer anyways
>>...
>
> Why? This is not useful behaviour.

What do you mean "why"? Because order of evaluation is not specified, so the value of abc is not specified. That was my answer. The question wasn't really "what would useful behavior be".

IMO: useful behavior would be if it was explicitly illegal to modify (or modify + read) the same value twice in the same expression. I'd rather expressions such as:
A()[] = B()[] + C()[];
x[i] = ++i + 1;

Be illegal rather than have (an arbitrarily defined) specified behavior. Trying to specify a specific behavior in such cases is opening a can of worms.

September 26, 2012
On 26-09-2012 13:45, Iain Buclaw wrote:
> On Wednesday, 26 September 2012 at 08:12:16 UTC, Walter Bright wrote:
>> On 9/26/2012 12:36 AM, Iain Buclaw wrote:
>>> On Tuesday, 25 September 2012 at 23:39:39 UTC, Walter Bright wrote:
>>>> On 9/25/2012 3:58 PM, Iain Buclaw wrote:
>>>>> The problem here is that the array operation A[] = B[] + C[] gets
>>>>> transformed
>>>>> into an extern(C) call.  And because there's no strict rules in
>>>>> place over the
>>>>> order of which it's parameters are evaluated, it could go either
>>>>> way (LTR, or
>>>>> RTL).
>>>>>
>>>>> Serious note: This test is bogus as this and similar other failing
>>>>> tests on
>>>>> non-x86 platforms are not at all obvious to the users who get
>>>>> issues. So what
>>>>> are we to do about it?
>>>>
>>>> D needs to move towards a defined order of evaluation. I understand
>>>> that
>>>> there's a problem when using parts of a C compiler that feels free
>>>> to reorder
>>>> within the C rules. It's something we have to deal with sooner or
>>>> later, by
>>>> either:
>>>>
>>>> 1. adjusting the C optimizer so it follows D rules for D code
>>>>
>>>> 2. assigning terms to temporaries that are executed in a specific
>>>> order by C
>>>> rules
>>>
>>>
>>> Indeed, but where does that leave code that gets compiled down to a
>>> extern(C) call?
>>
>> C functions all seem to evaluate their args right-to-left, even though
>> the C Standard doesn't specify that. So we should be all right by
>> simply defining that D do it that way for C functions.
>>
>> It doesn't actually matter what order D does things, we just have to
>> pick one. And so we might as well pick one that C compilers naturally
>> do anyway.
>>
>
> Speaking as a generalisation, no that is not true.  C functions for
> x86/x86_64 all seem to evaluate their args right-to-left. However - and
> this may be different from compiler to compiler - GCC at least does
> left-to-right evaluation for everything else (ARM, SPARC, etc) because
> that is the order that arguments are pushed onto the stack for those
> architecture backends.
>
> If right-to-left should be the strict standard to follow for extern(C),
> then document this behaviour as part of the specification, and I'll
> implement it. :~)
>
> Regards
> Iain

There is a pull request that does this, but it has not yet been merged because it's waiting on compilers to catch up... ;)

-- 
Alex Rønne Petersen
alex@lycus.org
http://lycus.org
September 26, 2012
On 26-09-2012 14:04, Timon Gehr wrote:
> On 09/26/2012 10:11 AM, Walter Bright wrote:
>> On 9/26/2012 12:36 AM, Iain Buclaw wrote:
>>> On Tuesday, 25 September 2012 at 23:39:39 UTC, Walter Bright wrote:
>>>> On 9/25/2012 3:58 PM, Iain Buclaw wrote:
>>>>> The problem here is that the array operation A[] = B[] + C[] gets
>>>>> transformed
>>>>> into an extern(C) call.  And because there's no strict rules in
>>>>> place over the
>>>>> order of which it's parameters are evaluated, it could go either way
>>>>> (LTR, or
>>>>> RTL).
>>>>>
>>>>> Serious note: This test is bogus as this and similar other failing
>>>>> tests on
>>>>> non-x86 platforms are not at all obvious to the users who get
>>>>> issues. So what
>>>>> are we to do about it?
>>>>
>>>> D needs to move towards a defined order of evaluation. I understand
>>>> that
>>>> there's a problem when using parts of a C compiler that feels free to
>>>> reorder
>>>> within the C rules. It's something we have to deal with sooner or
>>>> later, by
>>>> either:
>>>>
>>>> 1. adjusting the C optimizer so it follows D rules for D code
>>>>
>>>> 2. assigning terms to temporaries that are executed in a specific
>>>> order by C
>>>> rules
>>>
>>>
>>> Indeed, but where does that leave code that gets compiled down to a
>>> extern(C) call?
>>
>> C functions all seem to evaluate their args right-to-left, even though
>> the C Standard doesn't specify that.
>
> On x86. It is unspecified, therefore different compilers on different
> architectures do it in different ways.
>
>> So we should be all right by simply
>> defining that D do it that way for C functions.
>>
>
> I disagree. Why should the calling convention have an impact on code
> semantics? The code reads LTR, therefore evaluation should consistently
> be LTR.

Completely agree with this. Calling convention is irrelevant.

Let us not inherit the C/C++ mess.

>
>> It doesn't actually matter what order D does things, we just have to
>> pick one. And so we might as well pick one that C compilers naturally do
>> anyway.
>> ...
>
> There is no such thing. C and C++ get evaluation order thoroughly wrong.
>

-- 
Alex Rønne Petersen
alex@lycus.org
http://lycus.org
September 26, 2012
On 26-09-2012 15:34, monarch_dodra wrote:
> On Wednesday, 26 September 2012 at 12:48:19 UTC, Timon Gehr wrote:
>> On 09/26/2012 02:07 PM, monarch_dodra wrote:
>>> On Tuesday, 25 September 2012 at 22:58:11 UTC, Iain Buclaw wrote:
>>>> Pop quiz!
>>>>
>>>> Without cheating, I invite people to have a good guess what 'abc' is
>>>> equal to, but just to narrow it down.
>>>>
>>>> 1)  It isn't "ABC".
>>>> 2)  On x86/x86_64, it isn't "ACB".
>>>> 3)  On everything else, it's the reverse of what you'd expect on
>>>> x86/x86_64.
>>>
>>> I'd say abc's value is "unspecified", and any attempt at predicting it
>>> would be bogus. That's my answer anyways
>>> ...
>>
>> Why? This is not useful behaviour.
>
> What do you mean "why"? Because order of evaluation is not specified, so
> the value of abc is not specified. That was my answer. The question
> wasn't really "what would useful behavior be".
>
> IMO: useful behavior would be if it was explicitly illegal to modify (or
> modify + read) the same value twice in the same expression. I'd rather
> expressions such as:
> A()[] = B()[] + C()[];
> x[i] = ++i + 1;
>
> Be illegal rather than have (an arbitrarily defined) specified behavior.
> Trying to specify a specific behavior in such cases is opening a can of
> worms.
>

No it isn't. There's a perfectly sensible, sane, and intuitive fix for this: always evaluate arguments left-to-right. No exceptions.

It's not that complicated.

-- 
Alex Rønne Petersen
alex@lycus.org
http://lycus.org
September 26, 2012
On Wednesday, 26 September 2012 at 12:29:38 UTC, Iain Buclaw wrote:
> On 26 September 2012 13:07, monarch_dodra <monarchdodra@gmail.com> wrote:
>> I don't see how the "extern(C)" is involved here, since it is the D compiler
>> that first evaluates A(), B() and C() before passing the making the C
>> function call. Or did I miss something?
>>
>
> There is no physical code generation from the frontend that says
> "evaluate this".  What it passes to be backend for this operation is a
> function call. So the backend determines the order of evaluation
> depending on the order of parameters.

I'm not quite sure what you are trying to say here; I guess it depends on the definition of "backend". monarch_dodra is right in so far as the effects of "extern(C)" are an implementation detail coming from the particular way your compiler "glue code" is written; there is no fundamental reason why it should be important for parameter evaluation at all.

Speaking of it, what is the reason that the actual order of formal parameters (in terms of register allocation/stack layout) is reversed in the D calling convention, compared to extern (C) (e.g. on *nix x86_64)?

David