119
Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Embed Size (px)

Citation preview

Page 1: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

1

Theory of Compilation 236360

Erez Petrank

Lecture 8: Runtime.

Page 2: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Runtime Environment

• Code generated by the compiler to handle stuff that the programmer does not wish to handle.

• For example: file handling, memory management, synchronization (create threads, implement locks, etc.), runtime stack (activation records), dynamic optimization, debugging, etc.

• You can think of all those as library functions, but they are always there by languages definition. – No need to link them in.

• The more complex items nowadays are dynamic memory management, JIT optimization, and management of parallelism.

• We will talk about activation records and present an introduction to memory management. – Threads and synchronization are discussed in the

operating systems course.

Page 3: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

3

Microsoft CLR

(image source: wikipedia)

Page 4: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

4

Java Virtual Machine (JVM)

Language On JVM

Erlang Erjang

JavaScript Rhino (?)

Pascal Free Pascal

PHP Quercus

Python Jython

REXX NetRexx

Ruby Jruby

Tcl Jacl

(image source: wikipedia)

Page 5: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

5

JavaScript Engine (Mozilla SpiderMonkey)

• SpiderMonkey is a fast interpreter • SpiderMonkey contains two JavaScript Just-In-Time (JIT)

compilers, a garbage collector, code implementing the basic behavior of JavaScript values…

• SpiderMonkey's interpreter is mainly a single, tremendously long function that steps through the bytecode one instruction at a time, using a switch statement (or faster alternative, depending on the compiler) to jump to the appropriate chunk of code for the current instruction.

(source: https://developer.mozilla.org/En/SpiderMonkey/Internals)

Page 6: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

6

Activation Records and Function Call

Page 7: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

7

Motivation

• Functions are invoked frequently; it is important to understand what goes on during the execution.

• Handling functions has a significant impact on efficiency.

Page 8: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

8

Support of Procedures

• new computing environment – at least temporary memory for local variables

• passing information into the new environment– parameters

• transfer of control to/from procedure• handling return values

Page 9: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

9

Design Decisions

• scoping rules– static scoping vs. dynamic scoping

• caller/callee conventions– parameters– who saves register values?

• allocating space for local variables

Page 10: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

10

Static (lexical) Scopingmain ( ){

int a = 0 ;int b = 0 ;{

int b = 1 ;{

int a = 2 ;printf (“%d %d\n”, a, b)

}{

int b = 3 ;printf (“%d %d\n”, a, b) ;

}printf (“%d %d\n”, a, b) ;

}printf (“%d %d\n”, a, b) ;

}

B0

B1

B3B3

B2

Declaration Scopes

a=0 B0,B1,B3

b=0 B0

b=1 B1,B2

a=2 B2

b=3 B3

In most modern languages (C, Java), a name refers to its (closest) enclosing scope

known at compile time

Page 11: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

11

Dynamic Scoping

• Each identifier is associated with its latest definition in the run.

• To find the relevant definition, search in the local function first, then search in the function that called the local function, then search in the function that called that function, and so on.

Page 12: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

12

Dynamic Scoping Implementation

• Each identifier is associated with a global stack of bindings

• When entering scope where identifier is declared– push declaration on identifier stack

• When exiting scope where identifier is declared– pop identifier stack

• Evaluating the identifier in any context binds to the current top of stack

• Determined at runtime• Not always possible to type-check or discover

access to non-initialized variables at compile-time.

Page 13: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

13

Example

• what value is returned from main?• static scoping?• dynamic scoping?

int x = 42;

int f() { return x; } int g() { int x = 1; return f(); }int main() { return g(); }

Page 14: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

14

Static Scoping Properties

• Java, C++, etc.

• Identifier binding is known at compile time• Address of the variable is known at compile time• Assigning addresses to variables is part of code

generation• No runtime errors of “access to undefined

variable”• Can check types of variables

Page 15: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

A Naïve Implementation Using a Table

var x: int;

int foo(): int { return x; }

int bar(): int { var x: int; x = 1;return foo();

}int main() { x = 0;

print bar(); }

Identifier Address

x (global) 432234

x (in bar) 432238

Page 16: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

A problem with the naïve implementation: Recursion (direct or indirect)

What is the address of ‘a’ (of fib), in the following example?

procedure fib(n: int) } var a: int;var b: int;if (n == 2) return 1;if (n ≤ 1) return 0;a = fib(n – 1);b = fib(n – 2);return a + b;

}procedure main() { print fib(5);

}• Note that this problem does not exist for dynamic scoping.

Page 17: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

17

Activation Record (or Frame)

• A separate space for each procedure invocation

• Managed at runtime– code for managing it generated by the compiler

• Desired properties – efficient allocation and deallocation

• procedures are called frequently

– variable size • different procedures may require different memory sizes

