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

Reply via email to