The plot thickens Some MIPS instructions you can write cannot be translated to a 32-bit number some reasons why 1) constants are too big 2) relative addresses are too big 3) absolute addresses are outside the range of a jump 4) convenience instructions that have no opcode
CS 2630 Computer Organization Meeting 8: pseudo instructions; functions in MIPS Brandon Myers University of Iowa
Project 1 int x = arr[1]; arr[2] = x + 10; Compiler you build this lw $t0, 4($r0) addi $t0, $t0, 10 sw $t0, 8($r0) Assembler 10001110000010000000000000000100 00100001000010000000000000001010 10101110000010000000000000001000
Project 1: Three phases to MiniMa 0. We assume the program has been parsed from a text file into an array of Instruction objects 1. Translate MAL (combo of real and pseudo instructions) into TAL (real instructions) 2. Convert logical labels into immediates (i.e., branch label turned into a relative address) 3. Translate each instruction into a 32-bit word The main skeleton is provided, as well as test code and one test case your job: complete the three phases one at a time, testing as you go
Project 1 declare partner by end of today (ICON People tab) two more office hours this week and DYB on Monday take advantage!
Where we are going Compiler Instruction set architecture (e.g., MIPS) translating source code (C or Java) Programs to assembly language And linking your code to Library code How the software talks To the hardware Memory system Processor Datapath & Control Digital logic I/O system How a processor runs MIPS Programs! How switches (1 or 0) can be used to build Interesting functions: from integer arithmetic to programmable computers
Representations of a program int x = arr[1]; arr[2] = x + 10; Compiler High level language program (human readable) lw $t0, 4($r0) addi $t0, $t0, 10 sw $t0, 8($r0) Assembler assembly program as text (CS2630 student readable) 10001110000010000000000000000100 00100001000010000000000000001010 10101110000010000000000000001000 assembly program as binary (machine readable)
Storing an instruction as bits each MIPS instructions is 32 bits, stored in instruction memory address contents meaning 0000 0004 0008 000C 0010 0014 0018 8e 13 00 00 8e 14 00 04 02 74 90 20 ae 12 00 08 22 10 00 04 22 31 ff ff 1e 20 ff f9 lw lw $s3, 0($s0) $s4, 4($s0) add $s2, $s3, $s4 sw $s2, 8($s0) addi $s0, $s0, 4 addi $s1, $s1, -1 bgtz $s1, loop
Pseudo instructions Not all the MIPS instructions are real (i.e., understood by the processor) TAL the true assembly language; MIPS processors know all of these instructions MAL slightly more convenient language; each MAL instruction is usually translated to 1-3 TAL instructions by the assembler
Pseudo instructions (MAL) Example: how many bits are required for the immediate in this instruction? addiu $t1, $t2, 0x123456 assembler translates it to lui $t2, 0x0012 ori $t3, $t3, 0x3456 addu $t1, $t2, $t3 lui = load upper immediate
More pseudo instructions (MAL) other I-type instructions with too-large immediate move $rd, $rs li $rd, [32-bit immediate] la $rd, [32-bit immediate] branch instructions with <, >, <=, >= really use slt (set if less than), followed by beq to 0 or 1 For you to try: use these instructions in a MIPS program, then see what MARS translates them to
Peer instruction Put the following into (A) only in MAL (B) TAL
Reading quiz 1. What is the purpose of the stack in MIPS? 2. What is a return address? 3. When you call a procedure in MIPS, how do you pass arguments? 4. Why do procedure calls use the jal instruction?
Let s look at different proposals for translating procedure definitions and calls to MIPS
First try: use the name of the procedure as a label and just jump to it // definition int mult_by_2(int x) { return x*2; } // call the function int z = mult_by_2(5); mult_by_2: sll $v0, $a0, 1 # calculate and set return value j return_here # return addiu $a0, $zero, 5 # argument=5 j mult_by_2 # call procedure return_here: #... # use the result in $v0... #... What are the limitations of this proposal? Think / pair / share
Full example multbyn: # prolog # "push" registers onto the stack addiu $sp,$sp,-8 sw $ra, 0($sp) sw $s0, 4($sp) move $s0, $a0 bookkeeping before the body // x*n for positive n int multbyn(int x, int n) { if (n<=1) return 0; else return x + multbyn(n-1); } # body ble $a1, 1, return0 addiu $a1, $a1, -1 # recursive call jal multbyn # set return value add $v0, $s0, $v0 j end the actual computation return0: # set return value addiu $v0, $zero, 0 end: # epilog # "pop" registers off the stack lw $ra, 0($sp) lw $s0, 4($sp) addiu $sp,$sp,8 j $ra bookkeeping after the body / jump to caller
Function calls: only 6 steps 1. store arguments somewhere the callee code can access them 2. save current location and then jump to function 3. allocate local storage needed (saving registers) for running the function 4. perform the code in the function 5. store result somewhere the caller code can access it and restore saved registers 6. jump back to location of the function call multbyn: # prolog # "push" registers onto the stack addiu $sp,$sp,-8 sw $ra, 0($sp) sw $s0, 4($sp) move $s0, $a0 # body ble $a1, 1, return0 addiu $a1, $a1, -1 # recursive call jal multbyn # set return value add $v0, $s0, $v0 j end return0: # set return value addiu $v0, $zero, 0 end: # epilog # "pop" registers off the stack lw $ra, 0($sp) lw $s0, 4($sp) addiu $sp,$sp,8 j $ra bookkeeping before the body the actual computation bookkeeping after the body / jump to caller
MIPS registers used for function calls Register Number Conventional Name Usage $0 $zero hard-wired to 0 $1 $at reserved for assembler $2 - $3 $v0, $v1 return values from functions $4 - $7 $a0 - $a3 arguments $8 - $15 $t0 - $t7 temporary registers $16 - $23 $s0 - $s7 saved registers $24 - $25 $t8 - $t9 temporary registers $26 - $27 $k0 - $k1 reserved for OS $28 $gp global pointer $29 $sp stack Pointer $30 $fp frame Pointer $31 $ra return Address
First try: use the name of the procedure as a label and just jump to it // definition int mult_by_2(int x) { return x*2; } // call the function int z = mult_by_2(5); mult_by_2: sll $v0, $a0, 1 # calculate and set return value j return_here # return addiu $a0, $zero, 5 # argument=5 j mult_by_2 # call procedure return_here: #... # use the result in $v0... #...
Second try: allow multiple callsites by saving return address in $ra // definition int mult_by_2(int x) { return x*2; } // call the function int z = mult_by_2(5); reference: $ra return address $v0 return value $a0 argument mult_by_2: sll $v0, $a0, 1 # calculate # and set return value jr $ra # return addiu $a0, $zero, 5 la $ra, return_here j mult_by_2 return_here: la turns label into an address and assigns it to $ra
Convenient tip: use jal to save $ra for you! // definition int mult_by_2(int x) { return x*2; } // call the function int z = mult_by_2(5); mult_by_2: sll $v0, $a0, 1 # calculate jr $ra reference: $ra return address $v0 return value $a0 argument # and set return value # return addiu $a0, $zero, 5 jal mult_by_2 # jump-and-link does # $ra <- return addr # then jump to label
Problem: no modularity! every procedure needs to know what registers are being used by the callers L
Convention to the rescue! Register Number Conventional Name Usage $0 $zero hard-wired to 0 $1 $at reserved for assembler $2 - $3 $v0, $v1 return values from functions $4 - $7 $a0 - $a3 arguments $8 - $15 $t0 - $t7 temporary registers $16 - $23 $s0 - $s7 saved registers $24 - $25 $t8 - $t9 temporary registers $26 - $27 $k0 - $k1 reserved for OS $28 $gp global pointer $29 $sp stack Pointer $30 $fp frame Pointer $31 $ra return Address
temporary registers might have a different contents after a procedure call as they did before saved registers have the same contents after a procedure call as they did before
Treat $s_ (saved) registers like you treat a campground Before Labor day weekend During Labor day weekend After Labor day weekend
How to clean up your campground Before you use a saved register, save its value. When you are finished, restore the value. this instruction overwrites $s0
Refer to the preserved across a call column of the REGISTER table on the MIPS reference card No means the caller needs to save it if they want it later Yes means the callee needs to clean the campground before returning
Calling procedures within procedures RTL for jal R[31] PC + 8 (register 31 is $ra) What register do I need to SAVE in the prolog if I see a jal in the procedure body? multbyn: # prolog... # body ble $a1, 1, return0 addiu $a1, $a1, -1 # recursive call jal multbyn... calling multbyn recursively
Think / pair / share What must be saved before the procedure runs and restored before it returns? multiple answer 1. $t0 2. $a0 3. $zero 4. $s0 5. $s1 6. $s2 7. $ra 8. $v0
Stack machines x+y*z+u no architectural registers many architectures (real and virtual) have been designed this way https://en.wikipedia.org/wiki/stack_machine
CS 2630 Computer Organization Meeting 9: the stack Brandon Myers University of Iowa
how do we save and restore registers?
Memory organization of programs local variables, return addresses RW dynamically allocated memory like Java objects RW global data (initialized when process starts) RW assembled code (initialized when process starts) RX legend: R=readable W=writeable X=executable
The stack The call-stack (or just stack) has one part, or frame, for each active procedure an active procedure is a Java method, Python function, etc that has not yet returned The frame stores the state needed by the procedure call, including return address, local variables that don t fit in registers, and saved registers In MIPS, we use register $sp (stack pointer) to store the address of the current frame
Example: using the stack stack yoo frame who frame ami frame ami frame procedure ami is recursive (calls itself) ami frame
Example: using the stack in MIPS
Template for a procedure definition
Helpful tip: macros in MIPS # define macro to push a register value to stack.macro push (%x) addiu $sp, $sp, -4 sw %x, 0($sp).end_macro # define a macro to pop a register value from stack.macro pop (%x) lw %x, 0($sp) addiu $sp, $sp, 4.end_macro push/pop involves: 1. decrementing/incrementing $sp 2. sw/lwthe saved register so for convenience, we can define a macro that does those things
Template for a procedure definition (using push and pop macros) procedure_name: # prolog # "push" registers onto the stack push(r1) push(r2)... # body get arguments from $a0,$a1 (if applies) do the work, call other procedures call other functions set return value $v0 (if applies) # epilog # "pop" registers off the stack pop(r1) pop(r2)... j $ra
Checklist (go through this when you write a function in MIPS) qdid I use the $a0-$a3 registers to pass arguments? qdid I use the $v0, $v1 registers for passing return values? qdid I save the $s_ registers that my function clobbers? qdid I match the restores (pops) with the saves (pushes)? qif there is a jal in my function did I save/resotre the $ra register?
Full example (revisited) multbyn: # prolog # "push" registers onto the stack addiu $sp,$sp,-8 sw $ra, 0($sp) sw $s0, 4($sp) move $s0, $a0 // x*n for positive n int multbyn(int x, int n) { if (n<=1) return 0; else return x + multbyn(n-1); } # body ble $a1, 1, return0 addiu $a1, $a1, -1 # recursive call jal multbyn # set return value add $v0, $s0, $v0 j end return0: # set return value addiu $v0, $zero, 0 end: # epilog # "pop" registers off the stack lw $ra, 0($sp) lw $s0, 4($sp) addiu $sp,$sp,8 j $ra
1 Your turn get into a group of 3 (appoint scribe, manager, skeptic) for each problem: a) write in high level (e.g. Java) pseudocode b) translate to MIPS c) MIPS code to call the function on sample inputs (assume register $s0 already holds the address of array x for 2 & 3) // returns x+y int xplusy(int x, int y) on the board use the template and checklist to help with your skepticism 2 // add 1 to each array element in-place (recursive) void add1(int[] x, int size) 3 // return the sum of the elements in the array (recursive) int sum(int[] x, int size)