September 26, 2012
On Wednesday, 26 September 2012 at 15:12:05 UTC, Alex Rønne Petersen wrote:
> On 26-09-2012 15:34, monarch_dodra wrote:
>> [SNIP]
>
> 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.

You know, I think you are right on second though. However, it is a matter of not mistaking "left to right evaluation of arguments" with normal the associative behavior of operators.

EG: (a = (b = c) = d):
*a is evaluated
*b is evaluated
*c is evaluated
*(b = c) is evaluad into a temp called bc
*d is evaluated
*bc = d is evaluated into bcd
*a = bcd is evaluated.

So back to my example: (where i starts at 1)
x[i++] = ++i + 1;
*i++ is evaluated to 1, and i is now 2.
*x[i++] is now a reference to x[1]
*++i is evaluated to 3
*++i + 1 is 4
*x[1] becomes 4

Yeah... it looks like it works.

September 26, 2012
On 26 September 2012 16:25, David Nadlinger <see@klickverbot.at> wrote:
> 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.
>

Yes, this is what's done in GDC.  All parameters with side effects have their result evaluated and saved before calling the extern(D) functions.

> 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)?
>

DMD pushes arguments on the stack in reverse order to that of x86/x86_64.  So it gives the *impression* of LTR evaluation, however, the parameters themselves aren't actually explicitly evaluated / saved in temporaries prior to calling.

Regards
-- 
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';
September 26, 2012
On 26 September 2012 16:10, Alex Rønne Petersen <alex@lycus.org> wrote:
> 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... ;)
>

Got a link? :~)

-- 
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';
September 26, 2012
On Wednesday, September 26, 2012 17:12:49 Alex Rønne Petersen wrote:
> On 26-09-2012 15:34, monarch_dodra wrote:
> > 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.

I'd still consider it to be bad practice to do anything which relies on the order of evaluation of function arguments, because the order is undefined in other languages, and if you get into the habit of doing stuff like func(++i, ++i, ++i) in D, you're going to be screwed when you have to program in other languages. It's just cleaner to avoid that kind of stuff, and I'd love it if it were illegal (a single ++i is fine - it's using i in multiple arguments _and_ modifying it in one of them which is the problem). That being said, defining the order will at least reduce bugs, even if it's considered bad practice to do anything in a function call which would rely on the arguments being evaluated in a particular order.

- Jonathan M Davis
September 27, 2012
On 26-09-2012 19:23, Jonathan M Davis wrote:
> On Wednesday, September 26, 2012 17:12:49 Alex Rønne Petersen wrote:
>> On 26-09-2012 15:34, monarch_dodra wrote:
>>> 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.
>
> I'd still consider it to be bad practice to do anything which relies on the
> order of evaluation of function arguments, because the order is undefined in
> other languages, and if you get into the habit of doing stuff like func(++i,
> ++i, ++i) in D, you're going to be screwed when you have to program in other
> languages. It's just cleaner to avoid that kind of stuff, and I'd love it if it
> were illegal (a single ++i is fine - it's using i in multiple arguments _and_
> modifying it in one of them which is the problem). That being said, defining
> the order will at least reduce bugs, even if it's considered bad practice to
> do anything in a function call which would rely on the arguments being
> evaluated in a particular order.
>
> - Jonathan M Davis
>

I do not know any other languages than C and C++ that leave it undefined.

-- 
Alex Rønne Petersen
alex@lycus.org
http://lycus.org
September 27, 2012
On 9/26/2012 5:04 AM, Timon Gehr wrote:
> Why should the calling convention have an impact on code
> semantics?

Maximizing performance.

> The code reads LTR, therefore evaluation should consistently be LTR.

The best order is the one that generates the fewest temporaries, and that's the order that should be defined for D.
September 27, 2012
On 27 September 2012 11:38, Walter Bright <newshound2@digitalmars.com> wrote:
> On 9/26/2012 5:04 AM, Timon Gehr wrote:
>>
>> Why should the calling convention have an impact on code semantics?
>
>
> Maximizing performance.
>
>
>> The code reads LTR, therefore evaluation should consistently be LTR.
>
>
> The best order is the one that generates the fewest temporaries, and that's the order that should be defined for D.

Can't see how you can get any fewer temporaries when following the rule:  If it has side-effects, cache it's return value.


-- 
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';
September 27, 2012
Walter Bright:

> The best order is the one that generates the fewest temporaries, and that's the order that should be defined for D.

Java and Python and C# give a precedent, maybe many programmers expect a behavior like those ones.

Bye,
bearophile
September 27, 2012
On 09/27/2012 12:38 PM, Walter Bright wrote:
> On 9/26/2012 5:04 AM, Timon Gehr wrote:
>> Why should the calling convention have an impact on code
>> semantics?
>
> Maximizing performance.
>

Optimisations by definition do not have an impact on code semantics.

>> The code reads LTR, therefore evaluation should consistently be LTR.
>
> The best order is the one that generates the fewest temporaries, and
> that's the order that should be defined for D.

The number of temporaries is not affected by the order of
evaluation. Just push the arguments on the stack out-of-order.
Furthermore, the 'natural' order of C argument evaluation differs
between architectures, making the entire point moot.
September 27, 2012
On 09/27/2012 01:43 PM, bearophile wrote:
> Walter Bright:
>
>> The best order is the one that generates the fewest temporaries, and
>> that's the order that should be defined for D.
>
> Java and Python and C# give a precedent, maybe many programmers expect a
> behavior like those ones.
>

Well, those can use a calling convention that optimally fits the
evaluation order, as they do not provide zero-overhead interaction with
C code.

1 2 3
Next ›   Last »