07. Call frame and macros

Frame pointer

(Very slightly) complex example:

   1         # ... some code, now calling subr
   2 
   3         # preamble: passing arguments
   4         addiu   $sp $sp -4
   5         sw      $t0 ($sp)       # variable A0 +16
   6         addiu   $sp $sp -4
   7         sw      $t1 ($sp)       # variable B0 +12
   8         addiu   $sp $sp -4
   9         sw      $t2 ($sp)       # variable A1 +8
  10         addiu   $sp $sp -4
  11         sw      $t3 ($sp)       # variable B1 +4
  12         addiu   $sp $sp -4
  13         sw      $t4 ($sp)       # variable x
  14 
  15         jal     subr
  16 
  17         # now restoring $sp dropping 5 cells
  18         addiu   $sp $sp 20
  19 
  20         # ... some other code
  21         break 0
  22 
  23 subr:   # Prologue: keep $ra and all used $s*; allocate local vars
  24         addiu   $sp $sp -4      # storing $ra
  25         sw      $ra ($sp)
  26 
  27         # we will use $s0 as non-volatile register, keep it
  28         addiu   $sp $sp -4
  29         sw      $s0 ($sp)       # variable x
  30 
  31         # we want to use local variable Y at stack, allocate it
  32         addiu   $sp $sp -4
  33         sw      $zero ($sp)
  34 
  35         # Prologue end
  36         # Now Y is ($sp), x is 12($sp), B1 is 16($sp) … a is 28($sp)
  37 
  38         # … do something
  39 
  40         # Epilogue:
  41         # drop local variables
  42         addiu $sp $sp 4
  43 
  44         # restore $s* and $ra
  45         lw  $s0 ($sp)
  46         lw  $ra 4($sp)
  47         addiu $sp $sp 8
  48 
  49         jr  $ra

We need to:

Offset recalculating is hell. Let's evade this sacrificing a register — $fp, frame pointer.

$fp:

Simplified calling convention with frame

As always, there's 4 parts: preamble, prologue, epilogue and stack restore

  1. (preamble) Up to 4 arguments are stored in $a0$a3

  2. (preamble) More than 4 arguments are stored in stack
  3. jal to call

  4. No stack modification above current ($sp)
  5. (prologue) Store current $fp (outer subroutine frame) on -4($sp), no $sp change here

  6. (prologue) Store current $sp to $fp

  7. (prologue) Allocate stack space for $ra, $s* and local variables

  8. (prologue) Store $ra, $s* and local variables to allocated memory (using fixed $fp offset is possible)

  9. Return value(s) in $v0($v1)

  10. (epilogue) Restore $ra and $s*

  11. (epilogue) Drop the whole frame by restoring $sp from $fp,

  12. Return with jr $ra

  13. (stack restore) If there was more than 4 arguments, drop them from stack

Notes:

.eqv directive and .text/.data mixing

Compile this:

   1 .eqv  VarC  -4($sp)
   2 .eqv  VarD  -8($sp)
   3 
   4 .data
   5 
   6 AA:   .word 1234
   7 
   8 .text
   9       lw    $t0 BB
  10       sw    $t0 VarC
  11 
  12 .data
  13 BB:   .word 5678
  14 
  15 .text
  16       lw    $t0 AA
  17       sw    $t0 VarD

