Description:

Register convention:

RegisterABI NameDescriptionSaver
x0zeroHard-wired zero
x1raReturn addressCaller
x2spStack pointerCallee
x3gpGlobal pointer
x4tpThread pointer
x5-7t0-2TemporariesCaller
x8s0/fpSaved register/frame pointerCallee
x9s1Saved registerCallee
x10-11a0-1Function arguments/return valuesCaller
x12-17a2-7Function argumentsCaller
x18-27s2-11Saved registersCallee
x28-31t3-6Temporaries/local variableCaller
  • temporaties are used for the right side of the calculation =
  • Register is 1 of 2 types:
    • Caller saved:
      • the callee funciton can use them freely
      • caller will need to save to mem and then load after call callees
    • Callee saved:
      • callee must save the register before modifying them and restore it before returning to caller

Procedure calls convention:

  • Transfer control: how to jump to callee and back to caller?
    • Use JAL, JALR:
      • caller: JAL x1, callee // store the next address to run after done w the function in x1
      • callee: JALR x0, 0(x1)// return to instruction in address x1
      • but if the callee calls another callee, then that 2nd callee will override x1
    • Use stack:
      1. at the beginning of any function, push the return address to the stack
        • ADDI sp, sp, -4
          • calculate new stack pointer, only 4 for 1 address, but more is needed if argument is passed, ex: pass 2 then -12
          • or just -4 now and minus more later when add
        • SW x1, 0(sp)
          • store x1 in that new stack pointer
      2. if calls a callee, Jump and Link to callee
        • JAL x1, callee1
          • x1 is now the next instruction in caller
      3. then at the beginning of new function, push like in step 1
        • then the next in the stack is the address of instruction of last caller
      4. a callee that also a caller needs to pop its return address then return to its caller:
        • Pop by
          • LW x1, 0(sp)
          • ADDI sp, sp, 4
        • then JALR x0, 0(x1)
      5. a callee that doesnt call any other function, it only need to return to its x1 that defined at the begining of its function
        • JALR x0, 0(x1)
  • Pass arguments: how to pass value from caller to callee? ^5e5eca
    • Caller use register x10-x17 and callee uses it
      • ex: add(1,2)
        • ADDI x10, x0, 1
        • ADDI x11, x0, 1
        • JAL x1, add
      • then the callee uses x10 and x11 in its instruction
    • But callee calls another callee, then x10 is overwritten, we need a way to save the variables:
      • use saved registers and save to stack and load it back from stack
      • its callee’s job to store the saved registers of its callee and load it back
    • If callee calls another function and doesnt use the arguments after that, there is no need to store all the arguments, only store values exist before subfunction call and needed after that function
  • Manage registers:
    • Functions are complied in isolation
    • Functions make use of general purpose registers
    • Allow each routine to use registers
    • Prevent routines from clobbering each others’ data
      • Saved registers: mentioned in passing arguments
        • s1-s11, fp, sp are used before the function call and the value will be the same after
        • Usually used for saving argument
      • Temporaries registers:
        • If the variables is only used before the subfunction call and not needed after that, then use temporaties registers x5-7 and x28-31
        • ra, a0-a7, t0-t6 are assumed to be destroyed after the subfunction call
        • otherwise need to store it in memory

In a nutshell:

  • ex:
int test(int a, int b) {  
	int tmp = (a&b)+(a|b);  
	int s = sum(tmp,1,2,3);  
	int u = sum(s,tmp,b,a,b);  
	return u + a + b;  
}
  • Whever a new function is called, it needs a fixed amount of memory addresses, call stack frame:
    • 1 return address frame pointer
    • caller’s fp
    • saved registers
    • local variables
    • saved temporaries stack pointer
  1. Body first:
    • If the variable is needed after another call, save it to s1-s11
      • MV s1, a0 # s1 = a
      • MV s2, a1 # s2 = b
    • use temp variables for rhs
      • AND t0, s1, s2 # a & b
      • OR t1, s1, s2 # a | b
      • ADD t0, t0, t1 # t0 = temp
    • Prepare for subfunction arguments
      • MV a0, t0 # first arg
      • ADDI a1, x0, 1 # 2nd arg
      • ADDI a2, x0, 2 # 3rd arg
      • ADDI a3, x0, 3 # 4th arg
    • Save temporaries value if needed later
      • ADDI sp, sp, -4
      • SW t0, 0(sp)
    • Then call
      • JAL x1, sum
    • Retrieves tmps if needed:
      • LW t0, 0 (sp)
      • ADDI sp, sp, 4
    • other function
      • call again
      • MV a1, t0
      • MV a2, s2
      • MV a, s1
      • JAL x1, sum
      • ADD t0, s1, s2
      • ADD a0, t0, a0
      • epilogue
      • JALR x0, 0(x1)
  2. Determine stack frame size:
    • return address: 4 bytes
    • caller frame pointer: 4 bytes:
    • saved registers: 4 bytes * nb of arguments
    • local vars not a temp: 4 bytes * nb of vars
  3. Prologue: goes to begining of a function
    • create a stack frame
      • ADDI sp, sp, -16
    • Save return address
      • SW x1, 12(sp)
    • Save caller’s fp
      • SW fp, 8(sp) # store old fp
      • ADDI fp, sp, 12 # set value for new fp
      • sp’ denotes old sp
        • addval
          sp’x1
          sp’-4fp
          sp’-8s1
          sp’-12s2
    • Save saved registers of caller
      • SW s1, 4(sp)
      • SW s2, 0(sp)
  4. Epilogue: Before the JALR back to caller
    • restore saved registers
      • LW s2, 0(sp)
      • LW s1, 4(sp)
    • Restore old fp
      • LW fp, 8(sp)
    • Restore return address
      • LW x1, 12(sp)
    • Destroy stack frame
      • ADDI sp, sp, 16
    • Dont forget JALR