Page 18: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

18

Memory Layout

stack grows down (towards lower addresses)

heap grows up (towards higher

addresses)heap

code

static data

stack

Page 19: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

19

Activation Record (frame)parameter k

parameter 1

access link

return information

dynamic link

registers & misc

local variablestemporaries

next frame would be here

administrativepart

high addresses

lowaddresses

framepointer

stackpointer

incoming parameters

stack grows down

Page 20: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

20

Runtime Stack

• Stack of activation records• Call = push new activation record• Return = pop activation record• Only one “active” activation record – top of stack• Recursion handled implicitly.

Page 21: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

21

Runtime Stack

• SP – stack pointer – top of current frame

• FP – frame pointer – base of current frame– Sometimes called BP

(base pointer)

• Intel has 8 registers: EAX, EBX, ECX, EDX, ESI, EDI, ESP (stack pointer), EBP (base pointer).

• 2 out of 8 registers are used to manage the stack!

Current frame

… …

Previous frame

SP

FPstack grows down

Page 22: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Pentium Runtime Stack

22

Pentium stack registers

Pentium stack and call/ret instructions

Register Usage

ESP Stack pointer

EBP Base pointer

Instruction Usage

push, pusha,… push on runtime stack

pop,popa,… pop

call transfer control to called routine

return transfer control back to caller

Page 23: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

23

Call Sequences

• The processor does not save the content of registers on procedure calls

• So who will? – Caller saves and restores registers– Callee saves and restores registers– But can also have both save/restore some registers

Page 24: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Call Sequences

24

call

calle

r

calle

e

return

calle

r

Caller push code

Callee push code (prologue)

Function codeCallee pop code

(epilogue)

Caller pop code

Push caller-saved registersPush actual parameters (in reverse order)

push return addressJump to call address

Push current base-pointerbp = spPush local variablesPush callee-save registers

Pop callee-save registersPop callee activation recordPop old base-pointer

pop return addressJump to address

Pop parametersPop caller-saved registers

Page 25: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Call Sequences

25

call

calle

rca

llee

return

calle

r

add $8, %esppop %ecx

Push caller-saved registersPush actual parameters (in reverse order)

push return addressJump to call address

Push current base-pointerbp = spPush local variablesPush callee-save registers

Pop callee-save registersPop callee activation recordPop old base-pointer

pop return addressJump to address

Pop parametersPop caller-save registersPop parameters

push %ecxpush $21push $42call _foo

Function code

push %ebpmov %esp, %ebpsub $8, %esppush %ebx

pop %ebxmov %ebp, %esppop %ebpret

call

Page 26: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

26

“To Callee-save or to Caller-save?”

• Which of them implies more efficiency? • Callee-saved registers need only be saved when

callee modifies their value. • Caller saving:

– calling a recursive routine, we want to save before the call.

– assumes nothing on callee (library, unknown code).

• Typically, heuristics and conventions are followed.

Page 27: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

27

Accessing Stack Variables

• Use offset from FP (%ebp)• Remember –

stack grows downwards• Above FP = parameters• Below FP = locals• Examples

– %ebp + 4 = return address– %ebp + 8 = first parameter– %ebp – 4 = first local

… …

SP

FP

Return address

Local 1…

Local n

Previous fp

Param n…

param1FP+8

FP-4

Page 28: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

28

Factorial – fact(int n)fact:pushl %ebp # save frame pointermovl %esp,%ebp # ebp=esppushl %ebx # save ebxmovl 8(%ebp),%ebx # ebx = ncmpl $1,%ebx # n = 1 ?jle .lresult # then doneleal -1(%ebx),%eax # eax = n-1pushl %eax # call fact # fact(n-1)imull %ebx,%eax # eax=retv*njmp .lreturn # .lresult:movl $1,%eax # retv.lreturn:movl -4(%ebp),%ebx # restore ebxmovl %ebp,%esp # restore esppopl %ebp # restore ebp

ESP

EBP

Return address

old %ebx

Previous fp

nEBP+8

EBP-4 old %ebp

old %eax

(stack in intermediate point)

(disclaimer: real compiler can do better than that)

Page 29: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

29

Windows Exploit(s) Buffer Overflow

void foo (char *x) { char buf[2]; strcpy(buf, x); } int main (int argc, char *argv[])

{ foo(argv[1]); }

./a.out abracadabraSegmentation fault Stack grows

this way

Memory addresses

Previous frame

Return address

Saved FP

char* x

buf[2]

ab

ra

ca

da

br

Page 30: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

30

Runtime checks

• generate code for checking attempted illegal operations– Null pointer check

• MoveField, MoveArray, ArrayLength, VirtualCall• Reference arguments to library functions should not be null

– Array bounds check– Array allocation size check– Division by zero– …

