CDA 3101 Discussion Section 03 MIPS Assembly Language Programming (2) 1 Basic Instructions • Manipulate data: – ADD SUB MUL DIV AND OR XOR … – ADDI SLL … • Memory Instruction: – LW SW – LB SB • Branches –J – BNE BEQ BGE … 2 Decisions: C if Statements • 2 kinds of if statements in C – if (condition) statement-block – if (condition) statement-block1 else statement-block2 • 2nd statement can be rewritten as: if (condition) goto L1; statement-block2; goto L2; L1: statement-block1; L2: 3 Example: Compiling C if into MIPS • C Code if (i == j) f=g+h; else f=g-h; f: $s0, g: $s1, h: $s2, i: $s3, j: $s4 MIPS code: beq $s3,$s4,True sub $s0,$s1,$s2 j Fin True: add $s0,$s1,$s2 Fin: 4 Loops in C/Assembly • There are three types of loops in C: – do…while – while – for 5 Case-Switch Statement • C code switch(k) { case 0: f = i + j; break; case 1: f = g + h; break; case 2: f = g - h; break; case 3: f = i - j; break; } • Can be rewritten as if (k == 0) f = i + j; else if (k == 1) f = g + h; else if (k == 2) f = g – h; else if (k == 3) f = i – j; 6 Procedure • What is procedure? Example Code main() { int a, b; sum(a,b); … } int sum(int x, int y) { return(x+y); } Steps: 1. Caller places parameters in a place where the procedure can access them 2. Transfer control to the procedure 3. Acquire storage resources required by the procedure 4. Execute the statements in the procedure 5. Called function places the result in a place where the caller can access it 6. Return control to the statement next to the procedure call How do we achieve all this in MIPS Assembly language • To achieve this, following registers are used: – $4-$7 ($a0-$a3): used to pass arguments – $2-$3 ($v0-$v1): used to pass return values – $31 ($ra): used to store the addr of the instruction which is to be executed after the procedure returns main() { int a, b; sum(a,b); … } main: sum: int sum(int x, int y) { return(x+y); } add $a0,$s0,$zero add $a1,$s1,$zero addi $ra,$zero,? j sum ... add $v0,$a0,$a1 jr $ra #x=a #y=b #$ra=? #jump to sum # new instruction In MIPS, all instructions are 4 bytes, and stored in memory just like data. main() { int a, b; sum(a,b); … } 1000 1004 1008 1012 main: 2000 2004 sum: int sum(int x, int y) { return(x+y); } add $a0,$s0,$zero add $a1,$s1,$zero addi $ra,$zero,? j sum ... add $v0,$a0,$a1 jr $ra #x=a #y=b #$ra=? #jump to sum # new instruction main() { int a, b; sum(a,b); … } 1000 1004 1008 1012 main: 2000 2004 sum: int sum(int x, int y) { return(x+y); } add $a0,$s0,$zero add $a1,$s1,$zero addi $ra,$zero,1016 j sum ... add $v0,$a0,$a1 jr $ra #x=a #y=b #$ra=1016 #jump to sum # new instruction MIPS provides a single instruction called ‘jal’ to 1. Load $ra with addr of next instruction 2. Jump to the procedure. main() { int a, b; sum(a,b); … } 1000 1004 1008 main: 2000 2004 sum: int sum(int x, int y) { return(x+y); } add $a0,$s0,$zero add $a1,$s1,$zero jal sum ... add $v0,$a0,$a1 jr $ra #x=a #y=b #$ra=1012, jump to sum Non-Leaf Procedures main() { int a, b; sum2(a,b); … } 1000 1004 1008 main: add $a0,$s0,$zero add $a1,$s1,$zero jal sum2 ... int sum2(int x, int y) { return(sum(x,x) + y); } 2000 2004 2008 2012 int sum(int p, int q){ return(p+q); } 4000 4004 sum2: add $a1,$a0,$0 jal sum add $v0, $v0, $a1 jr $ra … sum: add $v0, $a0, $a1 jr $ra 1000 main: 1004 1008 add $a0,$s0,$zero add $a1,$s1,$zero jal sum2 ... 2000 2004 2008 2012 sum2: 4000 4004 sum: add $a1,$a0,$0 jal sum add $v0, $v0, $a1 jr $ra … add $v0, $a0, $a1 jr $ra Instr.A $ra ddr $a0 1000 a 1004 1008 $v0 b 1012 2000 2004 $a1 a 2008 4000 a+a 4004 2008 a+a+a 2012 2008 We need to do some bookkeeping! We need to save registers before rewriting them. Where should we save them? a+a+a+a Memory Organization $sp Reserved Stack Dynamic Data 0x80000000 0x10040000 •$sp ($29) points to the top of the stack 0x10000000 •To push a word on stack, decrement $sp by 4, and use sw Static Data Text Reserved •Stack grows from Hi addr to Lo addr 0x00400000 •To pop a word from stack, increment $sp by 4 Saving registers • Following registers should be spilled to the stack – $ra ($31) – $a0-$a3 ($4-$7) – $t0-$t7 ($8-$15) – $s0-$s7 ($16-$23) – $fp ($30) Saved by caller on stack before jal and restored after returning from jal; done only for registers used after jal Saved by called procedure before rewriting and then restored back before returning 1000 main: 1004 1008 add $a0,$s0,$zero add $a1,$s1,$zero jal sum2 ... 2000 2004 2008 2012 sum2: 4000 4004 sum: add $a1,$a0,$0 jal sum add $v0, $v0, $a1 jr $ra … add $v0, $a0, $a1 jr $ra Instr.A $ra=y ddr $a0 1000 a 1004 1008 4000 $v0 b 1012 2000 2004 $a1 a 2008 a+a 4004 2008 a+a+a 2012 2008 a+a+a+a 1000 main: add $a0,$s0,$zero 1004 1008 1012 1016 1020 1024 1028 add $a1,$s1,$zero addi $sp, $sp, -4 sw $ra, 0($sp) jal sum2 lw $ra, 0($sp) addi $sp, $sp, 4 jr $ra 2000 2004 2008 2012 2016 2020 2024 2028 2032 2036 4000 4004 sum2: addi $sp, $sp, -8 sw $ra, 4($sp) sw $a1, 0($sp) add $a1,$a0,$0 jal sum lw $a1, 0($sp) lw $ra, 4($sp) add $sp, $sp, 8 add $v0, $v0, $a1 jr $ra … sum: add $v0, $a0, $a1 jr $ra Addr 1000 1004 1008 1012 1016 2000 2004 2008 2012 2016 4000 4004 2020 2024 2028 2032 2036 1020 1024 1028 $ra=y $a0 $a1 $v0 a b $sp=x $sp $sp y x-4 1020 $sp b 1020 x-12 a Lo addr 2020 2a b 1020 x-4 2a+b y x Recursive functions main() { fact(2); } int fact(int n) { if (n < 1) return(1); else return(n*fact(n-1)); } 1000 1004 1008 1012 1016 1020 1024 main: addi $a0, $0, 2 addi $sp, $sp, -4 sw $ra, 0($sp) jal fact lw $ra 0($sp) addi $sp, $sp, 4 jr $ra 2000 2004 2008 2012 fact: slti $t0, $a0, 1 beq $t0, $0, L1 addi $v0, $0, 1 jr $ra 2016 2020 2024 2028 2032 2036 2040 2044 2048 2052 L1: addi $sp, $sp, -8 sw $ra, 4($sp) sw $a0, 0($sp) addi $a0, $a0, -1 jal fact lw $a0, 0($sp) lw $ra, 4($sp) add $sp, $sp, 8 mul $v0, $a0, $v0 jr $ra 1000 main: addi $a0, $0, 2 1004 addi $sp, $sp, -4 1008 sw $ra, 0($sp) 1012 jal fact 1016 lw $ra 0($sp) 1020 addi $sp, $sp, 4 1024 jr $ra 2000 fact: slti $t0, $a0, 1 2004 beq $t0, $0, L1 2008 addi $v0, $0, 1 2012 jr $ra 2016 L1: addi $sp, $sp, -8 2020 sw $ra, 4($sp) 2024 sw $a0, 0($sp) 2028 addi $a0, $a0, -1 2032 jal fact 2036 lw $a0, 0($sp) 2040 lw $ra, 4($sp) 2044 add $sp, $sp, 8 2048 mul $v0, $a0, $v0 2052 jr $ra Addr $ra=y $a0 1000 2 1004 1008 1012 1016 2000 2004 2016 2020 2024 2028 1 2032 2036 2000 2004 2016 2020 2024 2028 0 2032 2036 2000 2004 2008 2012 2036 1 2040 2036 2044 2048 2052 2036 2 2040 1016 2044 $t0 $v0 $sp=x x-4 $sp 4 bytes hi $sp y 1016 0 $sp 2 x-12 2036 $sp 1 0 lo x-20 1 1 x-12 1 x-4 Argument Passing • Recall: arguments to a procedure passed through $a0-$a3 • What if the procedure has > 4 arguments? hi x7 x6 x5 – First four arguments are put in $a0-$a3 – Remaining arguments are put on stack by the caller • Example: silly7(int x0, int x1, …, int x7) – Caller places arguments x0-x3 in $a0-$a3 – Caller places arguments x4-x7 on stack $sp x4 lo Return Values • Recall: return values from a procedure passed through $v0-$v1 • What if the procedure has > 2 return values? – First two return values put in $v0-$v1 – Remaining return values put on stack by the procedure – The remaining return values are popped from the stack by the caller Variables • Memory for Global variables is allocated using assembler directives like .space, .word, .halfword, .byte, etc. • Memory allocated is in the static data portion if MIPS memory • What about local variables? Local Variables in procedures • Example int sillyfunc(int i, int j) { int k, l, m, n, stuff[3]; …. } Hi addr stuff[2] stuff[1] stuff[0] n m l $sp k • How to acess stuff[2]? • How to access stuff[m]? Lo addr 24 20 16 12 8 4 0 Local Variables in procedures • Example int sillyfunc(int i, int j) { int k, l, m, n, stuff[3]; …. } Hi addr stuff[2] stuff[1] stuff[0] n m • How to acess k? • Say, sillyfunc() calls another function and saves $t0 on stack before this call • What will be the offset of k with respect to $sp now? l $sp k $sp $t0 Lo addr 24 20 16 12 8 4 0 Frame Pointers • Use register $30 ($fp) • At entry the called procedure Hi addr $sp $fp Old $fp – Saves old $fp on stack – sets $fp = $sp – 4 stuff[2] stuff[1] • During the procedure execution, $fp does not change – Address of m is always -20($fp) – Set $sp = $fp + 4 – Restore old $fp value from stack and set $fp = old $fp -8 -12 n -16 l $sp k $sp $t0 Lo addr -4 stuff[0] m • Before return 0 -20 -24 -28 Dynamic Memory Allocation • Memory allocated on heap • In PCSPIM, do the following to dynamically allocate memory – Set $v0 = 9 – Set $a0 = number of bytes you want to allocate – syscall