100 likes | 118 Views
This article provides examples of looping through arrays, manipulating array elements, making function calls, and performing stack operations in MIPS assembly language.
E N D
Looping through an array of bytes • Example 1: /* a = array of char */ for(i = 0; i < 10; i++) a[i] = 0; • What’s going on here? Answer: Pointer arithmetic! for(char *p = &a[0]; p < (a + 10); p++) *p = 0; li $t0, 0 # t0 = i la $a0, a # a0 = &a[i] loop: bge $t0, 10, done sb $0, 0($a0) # a[i] = 0 addi $t0, $t0, 1 addi $a0, $a0, 1 j loop done: la $a0, a # a0 = &a[i] addi $t0, $a0, 10 loop: beq $a0, $t0, done sb $0, 0($a0) # a[i] = 0 addi $a0, $a0, 1 j loop done:
Looping through an array of 32-bit ints • In this case, &a[i] is actually &a[0] + 4*i • add 4 to pointer inside loop • use lw/sw instead of lb/sb • Example 2: /* a = array of int */ for(i = 0; i < 10; i++) a[i] = 0; la $a0, a # a0 = &a[i] addi $t0, $a0, 40 loop: beq $a0, $t0, done sw $0, 0($a0) # a[i] = 0 addi $a0, $a0, 4 j loop done:
Translate into MIPS • Example 3: /* a = array of int */ for(i = 0; i < 4; i++) { if(a[i]==a[i+1]) break; } • Example 4: /* a = array of int */ for(i = 8; i >= 0; i--) { a[i+1] = a[i]; } a[0] = 0; la $a0, a addi $t0, $a0, 16 loop: beq $a0, $t0, done lw $t1, 0($a0) lw $t2, 4($a0) beq $t1, $t2, done addi $a0, $a0, 4 j loop done: la $t0, a addi $a0, $t0, 36 loop: beq $a0, $t0, done lw $t1, -4($a0) sw $t1, 0($a0) addi $a0, $a0, -4 j loop done: sw $0, 0($a0)
Function calls in MIPS • MIPS uses the jump-and-link instruction jal to call functions • jal saves the return address (the address of the next instruction) in the dedicated register $ra, before jumping to the function • jal is the only MIPS instruction that can access the value of the program counter, so it can store the return address PC+4 in $ra • To transfer control back to the caller, the function uses a jump-register instruction to the address that was stored in $ra: jr $ra • Arguments to functions can be “passed” by placing them, by convention, in registers $a0 to $a3 • Values are returned, by convention, in registers $v0 and $v1
Warnings and Problems • Assembly language is untyped — there is no distinction between integers, characters, pointers or other kinds of values • You must “type check” your programs • Make sure your function arguments/return values are consistent • No warning if you pass an address of an integer (instead of the integer itself) to a function • A problem: What if a function uses a register that the main program needs after the function call? • Things get really nasty with nested/recursive functions • Solution: spill registers to memory (stack) • save “important” registers to memory before function call • restore these registers after the function call • Who spills? Caller or callee?
Who saves the registers? • Argument 1: The caller knows which registers are important to it and should be saved. So caller should save. • Argument 2: The callee knows exactly which registers it will use and potentially overwrite. So callee should save. • Both approaches may wastefully save registers they don’t really need to. • But the caller and callee must not assume anything about each other • may be written by different people or companies • should be able to interface with any caller/callee • Solution: Caller assumes callee will destroy: $t0-$t9 $a0-$a3 $v0-$v1 Callee assumes caller will need: $s0-$s7 $ra
NO!! $a0 may contain garbage at this point $sp 0x7FFFFFFF 0x00000000 stack Example • C++ code fragment: if(func(7) == 7) func: # free to modify $a, $t and $v registers jr $ra main: li $a0, 7 # set argument for call to func jal func # call func(7) bne $a0, $v0, else # test if func(7) == 7 • Since we want to preserve $a0 across the function call, we must save it before the call, and then restore it after the call. • MIPS stack grows LEFT, $sp points to “last used slot”
$sp $sp Before Pushing elements • To push elements onto the stack: • “Grow stack” by subtracting from $sp • Store the elements into the stack (array) • Example: Push $t1 and $t2 onto stack addi $sp, $sp, -8 sw $t1, 0($sp) sw $t2, 4($sp) • Returning to our previous example: li $a0, 7 addi $sp, $sp, -4 sw $a0, 0($sp) jal func # restore $a0 bne $a0, $v0, else $t2 $t1 After
$sp $t2 $t1 $sp $t2 $t1 Accessing and popping elements • You can access any element in the stack (not just the top one) if you know where it is relative to $sp • For example, to retrieve the value of $t2: lw $t2, 4($sp) • You can pop, or “erase,” elements simply by adjusting the stack pointer upwards • Example: addi $sp, $sp, 4 • Note: The “popped” data is still present in memory, but data left of the stack pointer is considered invalid
Finishing the main example func: # free to modify $a, $t and $v registers jr $ra main: li $a0, 7 # set argument for call to func addi $sp, $sp, -4 # grow stack sw $a0, 0($sp) # save $a0 on stack jal func # call func(7) lw $a0, 0($sp) # restore $a0 from stack addi $sp, $sp, 4 # shrink stack bne $a0, $v0, else ... jr $ra • Unfortunately, main won’t return correctly! • Any time you do a jal, you must save and restore $ra