• If check fails jump to error handler code that prints a message and gracefully exists program

Page 31: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

31

Null pointer check

# null pointer check cmp $0,%eax je labelNPE

labelNPE: push $strNPE # error message call __println push $1 # error code call __exit

Single generated handler for entire program

Page 32: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

32

Array bounds check # array bounds check # ecx = index mov -4(%eax),%ebx # ebx = length cmp %ebx,%ecx jle labelABE # ebx <= ecx ? cmp $0,%ecx jl labelABE # ecx < 0 ?

labelABE: push $strABE # error message call __println push $1 # error code call __exit

Single generated handler for entire program

Page 33: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

33

Array allocation size check

# array size check cmp $0,%eax # eax == array size jle labelASE # eax <= 0 ?

labelASE: push $strASE # error message call __println push $1 # error code call __exit

Single generated handler for entire program

Page 34: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

34

Runtime Checks

• Improve reliability and security• But: costly!

• Java implements them. • C doesn’t.

Page 35: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

35

Nested Procedures

• For example – Pascal, Javascript• any routine can have sub-routines• any sub-routine can access anything that is

defined in its containing scopes or inside the sub-routine itself

Page 36: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

36

Example: Nested Procedures

program p;var x: Integer;procedure a

var y: Integer;procedure b begin…b… end;

function cvar z: Integer;procedure d begin…d… end;

begin…c…end;

begin…a… end;

begin…p… end.

possible call sequence:

pa a c b c d

What is the address of variable “y” in procedure d?

Page 37: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

37

nested procedures• Can call a sibling, ancestor, and ancestor’s siblings. • But can use variables only of ancestors!

• B can call C as it is defined in the ancestor A.• But if C updates y, it updates A’s y.

Procedure A; var y: int; Procedure B;

var y: real;begin …. end;

Procedure C;begin y:=5; end;

Page 38: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

38

nested procedures• When “c” uses variables from “a”, which “a” is it?• how do we find the right activation record at runtime?• Goal: find the closest activation record of a given nesting

level. • if routine of level k uses variables of the same level, it uses

its own variables.• if it uses variables of level

j < k then it must be the last routine called at level j

• If a procedure is last at level j on the stack, then it must be an ancestor of the current routine

P

a

b

P

Pc

Pd

a Pa

Pc

Pd

Page 39: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

39

Finding the Relevant Variable

• problem: a routine may need to access variables of another routine that contains it statically

• solution: the access link in the activation record• The access link points to the last activation record of the

nesting level above it– in our example, access link of d points to activation records of

c

• Access links are created at runtime• number of links to be traversed is known at compile time

• Cost while accessing a variable: traversing pointers from one nesting level to the other.

• Cost while entering a routine: walk the stack to find the closest routine with one lower nesting level.

Page 40: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

40

access links

program p;var x: Integer;procedure a var y: Integer; procedure b begin…b…

end; procedure c

var z: Integer; procedure d begin…d… end;

begin…c…end; begin…a… end;begin…p… end.

a

a

c

b

c

d

y

y

z

z

possible call sequence:pa a c b c d

a

b

P

c c

d

a

Page 41: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

41

Efficient management of the access links

• We maintain an array: the display array. • Size: max nesting level, • Content: D[i] holds a pointer to the closest

activation record with nesting level i. • When we need to access a variable in a

containing method, we can access it easily using the display array.

Page 42: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Managing the display array

• When a routine of nesting level i is called:

• Save the value of D[i] in the activation record (for restoration on exit)

• Update D[i] to point at the new activation record.

• On returning from the routine, restore the previous value of D[i].

access link

q(1, 9)

sd [ 1 ]

d [ 2 ]

Page 43: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Managing the display array

• When a routine of nesting level i is called:

• Save the value of D[i] in the activation record (for restoration on exit)

• Update D[i] to point at the new activation record.

• On returning from the routine, restore the previous value of D[i].

saved d [ 2 ]

q(1, 3)

saved d [ 2 ]

q(1, 9)

sd [ 1 ]

d [ 2 ]

Page 44: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Managing the display array

• When a routine of nesting level i is called:

• Save the value of D[i] in the activation record (for restoration on exit)

• Update D[i] to point at the new activation record.

• On returning from the routine, restore the previous value of D[i].

saved d [ 3 ]

p(1, 3)

saved d [ 2 ]

q(1, 3)

saved d [ 2 ]

q(1, 9)

sd [ 1 ]

d [ 2 ]

d [ 3 ]

Page 45: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Managing the display array

• When a routine of nesting level i is called:

• Save the value of D[i] in the activation record (for restoration on exit)

• Update D[i] to point at the new activation record.

• On returning from the routine, restore the previous value of D[i].

saved d [ 2 ]

e(1, 3)

saved d [ 3 ]

p(1, 3)

saved d [ 2 ]

