1 / 38

Lecture #2, April 5, 2007

Lecture #2, April 5, 2007. Overview of backend issues (storage locations, registers, aliasing), Translating arithmetic, Register allocation, Reducing demand for registers, Reuse of registers, List.find, Anonymous functions. Assignments. Reading Read chapter 7 sections 7.4 and 7.5

daw
Download Presentation

Lecture #2, April 5, 2007

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Lecture #2, April 5, 2007 • Overview of backend issues • (storage locations, registers, aliasing), • Translating arithmetic, • Register allocation, • Reducing demand for registers, • Reuse of registers, • List.find, • Anonymous functions.

  2. Assignments • Reading • Read chapter 7 sections 7.4 and 7.5 • Possible Quiz next Monday on the reading. • Programming assignment #1 is now available on the class website under the assignments link. • PLEASE take note of this as this page is not in the handout.

  3. Over view of Back-end issues • Code Shape • How to choose the correct shape for a language construct • Case – cascading if then else, binary search, dispatch table • Commutative associative operators • Instruction Choice • Which assembly level instructions to use • Register allocation • How to use registers vs memory • Instruction scheduling • Reording instructions

  4. Assigning Storage Locations • Lifetime and scope determine some storage issues • Parameters • Local variables • Global variables • Scoped variables • Static scope in block structured languages • Overridden instance variables in OO languages • Un-named values (x+3) in y = (x+3) * z

  5. Registers • Limited numbers, requires resource allocation • Not all values fit in a register • Some variables (with short lifetimes) only live in registers • Others need to be “stored” when the register is needed for another purpose. • Locations whose “address” is needed (for call by reference, or for pointer-to uses) cannot reside in registers.

  6. Aliasing • Sometimes there are more than 1 name for a location. • Pointers int a, *b; b = &a; • Reference parameters int f(ref int a, ref int b) { int c; c := a; a := b; b := c } call f(x,x) • Array References Arex[i] andx[j-n]different?

  7. Translating Arithmetic • Most machines have a full complement of arithmetic, logical, and shifting operations. • Such operations are almost always register based. • Translating requires. • Getting values in registers • Choosing an order to perform operations • Emitting instructions • Subtleties • Using the fewest registers • Constant folding ie. (x + 3 + 5) -> (x + 8)

  8. - * x 2 y Consider loadAO load at offset loadI @x => r1 loadAO rA,r1 => r2 loadI 2 => r3 loadI @y => r4 loadAO rA,r4 => r5 Mul r3,r5 => r6 Sub r2,r6 => r7

  9. Creating an algorithm • Design data structures to hold source (expressions) and target code (IR). • Decide how to treat registers • Decide how to deal with generating IR sequences.

  10. Expressions • We will reuse the Exp datatype from CS321 datatype Exp = Literal of Constant (* 5, 6.3, true *) | Binop of BINOP * Exp * Exp (* x + 3 *) | Var of Exp option * Id (* object.x, y *) | Relop of RELOP * Exp * Exp (* x < 7.7 *) | Not of Exp (* ! x *) | ArrayElm of Exp * Exp * (Basic TC) (* x[3] *) | ArrayLen of Exp (* x.length() *) | Call of Exp * Id *(Id TC)* Exp list (* x.f(1,z) *) | NewArray of Basic * Exp (* new int[3] *) | NewObject of Id (* new point() *)

  11. IR and Registers • We define a new ML datatype for IR • For today it will only have three general kinds of instructions type Reg = int; datatype IR = LoadI of (string * Reg) | LoadAO of (Reg * Reg * Reg) | Arith of (BINOP * Reg * Reg * Reg); BINOP is reused from the CS321 as well datatype BINOP = ADD | SUB | MUL | DIV (* Arithmetic *) | AND | OR; (* logical *)

  12. Compare The abstract syntax datatype IR = LoadI of (string * Reg) | LoadAO of (Reg * Reg * Reg) | Arith of (BINOP * Reg * Reg * Reg); With the concrete syntax loadI @x => r1 loadAO rA,r1 => r2 loadI 2 => r3 loadI @y => r4 loadAO rA,r4 => r5 Mul r3,r5 => r6 Sub r2,r6 => r7

  13. Computing the concrete Syntax • We can write a pattern matching ML program to compute the concrete syntax from the datatype representing the abstract syntax. • It is an ordinary pattern-matching ML program. datatype IR = LoadI of (string * Reg) | LoadAO of (Reg * Reg * Reg) | Arith of (BINOP * Reg * Reg * Reg); • We will need to write three parts • Converting registers to strings • Converting BINOP to strings • Converting IR itself to a string

  14. Registers to strings type Reg = int; fun showReg 0 = "rA" | showReg n = "r"^Int.toString n; • A register is just an integer, we can use the library function int.toString to turn it into a string • We use the convention that register 0 is the activation record pointer, which we print as “rA” • Other registers print the integer value preceded by “r”

  15. Op to string • A very simple pattern match across the 6 cases. fun showOp ADD = "Add " | showOp SUB = "Sub " | showOp MUL = "Mul " | showOp DIV = "Div " | showOp AND = "And " | showOp OR = "Or "

  16. IR to string • Note the extensive use of “^” the string concatenation function. fun showIR (LoadI(s,r)) = "loadI "^s^" => "^showReg r | showIR (LoadAO(x,y,z)) = "loadAO "^showReg x^","^showReg y^" => "^ showReg z | showIR (Arith(m,x,y,z)) = showOp m^" "^showReg x^","^ showReg y^" => "^showReg z;

  17. Emitting instructions • Emitting instructions is an effect. • It is implicit in the functions “expr”, “base”, and “offsett”. • Instructions are accumulated in the reference variable “emitted” val emitted = ref ([]: IR list); fun emit x = emitted := (x :: (!emitted)); • Note that the instructions are accumulated in the reverse order they are emitted.

  18. Registers • Registers are nothing more than integers. • We need a way to allocate them incrementally • We need to be able to reset the allocation. val Rarp = 0; val firstRegister = 1; val regCount = ref firstRegister; fun resetRegister () = regCount := firstRegister; fun NextRegister() = let val n = !regCount in (regCount := n+1; n) end;

  19. Base and offsett • We assume that all variables can be accessed as an offset from some base address. • The function “base” loads the base address for a variable into a register by emitting the correct code and then returns the register. • The function “offset” loads the offset into a register by emitting code, and then returns the register. • For simplicity • The base address is always assumed stored in the activation record pointer stored in register 0, which we write “ra” • The offset is some constant we write as “@x” for variable “x”, i.e. the address or offset for “x” • Later on we will relax these assumptions.

  20. fun offset (Var(_,x)) = let val result = NextRegister() in emit (LoadI("@"^x,result)); result end; fun base x = Rarp;

  21. Translation fun expr node = case node of Var(loc,x) => let val t1 = base node val t2 = offset node val result = NextRegister() in emit (LoadAO(t1,t2,result)); result end | Binop(m,x,y) => let val t1 = expr x val t2 = expr y val result = NextRegister() in emit (Arith(m,t1,t2,result)); result end | Literal(loc,Cint n) => let val result = NextRegister() in emit (LoadI(n,result)); result end

  22. Results fun var x = (Var(NONE,x)); fun const n = Literal(Cint n); val ex1 = Binop(SUB, (* x – (2 * y) *) var "x", Binop(MUL, const 2, var "y"));

  23. - trans ex1; loadI @x => r1 loadAO rA,r1 => r2 loadI 2 => r3 loadI @y => r4 loadAO rA,r4 => r5 Mul r3,r5 => r6 Sub r2,r6 => r7 val it = [LoadI ("@x",1),LoadAO (0,1,2),LoadI ("2",3), LoadI ("@y",4),LoadAO (Arith (MUL,3,5,6), Arith (SUB,2,6,7)] : IR list

  24. The function trans fun trans x = let val _ = emitted := [] val _ = resetRegister () val result = expr x val instrs = rev (!emitted) val _ = print "\n"; fun sh x = print(showIR x^"\n") val _ = map sh instrs val _ = print "\n\n"; in instrs end; Reset the emitted instructions and the next register counter The emiited instructions are collected in reverse order Print the instructions for the user to inspect

  25. Register Allocation • Register allocation assigns physical registers to each logical registers. Think of it as a renaming, where we have a limited number of names. • We can reuse a register after we have no further demands on it. no further demand loadI @x => r1 { } loadAO rA,r1 => r2 {r1} loadI 2 => r3 {r1} loadI @y => r4 {r1} loadAO rA,r4 => r5 {r1,r4} Mul r3,r5 => r6 {r1,r3,r4,r5} Sub r2,r6 => r7 {r1,r2,r3,r4,r5,r6}

  26. Immediate reuse. loadI @x => r1 loadAO rA,r1 => r1 loadI 2 => r2 loadI @y => r3 loadAO rA,r3 => r3 Mul r2,r3 => r2 Sub r1,r2 => r2 loadI @x => r1 loadAO rA,r1 => r2 loadI 2 => r3 loadI @y => r4 loadAO rA,r4 => r5 Mul r3,r5 => r6 Sub r2,r6 => r7

  27. Can we do better? loadI @x => r1 loadAO rA,r1 => r2 loadI 2 => r3 loadI @y => r4 loadAO rA,r4 => r5 Mul r3,r5 => r6 Sub r2,r6 => r7 • x – (2 * y) • Loading x first (before computing (2 * y)) causes the lifetime of r2 to be very long. • Suppose we translated (2*y) before x?

  28. Translating (2 * y) first loadI @y => r1 loadAO rA,r1 => r2 loadI 2 => r3 Mul r2,r3 => r4 loadI @x => r5 loadAO rA,r5 => r6 Sub r4,r6 => r7 Before register allocation After register allocation loadI @y => r1 loadAO rA,r1 => r1 loadI 2 => r2 Mul r2,r1 => r1 loadI @x => r2 loadAO rA,r2 => r2 Sub r2,r1 => r1

  29. Compare Translating x first translating (2 * y) first loadI @y => r1 loadAO rA,r1 => r1 loadI 2 => r2 Mul r2,r1 => r1 loadI @x => r2 loadAO rA,r2 => r2 Sub r2,r1 => r1 loadI @x => r1 loadAO rA,r1 => r1 loadI 2 => r2 loadI @y => r3 loadAO rA,r3 => r3 Mul r2,r3 => r2 Sub r1,r2 => r2

  30. Rule of thumb • To avoid tying up registers for a long time, we should translate subexpressions which are large first. • How do we determine large? • An expression is larger than another expression, it requires more registers to translate. • Chicken an egg problem. • To translate we need to know how many registers • To know how many registers we need to translate • Solution • Pre-processing • Dynamic exploration

  31. Variable reuse. • Consider translating x – (2 * x) loadI @x => r1 loadAO rA,r1 => r2 loadI 2 => r3 loadI @x => r4 loadAO rA,r4 => r5 Mul r3,r5 => r6 Sub r2,r6 => r7 loadI @x => r1 loadAO rA,r1 => r2 loadI 2 => r3 Mul r3,r2 => r4 Sub r2,r4 => r5 Register allocation loadI @x => r1 loadAO rA,r1 => r1 loadI 2 => r2 Mul r2,r1 => r1 Sub r2,r1 => r1

  32. How could we implement this? • If we kept a dictionary of which register (if any) a variable resides in, we could try this first in the Var case of “expr” case node of Var(loc,x) => let val t1 = base node val t2 = offset node val result = NextRegister() in emit (LoadAO(t1,t2,result)); result end

  33. Using the dictionary fun expr2 dict node = case node of Var(loc,x) => (case List.find (fn (nm,r) => x=nm) (!dict) of NONE => let val t1 = base node val t2 = offset node val result = NextRegister() in emit (LoadAO(t1,t2,result)); dict := (x,result)::(!dict); result end | SOME(x,r) => r)

  34. The List.find programming pattern Case List.find (fn (nm,r) => x=nm) (!dict) of NONE => … | SOME(_,z) => … • In SML we use library functions all the time. • Int.toString • List.find • The List.find function is particularly useful. • It takes a function as an argument • List.find : ('a -> bool) -> 'a list -> 'a option • It is worth studying this function closely

  35. Anonymous functions • Study: (fn x => (x+1)) • It is an anonymous function. A function without a name. • It has one parameter “x” • It adds one to its parameter, and returns the result. (fn x => (x+1)) 4; val it = 5 : int • Any non-recursive function can be written anonymously. • (fn x => x = 5) • Tests if its parameter is equal to 5 (fn x => x=5) 8; val it = false : bool; • (fn x => fn y => (x,y)) • Has two parameters • Returns a pair • (fn (x,y) => (not y, x+3)) • What is the type of this function?

  36. List.find • Used for searching a list. • List.find : ('a -> bool) -> 'a list -> 'a option • Uses a function as a parameter to determine if the search is successful. • E.g. Is there an even element in a list? List.find even [1,3,5]; val it = NONE : int option List.find even [1,3,4]; val it = SOME 4 : int option

  37. List.find and anonymous functions List.find (fn x => x = "Tim") ["Tom", "Jane"]; val it = NONE : string option List.find (fn x => even x andalso x>10) [2,4,5,12]; val it = SOME 12 : int option

  38. Assignment #1 CS322 Prog Lang & Compilers Prog Assignment #1 Assigned Wednesday April 5, 2007. Due Monday, April 10, 2007 This assignment is to extend the program discussed in class for translating Arithmetic expressions to IR code. The extension is to evaluate binary operators in a manner that decreases the number of registers needed. The rule of thumb is that given a binary operator (exp1 + exp2) we should first translate the sub expression (either exp1 or exp2) that is more demanding. I.e. we should translate first the sub expression that when translated will require the most registers. See the assignments link on the class web page for a full description.

More Related