Frame usage example

   1 .data
   2 .eqv    SZ 20                   # «constant»
   3 Arr:    .space  SZ
   4 EndArr: .align  2               # just to mark array end
   5 .text
   6 
   7 main:   la      $t1 Arr         # input integers
   8         la      $t2 EndArr
   9 input:  li      $v0 5
  10         syscall
  11         sw      $v0 ($t1)
  12         addiu   $t1 $t1 4
  13         bne     $t2 $t1 input
  14 
  15         li      $a0 SZ          # call sort
  16         la      $a1 Arr
  17         jal     sort
  18 
  19         move    $t1 $v1         # Address
  20         move    $t2 $v0         # size in bites
  21         sra     $t2 $t2 2       # sise in words
  22 
  23         li      $v0 11
  24         li      $a0 '\n'
  25         syscall
  26 
  27 output: li      $v0 1
  28         lw      $a0 ($t1)
  29         syscall
  30         li      $a0 '\n'
  31         li      $v0 11
  32         syscall
  33         sw      $v0 ($t1)
  34         addiu   $t1 $t1 4
  35         addiu   $t2 $t2 -1
  36         bgtz    $t2 output
  37 
  38         li      $v0 10
  39         syscall
  40 
  41 sort:   # a0array size
  42         # a1array address
  43         # v0output array size (same as input :)
  44         # v1aoutput array address (same as input :)
  45 .eqv    .RA     -8($fp)         # Offest of $ra storage
  46 .eqv    .S0     -12($fp)        # Offset of $s0 storage
  47 .eqv    .S1     -16($fp)        # Offest of $s1 storage
  48 .eqv    .ARR    -20($fp)        # Local variable
  49 .eqv    .SIZE   -24($fp)        # Local variable
  50 .eqv    FSIZE   -24             # Frame size
  51         sw      $fp -4($sp)     # Store old frame
  52         move    $fp $sp         # Allocate new frame
  53         addiu   $sp $sp FSIZE   # …on stack
  54         sw      $ra .RA         # Store registers
  55         sw      $s0 .S0
  56         sw      $s1 .S1
  57 
  58         sw      $a0 .SIZE       # Store arguments: size
  59         sw      $a1 .ARR        # …and array address
  60 
  61         move    $s0 $a0         # We can use $s* non-volatile registers
  62         move    $s1 $a1         # when calling any subroutine
  63 fnext:  move    $a0 $s0
  64         move    $a1 $s1
  65         jal     getmin
  66         lw      $t0 ($v0)
  67         lw      $t1 ($s1)
  68         sw      $t0 ($s1)
  69         sw      $t1 ($v0)
  70         addiu   $s1 $s1 4
  71         addiu   $s0 $s0 -4
  72         bleu    $a0 4 fnext
  73 
  74         lw      $v0 .SIZE       # local variables
  75         lw      $v1 .ARR
  76 
  77         lw      $ra .RA         # Restore registers
  78         lw      $s0 .S0
  79         lw      $s1 .S1
  80         move    $sp $fp         # Restore stack
  81         lw      $fp -4($sp)     # Restore old frame
  82         jr      $ra
  83 
  84 getmin: # terminal subroutine, we can relax a bit :)
  85         # a0array size
  86         # a1array address
  87         # v0return minimal element address
  88         lw      $t0 ($a1)
  89         move    $v0 $a1
  90 gmnext: addiu   $a0 $a0 -4
  91         addiu   $a1 $a1 4
  92         blez    $a0 gmfin
  93         lw      $t1 ($a1)
  94         bge     $t1 $t0 gmnext
  95         move    $t0 $t1
  96         move    $v0 $a1
  97         j       gmnext
  98 gmfin:  jr      $ra

Notes

Industrial conventions

What we need to add to convention to make in «universal»?

Full ABI documentation (the more universal is ABI, the more complex it became):

Industrial ABI is not intended for use manually, in mostly describes what code higher-language compilers (e. g. C compiler) shall generate.

Plus:

Strings

String is just byte sequence in memory with undefined size. String operations (s. a. input/output) process all the non-zero bytes until. Zero byte is string terminator (aka EOL).

Strings are just conventions and has no hardware support in MIPS.

   1 .data
   2 Ex1:    .asciiz     "This is example"
   3 Ex2:    .asciiz     "This is\nanother example\n"
   4 Ex3:    .ascii      "This is example without zero."
   5 Ex4:    .ascii      "Nobody khows when it terminates"
   6         .byte       33
   7         .byte       0
   8 .text
   9         li          $v0 4
  10         la          $a0 Ex1
  11         syscall
  12         li          $v0 4
  13         la          $a0 Ex2
  14         syscall
  15         li          $v0 4
  16         la          $a0 Ex3
  17         syscall
  18         li          $v0 4
  19         la          $a0 Ex4
  20         syscall

Macros