q(1, 3)

saved d [ 2 ]

q(1, 9)

sd [ 1 ]

d [ 2 ]

d [ 3 ]

Page 46: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

46

Cost of finding a Variable

• Without the Display: – Accessing a variable: traversing pointers from one nesting level to the

other. – Entering a routine: walk the stack to find the closest routine with one

lower nesting level.

• Using the Display: – Accessing a variable: constant. (check the Display.) – Entering/exiting a routine: constant. (update the Display using the

current frame.)

• Runtime costs !

Page 47: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

47

Activation Records: Summary

• compile time memory management for procedure data

• works well for data with well-scoped lifetime– deallocation when procedure returns

Page 48: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

48

Dynamic Memory Management: Introduction

There is a course about this topic: 236780 “Algorithms for dynamic memory management”

Page 49: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

49

Static and Dynamic Variables

• Static variables are defined in a method and are allocated on the runtime stack, as explained in the first part of this lecture.

• Sometimes there is a need for allocation during the run. – E.g., when managing a linked-list whose size is not

predetermined.

• This is dynamic allocation. • In C, “malloc” allocates a space and “delete” says that the

program will not use this space anymore.

Ptr = malloc (256 bytes);/* Use ptr */Free (Ptr);

Page 50: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

50

Dynamic Memory Allocation

• In Java, “new” allocates an object for a given class. – President obama = new President

• But there is no instruction for manually deleting the object.

• Objects reclaimed by a garbage collector when the program “does not need it” anymore.

course c = new course(236360);c.class = “TAUB 2”;Faculty.add(c);

Page 51: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

51

Manual Vs. Automatic Memory Management

• A manual memory management lets the programmer decide when objects are deleted.

• A memory manager that lets a garbage collector delete objects is called automatic.

• Manual memory management creates severe debugging problems– Memory leaks,– Dangling pointers.

• In large projects where objects are shared between various components it is sometimes difficult to tell when an object is not needed anymore.

• Considered the BIG debugging problem of the 80’s• What is the main debugging problem today?

Page 52: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

52

Automatic Memory Reclamantion

• When the system “knows” the object will not be used anymore, it reclaims its space.

• Telling whether an object will be used after a given line of code is undecidable.

• Therefore, a conservative approximation is used. • An object is reclaimed when the program has “no way of

accessing it”. • Formally, when it is unreachable by a path of pointers from

the “root” pointers, to which the program has direct access. – Local variables, pointers on stack, global (class) pointers, JNI

pointers, etc.

• It is also possible to use code analysis to be more accurate sometimes.

Page 53: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

What’s good about automatic “garbage collection”?

© Erez Petrank 53

• Software engineering: – Relieves users of the book-keeping burden. – Stronger reliability, fewer bugs, faster debugging. – Code understandable and reliable. (Less interaction

between modules.)

• Security (Java):– Program never gets a pointer to “play with”.

Page 54: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

54

Importance

• Memory is the bottleneck in modern computation. – Time & energy (and space).

• Optimal allocation (even if all accesses are known in advance to the allocator) is NP-Complete (to even approximate).

• Must be done right for a program to run efficiently.

• Must be done right to ensure reliability.

Page 55: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

GC and languages

© Erez Petrank 55

• Sometimes it’s built in:– LISP, Java, C#.– The user cannot free an object.

• Sometimes it’s an added feature:– C, C++.– User can choose to free objects or not. The collector

frees all objects not freed by the user.

• Most modern languages are supported by garbage collection.

Page 56: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

© Erez Petrank 56

Most modern languages rely on GC

Source: “The Garbage Collection Handbook” by Richard Jones, Anthony Hosking, and Eliot Moss.

61

7

Page 57: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

What’s bad about automatic “garbage collection”?

© Erez Petrank 57

• It has a cost: – Old Lisp systems 40%.– Today’s Java program (if the collection is done “right”)

5-15%.

• Considered a major factor determining program efficiency.

• Techniques have evolved since the 60’s. We will only go over basic techniques.

Page 58: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

58

Garbage Collection Efficiency

• Overall collection overheads (program throughput).

• Pauses in program run. • Space overhead. • Cache Locality (efficiency and energy).

Page 59: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

59

Three classical algorithms

• Reference counting• Mark and sweep (and mark-compact)• Copying.

• The last two are also called tracing algorithms because they go over (trace) all reachable objects.

Page 60: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Reference counting

60

• Recall that we would like to know if an object is reachable from the roots.

• Associate a reference count field with each object: how many pointers reference this object.

• When nothing points to an object, it can be deleted.

• Very simple, used in many systems.

Page 61: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Basic Reference Counting

61

• Each object has an RC field, new objects get o.RC:=1.

• When p that points to o1 is modified to point to o2 we execute: o1.RC--, o2.RC++.

