Rational Developer for System z
PL/I for Windows, Version 8.0, Programming Guide

Examples using the CDECL convention

The following examples are included for purposes of illustration and clarity only. They have not been optimized. The examples assume that you are familiar with programming in assembler. In the examples, the stack grows toward the bottom of the page, and ESP always points to the top of the stack.

Consider the following call:

  m = func(a,b,c);

The variables a, b, and c are 32-bit integers and FUNC has two local variables, x and y (both 32-bit integers).

The stack for the call to FUNC would look like this:

                                     Stack

                           .------------------------.       Higher Memory
                           |           c            |
                           '------------------------'
                           |           b            |
                           '------------------------'
                           |           a            |
                           '------------------------'
                           |     caller's EIP       |
                           '------------------------'
                           |     caller's EBP       |
           EBP  ------>    '------------------------'
                           |           x            |
                           '------------------------'
                           |           y            |
                           '------------------------'   <-.
                           |       Saved EDI        |     |
                           '------------------------'     | These would only
                           |       Saved ESI        |     | be pushed if they
                           '------------------------'     | were used in this
                           |       Saved EBX        |     | function.
           ESP  ------>    '------------------------'   <-'
                                                            Lower Memory

The instructions used to create this activation record on the stack look like this on the calling side:

  PUSH c
  PUSH b
  PUSH a
  CALL _func
    .
    .
  ADD  ESP, 12   : cleaning up the parameters
    .
    .
  MOV  m, EAX
    .
    .

For the callee, the code looks like this:

_func PROC
      PUSH    EBP
      MOV     EBP, ESP           ; Allocating 8 bytes of storage
      SUB     ESP, 08H           ;  for two local variables.
      PUSH    EDI                ; These would only be
      PUSH    ESI                ;  pushed if they were used
      PUSH    EBX                ;  in this function.
      .
      .
      MOV     EAX, [EBP - 8]    ; Load y into EAX
      MOV     EBX, [EBP + 12]   ; Load b into EBX
      .
      .
      XOR     EAX, EAX           ; Zero the return value
      POP     EBX                ; Restore the saved registers
      POP     ESI
      POP     EDI
      LEAVE                      ; Equivalent to  MOV   ESP, EBP
                                 ;                POP   EBP
      RET
_func ENDP

The saved register set is EBX, ESI, and EDI. In the case where the structure is passed as a value parameter and the size of the structure is 5, 6, 7, or more than 8 bytes in size, the address to place the return values is passed as a hidden parameter, and the address passed back in EAX.

Rational Developer for System z
PL/I for Windows, Version 8.0, Programming Guide