### Passing and Returning Aggregates by Value to a Prototyped Routine

If an aggregate is passed by value, the following code sequences are produced for the caller and callee:

    'C' Source:

       struct s_tag {
                 long  a;
                 float b;
                 long  c;
                 } x, y;
       long  z;
       double q;

       /* Prototype */
       struct s_tag bar(long lvar, struct s_tag aggr, float fvar);
        ·

       /* Actual Call */
       y = bar(z, x, q);
        ·

       /* callee */
       struct s_tag bar(long lvar, struct s_tag aggr, float fvar)
       {
          struct s_tag temp;

          temp.a = lvar + aggr.a + 23;
          temp.b = fvar - aggr.b;
          temp.c = aggr.c;

          return temp;
       }

Caller's code up until call:

       FLD     QWORD_PTR q          ; Load lexically first floating-point
                                    ;  parameter to be converted
       FSTP    DWORD_PTR [EBP - T1] ; Convert to formal parameter type by
       FLD     DWORD_PTR [EBP - T1] ; Storing and loading from a temp (T1)
       SUB     ESP, 4               ; Allocate space for the floating-point
                                    ;  register parameter
       PUSH    x.c                  ; Push nonconforming parameters on
       PUSH    x.b                  ;  stack
       PUSH    x.a                  ;
       MOV     EAX, Z               ; Load lexically first conforming
                                    ;  parameter into EAX
       SUB     ESP, 4               ; Allocate stack space for the first
                                    ;  general-purpose register parameter.
       PUSH    addr y               ; Push hidden first parameter (address of
                                    ;  return space)
       CALL    BAR

Callee's prolog code:

       PUSH     EBP          ; Save caller's EBP
       MOV      EBP, ESP     ; Set up callee's EBP
       SUB      ESP, 12      ; Allocate callee's Local
                             ;  = sizeof(struct s_tag)
       PUSH     EBX          ; Save preserved registers -
       PUSH     EDI          ;  will optimize to save
       PUSH     ESI          ;  only registers callee uses

Note: the term undefined in registers ECX and EDX refers to the fact that they can be safely overwritten by the code in bar.

       temp.a = lvar + aggr.a + 23;
       temp.b = fvar - aggr.b;
       temp.c = aggr.c

       return temp;

       ADD       EAX, 23               ;
       ADD       EAX, [EBP + 16]       ; Calculate temp.a
       MOV       [EBP - 12], EAX       ;

       FSUB      DWORD_PTR [EBP + 20]  ; Calculate temp.b
       FSTP      DWORD_PTR [EBP - 8]   ;

       MOV       EAX, [EBP + 24]       ; Calculate temp.c
       MOV       [EBP - 4], EAX        ;

       MOV       EAX, [EBP + 8]        ; Load hidden parameter (address
                                       ;  of return value storage). Useful
                                       ;  both for setting return value
                                       ;  and for returning address in EAX.

       MOV       EBX, [EBP - 12]       ; Return temp by copying its contents
       MOV       [EAX], EBX            ;  to the return value storage
       MOV       EBX, [EBP - 8]        ;  addressed by the hidden parameter.
       MOV       [EAX + 4], EBX        ;  String move instructions would be
       MOV       EBX, [EBP - 4]        ;  faster above a certain threshold
       MOV       [EAX + 8], EBX        ;  size of returned aggregate.

       POP       ESI                   ; Begin Epilog by restoring
       POP       EDI                   ;  preserved registers.
       POP       EBX
       MOV       ESP, EBP              ; Deallocate callee's local
       POP       EBP                   ; Restore caller's EBP
       RET                             ; Return to caller

Caller's code just after call:

       ADD    ESP, 24       ; Remove parameters from stack
        ...                 ; Because address of y was given as the
                            ;  hidden parameter, the assignment of the
                            ;  return value has already been performed.

If a `y.a = bar(x).b` construct is used instead of the more common `y = bar(x)` construct, the address of the return value is available in EAX. In this case, the address of the return value (hidden parameter) would point to a temporary variable allocated by the compiler in the automatic storage of the caller.

Calling Conventions

Examples of Passing Parameters Using _Optlink