• if then o1.RC==0:– Delete o1.

– Decrement o.RC for all “children” of o1.

– Recursively delete objects whose RC is decremented to 0.

o1 o2

p

Page 62: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

A Problem: Cycles

62

• The Reference counting algorithm does not reclaim cycles!

• Solution 1: ignore cycles, they do not appear frequently in modern programs.

• Solution 2: run tracing algorithms (that can reclaim cycles) infrequently.

• Solution 3: designated algorithms for cycle collection.

• Another problem for the naïve algorithm: requires a lot of synchronization in parallel programs.

• Advanced versions solve that.

Page 63: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

The Mark-and-Sweep Algorithm

63

• Mark phase:– Start from roots and traverse all objects reachable by

a path of pointers. – Mark all traversed objects.

• Sweep phase:– Go over all objects in the heap. – Reclaim objects that are not marked.

Page 64: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

The Mark-Sweep algorithm

64

• Traverse live objects & mark black. • White objects can be reclaimed.

sta

ckHeap

registers

Roots

Note! This is not the heap data structure!

Page 65: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Triggering

65

New(A)=if free_list is empty

mark_sweep() if free_list is empty

return (“out-of-memory”)pointer = allocate(A)return (pointer)

Garbage collection is triggered by allocation.

Page 66: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Basic Algorithm

66

mark_sweep()= for Ptr in Roots

mark(Ptr)sweep()

mark(Obj)=if mark_bit(Obj) == unmarked

mark_bit(Obj)=markedfor C in Children(Obj)

mark(C)

Sweep()=p = Heap_bottomwhile (p < Heap_top)

if (mark_bit(p) == unmarked) then free(p)else mark_bit(p) = unmarked; p=p+size(p)

Page 67: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Properties of Mark & Sweep

67

• Most popular method today (at a more advanced form). • Simple.• Does not move objects, and so heap may fragment. • Complexity:

Mark phase: live objects (dominant phase) Sweep phase: heap size.

• Termination: each pointer traversed once. • Various engineering tricks are used to improve

performance.

Page 68: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

• During the run objects are allocated and reclaimed. • Gradually, the heap gets fragmented. • When space is too fragmented to allocate, a compaction

algorithm is used. • Move all live objects to the beginning of the heap and

update all pointers to reference the new locations. • Compaction is considered very costly and we usually

attempt to run it infrequently, or only partially.

Mark-Compact

68

The Heap

Page 69: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

69

An Example: The Compressor

• A simplistic presentation of the Compressor: • Go over the heap and compute for each live object where it

moves to – To the address that is the sum of live space before it in the

heap. – Save the new locations in a separate table.

• Go over the heap and for each object: – Move it to its new location– Update all its pointers.

• Why can’t we do it all in a single heap pass? • (In the full algorithm: succinct table, execute the first pass

very quickly, and parallelization.)

Page 70: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

70

Mark Compact

• Important parameters of a compaction algorithm:– Keep order of objects?– Use extra space for compactor data structures?– How many heap passes? – Can it run in parallel on a multi-processor?

• We do not elaborate in this intro.

Page 71: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Copying garbage collection

71

• Heap partitioned into two.• Part 1 takes all allocations.• Part 2 is reserved. • During GC, the collector traces all reachable

objects and copies them to the reserved part. • After copying the parts roles are reversed: • Allocation activity goes to part 2, which was

previously reserved. • Part 1, which was active, is reserved till next

collection.

1 2

Page 72: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Copying garbage collection

72

Part I Part II

Roots

A

D

C

B

E

Page 73: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

The collection copies…

73

Part I Part II

Roots

A

D

C

B

E

A C

Page 74: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Roots are updated; Part I reclaimed.

74

Part I Part II

Roots

A C

Page 75: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Properties of Copying Collection

75

• Compaction for free• Major disadvantage: half of the heap is not

used. • “Touch” only the live objects

– Good when most objects are dead. – Usually most new objects are dead, and so there are

methods that use a small space for young objects and collect this space using copying garbage collection.

Page 76: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

A very simplistic comparison

Copying

Mark & sweep

Reference Counting

Live objects

Size of heap (live objects)

Pointer updates + dead objects

Complexity

Half heap wasted

Bit/object + stack for DFS

Count/object + stack for DFS

Space overhead

For free Additional work Additional work

Compaction

long long Mostly short Pause time

Cycle collection

More issues

Page 77: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

© Erez Petrank 77

Generational Garbage Collection

“The weak generational hypothesis”: most objects die young.

Using the hypothesis: separate objects according to their ages and collect

the area of the young objects more frequently.

Page 78: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

© Erez Petrank 78

More Specifically,

• The heap is divided into two or more areas (generations).

• Objects allocated in 1st (youngest) generation. • The youngest generation is collected frequently. • Objects that survive in the young generation

