Homework #3: Functions Calling Functions, and Recursion

Similar documents
Functions in MIPS. Functions in MIPS 1

Subroutines. int main() { int i, j; i = 5; j = celtokel(i); i = j; return 0;}

Lecture 5. Announcements: Today: Finish up functions in MIPS

Lectures 5. Announcements: Today: Oops in Strings/pointers (example from last time) Functions in MIPS

Today. Putting it all together

COMP 303 Computer Architecture Lecture 3. Comp 303 Computer Architecture

CA Compiler Construction

Compilers and computer architecture: A realistic compiler to MIPS

MIPS Programming. A basic rule is: try to be mechanical (that is, don't be "tricky") when you translate high-level code into assembler code.

Branch Addressing. Jump Addressing. Target Addressing Example. The University of Adelaide, School of Computer Science 28 September 2015

Control Instructions

MIPS Functions and Instruction Formats

Memory Usage 0x7fffffff. stack. dynamic data. static data 0x Code Reserved 0x x A software convention

Homework #2 Think in C, Write in Assembly

Common Problems on Homework

Control Instructions. Computer Organization Architectures for Embedded Computing. Thursday, 26 September Summary

Chapter 2. Computer Abstractions and Technology. Lesson 4: MIPS (cont )

Implementing Procedure Calls

Calling Conventions. Hakim Weatherspoon CS 3410, Spring 2012 Computer Science Cornell University. See P&H 2.8 and 2.12

EE 361 University of Hawaii Fall

Computer Science 2500 Computer Organization Rensselaer Polytechnic Institute Spring Topic Notes: MIPS Programming

Course Administration

ECE260: Fundamentals of Computer Engineering

CSCI 402: Computer Architectures. Instructions: Language of the Computer (3) Fengguang Song Department of Computer & Information Science IUPUI.

CSE Lecture In Class Example Handout

CS 61c: Great Ideas in Computer Architecture

SPIM Procedure Calls

Anne Bracy CS 3410 Computer Science Cornell University

CS 61C: Great Ideas in Computer Architecture (Machine Structures) More MIPS Machine Language

MIPS Functions and the Runtime Stack

Function Calling Conventions 1 CS 64: Computer Organization and Design Logic Lecture #9

Chapter 2A Instructions: Language of the Computer

CS64 Week 5 Lecture 1. Kyle Dewey

CS61C Machine Structures. Lecture 12 - MIPS Procedures II & Logical Ops. 2/13/2006 John Wawrzynek. www-inst.eecs.berkeley.

Compiling Code, Procedures and Stacks

CSCI 2321 (Computer Design), Spring 2018 Homework 3

ECE232: Hardware Organization and Design

CS1622. Semantic Analysis. The Compiler So Far. Lecture 15 Semantic Analysis. How to build symbol tables How to use them to find

CS153: Compilers Lecture 8: Compiling Calls

Prof. Kavita Bala and Prof. Hakim Weatherspoon CS 3410, Spring 2014 Computer Science Cornell University. See P&H 2.8 and 2.12, and A.

Instructions: Assembly Language

Code Generation. The Main Idea of Today s Lecture. We can emit stack-machine-style code for expressions via recursion. Lecture Outline.

We can emit stack-machine-style code for expressions via recursion

Function Calls. 1 Administrivia. Tom Kelliher, CS 240. Feb. 13, Announcements. Collect homework. Assignment. Read

Machine Language Instructions Introduction. Instructions Words of a language understood by machine. Instruction set Vocabulary of the machine

Do-While Example. In C++ In assembly language. do { z--; while (a == b); z = b; loop: addi $s2, $s2, -1 beq $s0, $s1, loop or $s2, $s1, $zero

COMP2611: Computer Organization MIPS function and recursion

CS 61C: Great Ideas in Computer Architecture Strings and Func.ons. Anything can be represented as a number, i.e., data or instruc\ons

Runtime management. CS Compiler Design. The procedure abstraction. The procedure abstraction. Runtime management. V.

Anne Bracy CS 3410 Computer Science Cornell University

MIPS Procedure Calls - Review

Function Calling Conventions 2 CS 64: Computer Organization and Design Logic Lecture #10

Computer Architecture Prof. Mainak Chaudhuri Department of Computer Science & Engineering Indian Institute of Technology, Kanpur

Instruction Set Architectures (4)

Function Calls. Tom Kelliher, CS 220. Oct. 24, SPIM programs due Wednesday. Refer to homework handout for what to turn in, and how.

CS61C : Machine Structures

Functions and the MIPS Calling Convention 2 CS 64: Computer Organization and Design Logic Lecture #11

Computer Architecture

Code Generation. Lecture 12

CS 161 Computer Security

Concepts Introduced in Chapter 7

Q1: /20 Q2: /30 Q3: /24 Q4: /26. Total: /100

MIPS R-format Instructions. Representing Instructions. Hexadecimal. R-format Example. MIPS I-format Example. MIPS I-format Instructions

Midterm II CS164, Spring 2006

Storage in Programs. largest. address. address

MIPS function continued

Weeks 6&7: Procedures and Parameter Passing

Using the MIPS Calling Convention. Recursive Functions in Assembly. CS 64: Computer Organization and Design Logic Lecture #10 Fall 2018

Functions and Procedures

Topic Notes: MIPS Instruction Set Architecture

Contents. Slide Set 2. Outline of Slide Set 2. More about Pseudoinstructions. Avoid using pseudoinstructions in ENCM 369 labs

CSE Lecture In Class Example Handout

CS 110 Computer Architecture Lecture 6: More MIPS, MIPS Functions

The plot thickens. Some MIPS instructions you can write cannot be translated to a 32-bit number

CPS311 Lecture: Procedures Last revised 9/9/13. Objectives:

Lecture 5: Procedure Calls

CS 61C: Great Ideas in Computer Architecture Func%ons and Numbers

Ch. 11: References & the Copy-Constructor. - continued -

Lecture 7: Procedures and Program Execution Preview

Lecture 5: Procedure Calls

2/16/2018. Procedures, the basic idea. MIPS Procedure convention. Example: compute multiplication. Re-write it as a MIPS procedure

Lec 10: Assembler. Announcements

ECE 30 Introduction to Computer Engineering

LAB C Translating Utility Classes

Code Generation & Parameter Passing

Procedure Call and Return Procedure call

CS61C : Machine Structures

ECE260: Fundamentals of Computer Engineering. Supporting Procedures in Computer Hardware

CS 61C: Great Ideas in Computer Architecture. MIPS Instruction Formats

Instructor: Randy H. Katz hap://inst.eecs.berkeley.edu/~cs61c/fa13. Fall Lecture #7. Warehouse Scale Computer

ECE 331 Hardware Organization and Design. Professor Jay Taneja UMass ECE - Discussion 3 2/8/2018

Run-time Environment

CS356: Discussion #6 Assembly Procedures and Arrays. Marco Paolieri

Chapter 2. Instructions: Language of the Computer. Adapted by Paulo Lopes

Thomas Polzer Institut für Technische Informatik

comp 180 Lecture 10 Outline of Lecture Procedure calls Saving and restoring registers Summary of MIPS instructions

The plot thickens. Some MIPS instructions you can write cannot be translated to a 32-bit number

Architecture II. Computer Systems Laboratory Sungkyunkwan University

CS 316: Procedure Calls/Pipelining

Chapter 10 Memory Model for Program Execution. Problem

Transcription:

Homework #3: Functions Calling Functions, and Recursion Assigned: Tuesday 17 Jun 2014 Due: Monday 23 Jun 2014 (at 23:59:59) This MIPS programming assignment introduces MIPS functions (which [P&H14] calls procedures ), stack frames, and simple recursion. Handing In By e-mail to ewang@eecs.wsu.edu. Add [CS 260] to the Subject: line. Fundamentals I: Stack Frames (Reading Assignment) A trivial function doesn t need a stack frame. But most functions are non-trivial. In particular, recursive functions are always non-trivial, and always need a stack frame! High-level languages like C automatically generate all the code to manage stack frames. In MIPS assembly, you must do this explicitly yourself. You might never need to write explicit stack frame code beyond CS 260. But gaining a thorough understanding of the stack is fundamentally useful, in every language. This is because all modern programming languages have functions, and a stack is the natural mechanism for data transfer across function boundaries. Some examples of stack-based constructs include: Local variables Return addresses hence their vulnerability to buffer overrun attacks!! Nested scopes with C name hiding, C++ scope resolution, Emacs Lisp dynamic scoping Variadic function arguments, as in C `printf(fmt, )`, or ParamArray in Perl/ Common Lisp/Visual Basic/, etc. Passing an object (C/C++ struct/class) by value Returning an object by value (C/C++), or multiple values (Common Lisp) 1

Higher-language features, such as stack unwinding in exception handling (C++, Java, Common Lisp), return value optimization (C++), stack frame elision for tail-recursive functions (Scheme, Common Lisp), and functional languages (Erlang, ML, Haskell) also reduce to clever manipulations of the stack. By understanding the stack, you can understand exactly what these features are doing, and perhaps go on to help develop new techniques in the next major language. Stack Push, Stack Pop A: #~~~~ stack push ~~~~ addi $sp, $sp, N sw # grow downward code #~~~~ stack pop ~~~~ lw addi $sp, $sp, N # shrink upward # end of A A stack frame saves and restores register values (into memory). The purpose is to make these values persist for some duration (more on this later). Meanwhile, you can re-use those registers for other purposes. You often must do this because MIPS dedicates some registers to specific purposes, which can result in contention for those registers. Example: $a0 is always the first function argument, and $v0 is always the first return value. Whenever you have a sequence of 2+ nested function calls, they will contend for these registers (and possibly others). The code to create a stack frame consists merely of two blocks: a stack push that allocates space and stores register values into it, and a stack pop that does exactly the inverse. These two blocks bracket some region of your code. They correspond to a matched pair of `{}` curly-braces in C/C++/C#/Java, `()` in Common Lisp, etc. 2

The stack push must decrement $sp first (and then store to the new $sp + 0, ). Otherwise, you ll clobber memory that belongs to somebody else (usually your own caller). 1 The stack pop must increment $sp by exactly the same amount. (This is [P&H14] s spy covering his tracks on p.97.) Otherwise, you ll probably restore the wrong value into $ra and crash on return or one of your callers will! The stack pop usually restores (loads) everything that was saved in the push. Corollary: If you never need to restore it anywhere, then you didn t need to save it in the first place. Simple guideline: Copy-paste your entire stack push block. Reverse the order of the lines, change all `sw` to `lw`, and delete the ` ` character. The result is a perfectly acceptable stack pop block. Note: It is your responsibility to make sure that your stack pop exactly cancels out your stack push! Non-Leaf Stack Frame: jal changes $ra The simplest case of register contention is when your function makes any function call of its own. (This includes all recursive functions, by definition!) Consider your program s tree of function call dependencies. A leaf (bottommost) node in the call dependency tree is a function that doesn t call any other function. Such a function may be able to omit the stack frame. However, a non-leaf function, i.e. one that contains a `jal` or equivalent instruction anywhere in its body, immediately creates contention for the $ra register. The `jal` automatically changes $ra (that s exactly how `jal` works) so your function must save/restore its own value of $ra first. (It may also need to save/restore some $a* s.) 1 Advanced usage: Some languages permit you to access your caller s local variables, or generally variables in enclosing scopes. They do this by computing (at compile-time) the offsets of those local variables on the stack, relative to your function s stack pointer. You could do the same in MIPS (but we won t in this class). 3

f: # when f is done, it must return to $ra s current value #~~~~ stack push ~~~~ addi $sp, $sp, 4 # grow downward sw $ra, 0($sp) # save it while we have it J: jal g # clobbers $ra: sets $ra = (J+4) == K K: # that s how g knows to return to address K #~~~~ stack pop ~~~~ lw $ra, 0($sp) # restore before we use it addi $sp, $sp, 4 # shrink upward # end of f The stack push and pop blocks can go (almost) anywhere in your function. Guidelines: Simplest rule: Stack push at the very top. Stack pop at the very bottom. Non-vacuous rule (a mild optimization): Some functions may have vacuous (empty) or trivial bodies, e.g. a loop of 0 iterations, or a recursive function s base case. If only the recursive branch involves `jal` calls, then you can put the stack push/pop at the top/bottom of the recursive branch only. (Caution: It is your responsibility to make sure that your trivial branches properly jump over the stack pop! An unmatched pop is just as fatal as an unmatched push.) Function Call Boundary: What s the Difference Between $s* and $t*? Consider a function call from the perspective of the caller (parent, outside). A function is now a black box 2, which magically does something. (This is why functions are useful, and why every language has them.) But a MIPS function can also have sideeffects, especially when it clobbers registers (and doesn t bother to save/restore them). Here s a program fragment that includes one `jal` call to an external function. 2 In the real world, we almost never have the source code for library functions, but we can call them anyways, and trust them to do the right thing. 4

la $s0, MyArrayStart # char * P = A + 0; li $t1, 2 # int j = 2; addi $s0, $s0, 1 # ++P; #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ jal TransmogrifyBits # v0 = TB(a0, a1) and? #~~~~~~~~~~~~ (boundary) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # now did $s0 persist? -- yes, all $s* persist # now did $t1 persist? -- no, it could be anything # now did $ra persist? -- no, `jal` clobbered it for sure addi $s0, $s0, 1 # ok, $s0 is still P bne $t1, 5, FRO # oops: j could be -298,158!! Hence, a function call is a boundary, beyond which some registers are trustworthy, and others aren t. MIPS adopts the following convention 3 for registers across a function call boundary; see [P&H14] (p.97) (p.105 Fig. 2.14) (p.a-24 Fig. A.6.1) and the green card in the front of the book. Callee-saved registers do persist: $s0 $s7, $sp (stack), $ra (return address). Caller-saved registers do not persist: $t0 $t9, $a0 $a3 (arguments), $v0 $v1 (return values). This means any function can clobber them freely, and the caller can t trust them. Now let s switch hats, and consider this from the perspective of the callee (child, inside, the one that is called ). Then the convention these registers persist becomes the absolute rule: Thou Shalt Make Them Persist 3 This convention isn t enforced at the hardware level, nor even by the MIPS assembler. So you could violate these conventions and still produce running code. But such code would fail to integrate seamlessly with other MIPS code that does adhere to this convention. More practically, this includes the class s automatic grading harness hence code that violates this convention could be mistaken for code that doesn t work! 5

It is your responsibility to do this manually! The MIPS assembler doesn t do it for you automatically. Within a callee function body F, the obvious way to make $s* registers persist across a call to F is to save/restore them in F s own stack frame. If F didn t have a stack frame already, it needs one now. (If F already has one, just add $s* to its push/pop blocks.) Here s a plausible way to write that external function, with a proper stack frame: # for reasons of my own, I insist on reusing $s0 TransmogrifyBits: # ~~~~ stack push ~~~~ addi $sp, $sp, -4 sw $s0, 0($sp) # [0] <= s0 # now I ll use $s0 li $s0, 15 li $t0, 0 # I can clobber $t* freely # ~~~~ stack pop ~~~~ lw $s0, 0($sp) # [0] => s0 addi $sp, $sp, 4 # never called `jal`, so $ra never changed Now we can use it, and trust that it behaves itself, like a well-mannered child: addi $s0, $s0, 1 # ++P; jal TransmogrifyBits # ~~~~~~~~ boundary ~~~~~~~~ # now did $s0 persist? -- yes, because I made it persist!! 6

Guidelines for callee-saved (persistent) registers: 1. If you never change any callee-saved register (including $ra!), then you don t need a stack frame at all. Hence, one work-around is to use $t* inside your functions, as much as possible. This works for leaf functions only. 2. If you never change a particular $s*, then you don t need to save/restore it. 3. If you do change any $s* (or $ra), then you must have a stack frame, and it must save/restore all of those $s*. Example: Suppose your function is a complex loop. It could be easier to manage if you split it up into 1 outer loop + a few subfunctions, where the loop body calls those functions. Then the outer loop body could put its loop variables into $s* registers, and rely on the convention that these $s* will persist across its subfunction calls. This helps improve clarity for your outer loop. When you write those subfunctions, you must explicitly write them to do #1, #2, or #3. Your outer loop function must do #3. This is a mild burden when you re writing a function. The benefit is that when you call other functions, e.g. system or library functions, or other programmers functions, you can trust that they ve already done #1, #2, or #3, no matter who wrote them. Other programmers will expect the same from you! Caller-Saved Registers and the Point-of-Call Stack Frame What if you have contention for argument registers $a* and/or result registers $v*? This often happens whenever you call non-trivial subfunctions (that require their own arguments, and return intermediate results). These caller-saved registers do not persist, so you can t just change-and-forget them. Generally, you can organize your save/restore code into two places: 1. Outermost. You could treat $a* exactly the same as callee-saved $s* registers, and just add them to your topmost/bottommost stack frame. Disadvantage: The saving/restoring can be far away (in lines) from where you use it, which can be hard to read. 7

Caution: Don t blindly save/restore your $v* registers. They re supposed to change, to return the results of a call! If you did save/restore $v*, it could look like your function had no effect at all! 2. Point-of-call. Or, insert a 2-line mini-push block right before the `jal` call, and a 2-line mini-pop block right after it, and think of the entire 5-line block as one atomic (indivisible) chunk. (Bonus: You can even comment your code this way, e.g. by adding a blank line above and below, and/or a comment separator line. Being able to see the chunk as a chunk is also very useful!) Here s a sample code fragment, comparing these two approaches: fe: # 1. Outermost fm: # 2. Point-of-call #~~~~ top push ~~~~ #~~~~ top push ~~~~ addi $sp, $sp, 8 addi $sp, $sp, 4 sw sw $ra, 0($sp) $a0, 4($sp) sw $ra, 0($sp) # caller must save/restore! #~~~~ mini push ~~~~ addi $sp, $sp, 4 sw $a0, 0($sp) mov $a0, $v0 # g(h(a0)) mov $a0, $v0 jal g jal g #~~~~ mini pop ~~~~ lw $a0, 0($sp) addi $sp, $sp, 4 lw $a0, 4($sp) #~~~~ bottom pop ~~~~ #~~~~ bottom pop ~~~~ lw $ra, 0($sp) addi $sp, $sp, 8 lw $ra, 0($sp) addi $sp, $sp, 4 8

Note that the stack offsets to $sp differ based on where you re counting from. If in doubt, sketch the stack and label its slots (on scratch paper, or right in your source code as comments in your stack push block). Part II (Programming Assignment) For each function definition below, write an equivalent MIPS function. (That means it must return to its caller using ``.) You may assume n 0 (and you don t have to check for it). Hint: Write all of your functions in the same file. To test your code, write a separate `main:` block that just calls whichever function you re currently testing, then exits gracefully. Update your `main:` block as you reach each problem. 1. A function named quart that implements 4 quarts ( n) = n. (In other words, it returns $v0 = ($a0) 4.) 4 2. A function named qi that implements qi( n) = i, using iteration (a loop). Hint: Call your quart function. (Do not copy-paste its code into qi.) n i= 1 4 3. A function named qr that implements qr( n) = i, using recursion. Hint: Call your quart function (again). n i= 1 4. A function named qeq that implements the function qeq(n) = qi(n) qr(n) Hint: Call your previous two functions, subtract their results, and return it. 5. A function named hib. You must use recursion. hib(0) = 0 hib(1) = 1 hib(n) = 3 hib(n 1) + 4 hib(n 2) + n 9