Example

The following is a small C program, trivial.c (which is very similar to a mini-Oberon program), and the resulting output from the cc compiler (/opt/SUNWspro/bin/cc to be exact) with the command cc -S trivial.c (I've added the long comments). Not all of this will make sense immediately, but it should give you a good idea of what to aim for.

 


main ()
{
        int a, b, c;
        a = 99;
        b = a + two();
        if (b > 100) 
                printf ("%d\n", b);
}

two ()
{
        return 2;
}

--------------------------------------------------------------------

!  Header stuff first.  Declare that we are writing code: "text"
!  The next few lines are needed for each function that is declared.
!  These are directions to the assembler.  They are not real instructions;
!  they're declarations.  All "dot" commands are declarations of some kind.

        .section        ".text",#alloc,#execinstr
        .align  8
        .skip   16

        ! block 0

!  This makes main a label that is visible outside this file.
!  I don't know what the second command does.  Finally, we get
!  the actual goto label for main.  "call main" will result in
!  a jump to this spot in the code, plus some other things.

        .global main
        .type   main,2
main:

!  The next line sets up storage for main's local variables
!  (rather cryptically, I might add).  The important value to look at
!  is -112, which is set to the actual number of bytes of space that
!  main's activation record requires.  How this number is calculated
!  will be discussed later.  One thing worth knowing now is that the
!  code generator doesn't actually know this value when it is at this
!  part of the program.  So we'll use a name here and give the name a
!  value at the *bottom* of the file.

        save    %sp,-112,%sp

        ! block 1
.L14:

! File ex.c:
!    1  int main ()
!    2  {
!    3          int a, b, c;
!    4          a = 99;

!  The next two lines perform an assignment of the value 99
!  to the variable 'a'.  'a' is the main's first local variable -- all
!  local variables are referenced using the frame pointer (%fp) -- in
!  this case, the address of 'a' is found by subtracting 8 from fp.
|  For most assembly languages, one move instruction would suffice for
!  a simple assignment like this -- however, the Sparc does not allow
!  a constant to be assigned to a stack location with a 'mov', so
!  we first store ('st') the constant in a local register (%l0), and
!  then move the register value to the stack location.

        mov     99,%l0          ! note: everything done out of registers
        st      %l0,[%fp-8]     ! store 99 in a's location

!    5          b = a + two();

!  We now want to perform the add.  This requires that we reload a into
!  a register and call two() and retrieve its return value.
!  The 'nop' is needed after every function call due to the Sparc's
!  tendency to pipeline (pre-fetch) instructions (we'll partially
!  explain this in class -- don't worry about now -- just get in the
!  habit of always outputing a 'nop' after a 'call').  BTW, the way we
!  will handle registers may do all the register loading *after* all the
!  functions are called, etc. in the expression.

        ld      [%fp-8],%l0     ! load a back into a register
        call    two
        nop

!  By convention, the return value of a function will be in register
!  %o0.  The next three instructions, therefore, perform the add of 'a'
!  and the result of 'two', storing the result in register %l0.
!  (Personally, I would used %l2 for the result rather than reuse %l0.)
!  Finally, take the result of the addition, and store in 'b'.  'b' is main's
!  second local variable, so it's address is 4 more bytes from the frame
!  pointer than 'a', 12 bytes total.

        mov     %o0,%l1         ! retrieve result from output register
        add     %l0,%l1,%l0     ! do the add
        st      %l0,[%fp-12]    ! store result in b

!    6          if (b > 100)

!  Now we want to compare 'b' with 100.  We move 'b' to
!  register %l0, compare, and branch on *false*.  Note the nop again --
!  we'll need one after every branch as well as function call.

        ld      [%fp-12],%l0    ! load b
        cmp     %l0,100         ! compare and
        ble     .L16            ! branch on *inverted* condition
        nop

        ! block 2
.L17:

!    7                  printf ("%d\n", b);

!  If we make it here, we want to print.  The compiler makes a
!  normal function call to the C printf library routine, passing
!  it two parameters -- the format string, and the variable.  Below
!  then, is declared the print string as a constant -- the "address"
!  of this constant (.L19) will be the parameter to printf.  I would
!  prefer to just dump the string here rather than save it for the end,
!  but it doesn't really matter: recall that the data is and text are
!  stored in two completely different memory segments.

        sethi   %hi(.L19),%l0           ! get fancy to load large number/addr
        or      %l0,%lo(.L19),%l0       !   which is pointer to format string
        ld      [%fp-12],%l1            ! load b
        mov     %l0,%o0                 ! move into argument-passing regs
        mov     %l1,%o1
        call    printf                  ! call
        nop

!  We're done with this function (.L16 is the "else" part of the
!  if statement we handled above, and .LE13 is apparently the "end-
!  of-function" label.  All we need to do is return.  This is done
!  with a jump off the value of the input register %i7, the saved PC.
!  Adding skips over the original call instruction and the nop after it.
!  The 'restore' instruction does some cleanup -- the reason it comes after
!  the return ('jmp') is again due to Sparc pipelining.  Rather than
!  doing a jump, you can also use the "ret" macro, which does exactly
!  the same thing.  The last two commands are just cleanup stuff for the
!  assembler.

        ! block 3
.L16:
.L13:
        jmp     %i7+8                   ! this is a ref to the return PC
        restore                         ! in delay slot, execs with jmp
        .size   main,(.-main)
        .align  8

! Set up sequence happens all over again.  Note similarities and differences.

        .align  8
        .skip   16

        ! block 0

        .global two
        .type   two,2
two:

        save    %sp,-104,%sp    ! smaller value here because no locals.

        ! block 1
.L22:

! File ex.c:
!    8  }
!    9  
!   10  int two ()
!   11  {
!   12          return 2;

        mov     2,%l0
        st      %l0,[%fp-4]
        ba      .L21
        nop

        ! block 2
.L21:
        ld      [%fp-4],%l0
        mov     %l0,%i0
        jmp     %i7+8
        restore
        .size   two,(.-two)
        .align  8


! The compiler saved this constant until the end, but it can be put anywhere
! as long as it is declared to be in the data segment and then the text
! segment is redeclared to continue generating code.

        .section        ".data1",#alloc,#write
        .align  4

.L19:
        .ascii  "%d\n\000"
        .type   .L19,#object
        .size   .L19,4

! Random footer stuff that you probably don't need.

        .file   "trivial.c"
        .xstabs ".stab.index","Xa ; V=3.1 ; R=WorkShop Compilers 4.2 01 Oct 1997 C 4.2 patch 104668-05",60,0,0,0
        .xstabs ".stab.index","/net/cs/htdocs/users/wgg/CSE131B/Project; /opt/SUNWspro/bin/../SC4.2/bin/cc -S  trivial.c -W0,-xp",52,0,0,0
        .xstabs ".stab.index","main",42,0,0,0
        .ident  "acomp: WorkShop Compilers 4.2 01 Oct 1997 C 4.2 patch 104668-05"


!  This is how the compiler declares constants.  It can declare them at the
!  and still use them anywhere in the file.

        .global __fsr_init_value
__fsr_init_value = 0x0