“long enough” are promoted to the old generation.

Page 79: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

© Erez Petrank 79

• Short pauses: the young generation is kept small and so most pauses are short.

• Efficiency: collection efforts are concentrated where many dead objects exists.

• Locality: • Collector: mostly concentrated on a small part of

the heap• Program: allocates (and mostly uses) young

objects in a small part of the memory.

Advantages

Page 80: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

© Erez Petrank 80

Mark-Sweep or Copying ?

• Copying is good when live space is small (time) and heap is small (space).

• A popular choice: – Copying for the (small) young generation.– Mark-and-sweep for the full collection.

• A small waste in space, high efficiency.

Page 81: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

© Erez Petrank 81

Inter-Generational Pointers

• Pointers from objects in the old generation to objects in the young generation may witnesses the liveness of an object.

• We don’t want to trace the old generation to see if the pointing objects are alive. – Why?

• The solution: – “Keep a list” of all inter-generational pointers.– Assume (conservatively) the parent (old) object is

alive. – Treat these pointers as additional roots.

• “Typically”: most pointers are from young to old (few from old to young).

• When collecting the old generation, collect the entire heap.

Page 82: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

© Erez Petrank 82

Inter-Generational Pointers

• Inter-generational pointers are created: – When objects are promoted to old generation– When pointers are modified in the old generation.

• The first can be monitored by the collector during promotion.

• The second requires a write barrier.

Page 83: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

© Erez Petrank 83

Write Barrier

Update(object y, field f, object x) { if y is in old space and x is in young

remember y.f->x ; y.f := x;}

x f

young old

Reference counting also had a write-barrier:

update(object y, field f, object x) {x.RC++; // increment new referent

y.f^.RC--; // decrement old referentif (y.f^.RC == 0) collect(y.f); y.f := x;

}

Y

Page 84: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

84

Memory Management with Parallel Processors

• Stop the world• Parallel (stop-the-world) GC• Concurrent GC

• Trade-offs in pauses and throughput• Difference in complexity• Choose between parallel (longer pauses) and

concurrent (lower throughput)

Page 85: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

© Erez Petrank 85

Terminology

Stop-the-World

Parallel

Concurrent

On-the-Fly

programGC

Page 86: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

© Erez Petrank 86

Concurrent GC• Problem: Long pauses disturb the user.An important measure for the collection: length

of pauses.

• Can we just run the program while the collector runs on a different thread?

• Not so simple!• The heap changes while we collect. • For example,

– we look at an object B, but before we have a chance to mark its children, the program changes them.

Page 87: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

© Erez Petrank 87

Example

A

B

C

A

B

C

A

B

C

GC marks A’s children and makes A black.

Time

Programmodifies pointer.

Collector fails to trace C.

Problem: a new un-nonticed object becomes a child of an already scanned object.

Page 88: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

88

Solution with a write-barrier

• The program notifies the collector that changes happen so that it can mark objects conservatively.

• E.g.: the program registers objects that gain pointers.

• update(object y, field f, object x) {notifyCollector(x); // record new referent y.f := x;

}

• Collector can then assume all recordedobjects are alive (and trace their descendants as live).

Y

XZ

Page 89: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

89

Modern Memory Management

• Handle parallel platforms.• Real-time. • Cache consciousness. • Handle new platforms (GPU, PCM, …) • Hardware assistance.

Page 90: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

90

Summary: Dynamic Memory Management

• Compiler generates code for allocation of objects and garbage collection when necessary.

• Reference-Counting, Mark-Sweep, Mark-Compact, Copying. • Generations

– inter-generational pointers, write barrier.

• Concurrent garbage collection

Page 91: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

91

Runtime Summary• Runtime:

– services that are always there: function calls, memory management, threads, etc.

– We discussed function calls• scoping rules• activation records• caller/callee conventions• Nested procedures (and the display array)

– Memory Management• mark-sweep• copying• reference counting• compaction

Page 92: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

92

OO Issues

Page 93: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

93

Representing Data at Runtime

• Source language types– int, boolean, string, object types

• Target language types– Single bytes, integers, address representation

• Compiler should map source types to some combination of target types– Implement source types using target types

Page 94: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

94

Basic Types

• int, boolean, string, void• Arithmetic operations

– Addition, subtraction, multiplication, division, remainder

• Can be mapped directly to target language types and operations

Page 95: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

95

Pointer Types

• Represent addresses of source language data structures

• Usually implemented as an unsigned integer• Pointer dereferencing – retrieves pointed value

• May produce an error– Null pointer dereference – when is this error triggered?

Page 96: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

96

Object Types

• An object is a record with built in methods and some additional features.

• Basic operations– Field selection + read/write

• computing address of field, dereferencing address