docimentation

Macro:

Simple form:

.macro  name
        ...
        any code
        ...
.end_macro

E. g.

   1 .macro  exit
   2         li      $v0 10
   3         syscall
   4 .end_macro
   5 
   6 # ... code
   7         exit

Parametric macro:

.macro  name      %parameter1 %parameter2 …
# ... code using %parameter1 %parameter2 etc.
.end_macro

E. g.

   1 .macro  printN  %reg
   2         li      $v0 1
   3         move    $a0 %reg
   4         syscall
   5         li      $v0 11
   6         li      $a0 '\n'
   7         syscall
   8 .end_macro
   9 # ...
  10         li      $t5 100500
  11         printN  $t5

Note:

Local labels:

.macro  printS %message
.data
msg:    .asciiz %message
.text   li  $v0 4
        la  $a0 msg
        syscall
.end_macro
# ....
        printS  "Hello!"

Macro explosion: when expanding macros within macros within macros etc, te size of resulting code can be abruptly™ huge.

H/W

  1. EJudge: ASCIIGrid 'Character grid'

    Wrirte a program that inputs ordinals M an N, and outputs MxN grid made with «+» and «-». You should write a macro that accepts three parameters: a number of cells and two characters, and outputs a line like this:

    • printline 4, '+', '-'

      +-+-+-+-+

    Input:

    3
    4
    Output:

    +-+-+-+
    | | | |
    +-+-+-+
    | | | |
    +-+-+-+
    | | | |
    +-+-+-+
    | | | |
    +-+-+-+
  2. EJudge: CrtDraw 'ASCII art'

    Write a program, that input an odd number of integers. Each odd input is x coordinate, and each even input is y coordinate of a certain dot. If x is negative, input ends immediately and the program outputs 16×16 character matrix, containing '*' at places of the dots and '.' at empty places. The 0:0 coordinate is at upper-left corner (OX points right, OY points down).

    Input:

    1
    1
    2
    2
    13
    2
    12
    3
    11
    11
    12
    12
    1
    14
    0
    15
    -1
    Output:

    ................
    .*..............
    ..*..........*..
    ............*...
    ................
    ................
    ................
    ................
    ................
    ................
    ................
    ...........*....
    ............*...
    ................
    .*..............
    *...............
  3. EJudge: KeySort 'Key sorting'

    Write a program of key sorting. You should write a subroutine that accepts three arguments: array size (in words, not in bytes), array address, and comparison subroutine. You subroutine sorts an array and return array size in $v0 and array address in $v1. You subroutine should use comparing subroutine to determine if two elements is in order. Comparing subroutine accepts integers in $a0 and $a1 and returns 1 in $v0 if they're in proper order, and 0 otherwise. Write two comparing subroutines: first for $a0 < $a1, second for $a0%10 > $a1%10. Please use bubble sort algorithm (ar any other stable). Your program reads an ordinal N, then an integer T, then N integers. If T is 0, use first comparison subroutine, if T != 0, use second one. Then output an array.

    Input:

    9
    0
    34
    456
    2
    5
    567
    2
    2
    0
    42
    Output:

    0
    2
    2
    2
    5
    34
    42
    456
    567
  4. EJudge: ReverseString 'Reverse string'

    Write a program which accepts a sequence of non-empty strings (each not longer than 200 characters) and outputs every string backwards. Sequence ends with a string started from '.'; this final string is not printed. You should write a subroutine which accepts string address in $a0 and prints it backwards. Caution: if you want to use syscall 8, read carefully the documentation about '\n' at the end of the input string (it either can evolve or not, you should omit it).

    Input:

    Write a program which accepts a sequence of strings
    and outputs every string backwards.
    Empty strings are treated as normal.
    .that's all
    Output:

    sgnirts fo ecneuqes a stpecca hcihw margorp a etirW
    .sdrawkcab gnirts yreve stuptuo dna
    .lamron sa detaert era sgnirts ytpmE

HSE/ArchitectureASM/07_FrameAndMacros (последним исправлял пользователь FrBrGeorge 2019-12-08 15:11:17)