On Wednesday, 2 April 2014 at 14:43:44 UTC, Iain Buclaw wrote:
Please do not get confused between operands evaluation order
in an
expression and arguments passing order to a function. Those
are two
different things. I was talking about both of them because
both of them are
involved in the evaluation of a()[] = b()[] + c()[]. To a
programmer this is
an expression that should follow expression evaluation rules.
To a compiler
implementer, this is a builtin function call whose arguments
should be
evaluated such that the expression evaluation rules are not
broken.
Right. But order of evaluation is Language-specific, order of
pushing
arguments is Target-specific. Both are completely indifferent
from
each other, and this is what I think you are not understanding.
I started my career, 19 years back, as a C compiler developer. So
I know what is evaluation order and argument passing order. And
more importantly, the discussion is about the *evaluation order*
of "a()[] = b()[] + c()[]" and not about what I understand or
don't! So if you have any valid points that says why this
expression should be evaluated in LTR order (i.e. first a then b
and then c) let us discuss that.
You can write a small code that evaluates "a()[] = b()[] + c()[]"
before and after the proposed modifications and check whether the
evaluation order is same w.r.t dmd. DMD v2.64 evaluates first b,
then c and then a. This behaviour conforms to the D spec.
If you read the last para in my first post, I was talking
about argument
pushing order *not* evaluation order for function args. The
function
argument passing order (called calling convention) is not
defined by C spec,
but by C ABI spec of any architecture. In all the C calling
conventions, the
first few arguments are passed in registers and the remaining
on the stack.
On Linux+x86, all the arguments are passed on the stack. For
C, the
arguments that are passed on the stack are in reverse order
i.e RTL. Since
the proposal was to change the argument evaluation order for
extern(C)
functions,
And the pushing order is unaffected, so why bring it up in the
first place?
Let me take an example to explain what I'm trying to say.
extern (C) int foo(int a, int b);
void main(void)
{
foo(a(), b());
}
With RTL function argument evaluation order and with push
instructions, the above code gets compiled by dmd as (only
relevant asm code shown) (on x86)
main:
call b
push %eax
call a
push %eax
call foo
Now if the evaluation order of function args is changed to LTR,
the new asm code would be
main:
call a
mov %eax, %esi
call b
push %eax
push %esi
call foo
Notice the additional mov instruction to save the return value of
a() in a temporary. This is the impact that I'm talking about.
Now if dmd backend uses mov instructions to push args on to the
stack instead of push, then there will not be a need for
temporary. But the code size will increase as push is only 1 byte
where as mov %eax offset(%esp) is 3 to 4 bytes long.
Asm code with LTR func args evaluation order for extern(C) foo
with mov instrs
main:
call a
mov %eax, (%esp)
call b
mov %eax, 0x4(%esp)
call foo
Notice that the args are still pushed in RTL order.
I was merely pointing out that this will have an impact on the
dmd backend because it uses pushl instructions. Notice that
for extern (C)
functions, the argument evaluation order and argument pushing
order is same.
So dmd evaluates an argument and pushes it immediately. If the
evaluation
order is opposite to that of the pushing order, then it cannot
immediately
push the argument that it has evaluated. However if it uses
movl
instructions as is done by gcc backend, then there is no issue.
Actually, the gcc backend does the same if the parameter passed
has
not had all side effects removed from it.
- Sarath