– Copying• copy block (not Java) or field-by-field copying

– Method invocation• Identifying method to be called, calling it

• How does it look at runtime?

Page 97: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

97

Object Types

class Foo { int x; int y;

void rise() {…} void shine() {…}}

x

y

rise

shine

Compile time information

Runtime memory layout for object of class Foo

DispacthVectorPtr

Page 98: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

98

Field Selection

x

y

rise

shine

Compile time information

Runtime memory layout for object of class Foo

DispacthVectorPtrFoo f;int q;

q = f.x;

MOV f, %EBXMOV 4(%EBX), %EAXMOV %EAX, q

base pointer

field offset

from base pointer

Page 99: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

99

Object Types - Inheritance

x

y

rise

shine

Compile time information

Runtime memory layout for object of class Bar

twinkle

z

DispacthVectorPtr

class Foo { int x; int y;

void rise() {…} void shine() {…}}

class Bar extends Foo{ int z; void twinkle() {…}}

Page 100: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

100

Object Types - Polymorphism

class Foo { … void rise() {…} void shine() {…}}

x

y

Runtime memory layout for object of class Bar

class Bar extends Foo{ …}

z

class Main { void main() { Foo f = new Bar(); f.rise();}

f

Pointer to Bar

Pointer to Foo inside Bar

DVPtr

Page 101: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

101

Static & Dynamic Binding

• Which “rise” should is main() using? • Static binding: f is of type Foo and therefore it always refers

to Foo’s rise. • Dynamic binding: f points to a Bar object now, so it refers

to Bar’s rise.

class Foo { … void rise() {…} void shine() {…}}

class Bar extends Foo{ void rise() {…}}

class Main { void main() { Foo f = new Bar(); f.rise();}

Page 102: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

102

Typically, Dynamic Binding is used

• Finding the right method implementation at runtime according to object type

• Using the Dispatch Vector (a.k.a. Dispatch Table)

class Foo { … void rise() {…} void shine() {…}}

class Bar extends Foo{ void rise() {…}}

class Main { void main() { Foo f = new Bar(); f.rise();}

Page 103: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

103

Dispatch Vectors in Depth

• Vector contains addresses of methods• Indexed by method-id number• A method signature has the same id number for all subclasses

class Main { void main() { Foo f = new Bar(); f.rise();}

class Foo { … void rise() {…} void shine() {…}}

01

class Bar extends Foo{ void rise() {…}}

0

xyz

fPointer to Bar

Pointer to Foo inside BarDVPtr

shine

rise

shinerise

Dispatchvector for Bar

Methodcode

using Bar’s

dispatch table

Page 104: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

104

Dispatch Vectors in Depthclass Main { void main() { Foo f = new Foo(); f.rise();}

class Foo { … void rise() {…} void shine() {…}}

01

class Bar extends Foo{ void rise() {…}}

0

xy

f

Pointer to Foo

DVPtrshine

rise

shinerise

using Foo’s

dispatch table

Dispatchvector for Foo

Methodcode

Page 105: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

Multiple Inheritance

105

supertyping convert_ptr_to_E_to_ptr_to_C(e) = e convert_ptr_to_E_to_ptr_to_D(e) = e + sizeof (class C)

subtyping convert_ptr_to_C_to_ptr_to_E(e) = e convert_ptr_to_D_to_ptr_to_E(e) = e - sizeof (class C)

class C { field c1; field c2; void m1() {…} void m2() {…}}class D { field d1; void m3() {…} void m4() {…}}class E extends C,D{ field e1; void m2() {…} void m4() {…} void m5() {…}}

c1c2

DVPtr

Pointer to E

Pointer to C inside EDVPtr

m2_C_E

m1_C_C

E-Object layout

Dispatchvector

d1e1

m4_D_E

m3_D_D

m5_E_EPointer to D inside E

Page 106: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

106

Runtime checks

• generate code for checking attempted illegal operations– Null pointer check– Array bounds check– Array allocation size check– Division by zero– …

• If check fails jump to error handler code that prints a message and gracefully exists program

Page 107: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

107

Null pointer check

# null pointer check cmp $0,%eax je labelNPE

labelNPE: push $strNPE # error message call __println push $1 # error code call __exit

Single generated handler for entire program

Page 108: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

108

Array bounds check

# array bounds check mov -4(%eax),%ebx # ebx = length# ecx holds index cmp %ecx,%ebx jle labelABE # ebx <= ecx ? cmp $0,%ecx jl labelABE # ecx < 0 ?

labelABE: push $strABE # error message call __println push $1 # error code call __exit

Single generated handler for entire program

Page 109: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

109

Array allocation size check

# array size check cmp $0,%eax # eax == array size jle labelASE # eax <= 0 ?

labelASE: push $strASE # error message call __println push $1 # error code call __exit

Single generated handler for entire program

Page 110: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

110

Recap• Lexical analysis

– regular expressions identify tokens (“words”)

• Syntax analysis– context-free grammars identify the structure of the program

(“sentences”)

• Contextual (semantic) analysis– type checking defined via typing judgements– can be encoded via attribute grammars– Syntax directed translation

• Intermediate representation – many possible IRs; generation of intermediate representation;

3AC; backpatching

• Runtime: – services that are always there: function calls, memory

management, threads, etc.

Page 111: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

111

Journey inside a compiler

LexicalAnalysi

s

Syntax Analysi

s

Sem.Analysi

s

Inter.Rep.

Code Gen.

float position;

float initial;

float rate;

position = initial + rate * 60

<float> <ID,position> <;> <float> <ID,initial> <;> <float> <ID,rate> <;> <ID,1> <=> <ID,2> <+> <ID,3> <*> <60>

TokenStream

Page 112: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

112

Journey inside a compiler

LexicalAnalysi

s

Syntax Analysi

s

Sem.Analysi

s

Inter.Rep.

Code Gen.

<ID,1> <=> <ID,2> <+> <ID,3> <*> <60>

60

<id,1>

=

<id,3>

<id,2>

+

*

AST

id symbol type data

1 position float …

2 initial float …

3 rate float …

symbol table

S ID = EE ID | E + E | E * E | NUM

Page 113: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

113

Journey inside a compiler

LexicalAnalysi

s

Syntax Analysi

s

Sem.Analysi

s

Inter.Rep.

Code Gen.

60

=

<id,3>

<id,2>

+

*

<id,1>

inttofloat

60

<id,1>

=

<id,3>

<id,2>

+

*

AST AST

coercion: automatic conversion from int to floatinserted by the compiler

id symbol type

1 position float

2 initial float

3 rate float

symbol table

Page 114: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

114

Journey inside a compiler

LexicalAnalysi

s

Syntax Analysi

s

Sem.Analysi

s

Inter.Rep.

Code Gen.

t1 = inttofloat(60)t2 = id3 * t1t3 = id2 + t2id1 = t3

3AC

60

=

<id,3>

<id,2>

+

*

<id,1>

inttofloat

production semantic rule

S id = E S.code := E. code || gen(id.var ‘:=‘ E.var)

E E1 op E2 E.var := freshVar(); E.code = E1.code || E2.code || gen(E.var ‘:=‘ E1.var ‘op’ E2.var)

E inttofloat(num) E.var := freshVar(); E.code = gen(E.var ‘:=‘ inttofloat(num))

E id E.var := id.var; E.code = ‘’

t1 = inttofloat(60)t2 = id3 * t1

t3 = id2 * t2id1 = t3

(for brevity, bubbles show only code generated by the node and not all accumulated “code” attribute)

note the structure:translate E1translate E2

handle operator

Page 115: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

115

Journey inside a compiler

Inter.Rep.

Code Gen.

LexicalAnalysi

s

Syntax Analysi

s

Sem.Analysi

s

3AC Optimized

t1 = inttofloat(60)t2 = id3 * t1t3 = id2 + t2id1 = t3

t1 = id3 * 60.0id1 = id2 + t1

value known at compile timecan generate code with converted value

eliminated temporary t3

Page 116: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

116

Journey inside a compiler

Inter.Rep.

Code Gen.

LexicalAnalysi

s

Syntax Analysi

s

Sem.Analysi

s

Optimized

t1 = id3 * 60.0id1 = id2 + t1

Code Gen

LDF R2, id3MULF R2, R2, #60.0LDF R1, id2ADDF R1,R1,R2STF id1,R1

Page 117: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

117

Problem 3.8 from [Appel]

A simple left-recursive grammar: E E + id E id

A simple right-recursive grammar accepting the same language:

E id + E E id

Which has better behavior for shift-reduce parsing?

Page 118: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

118

Answer

The stack never has more than three items on it. In general, withLR-parsing of left-recursive grammars, an input string of length O(n)requires only O(1) space on the stack.

E E + idE id

Input

id+id+id+id+id

id (reduce) E E + E + id (reduce) E E + E + id (reduce) E E + E + id (reduce) E E + E + id (reduce) E

stack

left recursive

Page 119: Theory of Compilation 236360 Erez Petrank Lecture 8: Runtime. 1

119

Answer

The stack grows as large as the input string. In general, with LR-parsingof right-recursive grammars, an input string of length O(n) requires O(n) space on the stack.

E id + EE id

Input

id+id+id+id+id

id id + id + id id + id + id + id + id id + id + id id + id + id + id id + id + id + id + id + id + id + id + id (reduce) id + id + id + id + E (reduce) id + id + id + E (reduce) id + id + E (reduce) id + E (reduce) E

stack

right recursive