Runtime Environments, Part II

Similar documents
Runtime Environment. Part II May 8th Wednesday, May 8, 13

PA5: Last Hints on Symbol Table: Other Info to Keep

Introducing C++ David Chisnall. March 15, 2011

Announcements. My office hours are today in Gates 160 from 1PM-3PM. Programming Project 3 checkpoint due tomorrow night at 11:59PM.

Three-Address Code IR

Week 7. Statically-typed OO languages: C++ Closer look at subtyping

Inheritance, Polymorphism and the Object Memory Model

CSE P 501 Compilers. Java Implementation JVMs, JITs &c Hal Perkins Winter /11/ Hal Perkins & UW CSE V-1

Agenda. CSE P 501 Compilers. Java Implementation Overview. JVM Architecture. JVM Runtime Data Areas (1) JVM Data Types. CSE P 501 Su04 T-1

IR Generation. May 13, Monday, May 13, 13

C++ Programming: Polymorphism

Supporting Class / C++ Lecture Notes

PROFESSOR: Last time, we took a look at an explicit control evaluator for Lisp, and that bridged the gap between

Overview. Constructors and destructors Virtual functions Single inheritance Multiple inheritance RTTI Templates Exceptions Operator Overloading

Chapter 10 :: Data Abstraction and Object Orientation

Dynamic Dispatch and Duck Typing. L25: Modern Compiler Design

COP 3330 Final Exam Review

CSE 401/M501 Compilers

CSE 504: Compiler Design. Intermediate Representations Symbol Table

CS 360 Programming Languages Interpreters

C++ Important Questions with Answers

Derived and abstract data types. TDT4205 Lecture 15

Java and C I. CSE 351 Spring Instructor: Ruth Anderson

Outline. Java Models for variables Types and type checking, type safety Interpretation vs. compilation. Reasoning about code. CSCI 2600 Spring

static CS106L Spring 2009 Handout #21 May 12, 2009 Introduction

Programming Languages and Techniques (CIS120)

CS558 Programming Languages Winter 2013 Lecture 8

Java and C CSE 351 Spring

Super-Classes and sub-classes

Project Compiler. CS031 TA Help Session November 28, 2011

MITOCW watch?v=flgjisf3l78

Announcements. Written Assignment 2 Due Monday at 5:00PM. Midterm next Wednesday in class, 11:00 1:00. Midterm review session next Monday in class.

Announcements. Written Assignment 2 due today at 5:00PM. Programming Project 2 due Friday at 11:59PM. Please contact us with questions!

Final CSE 131B Spring 2004

What about Object-Oriented Languages?

Contents. I. Classes, Superclasses, and Subclasses. Topic 04 - Inheritance

Chapter 9 :: Data Abstraction and Object Orientation

Short Notes of CS201

Types II. Hwansoo Han

CSC 467 Lecture 13-14: Semantic Analysis

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

Procedure and Object- Oriented Abstraction

Overview. Constructors and destructors Virtual functions Single inheritance Multiple inheritance RTTI Templates Exceptions Operator Overloading

C++ for Java Programmers

CS201 - Introduction to Programming Glossary By

Exceptions and Continuations. Lecture #19: More Special Effects Exceptions and OOP. Approach II: Non-Standard Return. Approach I: Do Nothing

QUIZ. Write the following for the class Bar: Default constructor Constructor Copy-constructor Overloaded assignment oper. Is a destructor needed?

Where We Are. Lexical Analysis. Syntax Analysis. IR Generation. IR Optimization. Code Generation. Machine Code. Optimization.

COMP322 - Introduction to C++ Lecture 09 - Inheritance continued

Late Binding; OOP as a Racket Pattern

Brief Summary of Java

Lecture 7: Type Systems and Symbol Tables. CS 540 George Mason University

Semantic Analysis and Type Checking

CS 330 Lecture 18. Symbol table. C scope rules. Declarations. Chapter 5 Louden Outline

CS260 Intro to Java & Android 03.Java Language Basics

Object Model. Object Oriented Programming Spring 2015

C++ Yanyan SHEN. slide 1

Inheritance in java is a mechanism in which one object acquires all the properties and behaviors of parent object.

Introduction to Computer Science Midterm 3 Fall, Points

CS153: Compilers Lecture 11: Compiling Objects

These new operators are intended to remove some of the holes in the C type system introduced by the old C-style casts.

UNIVERSITY OF CALIFORNIA Department of Electrical Engineering and Computer Sciences Computer Science Division DO NOT. P. N.

Binghamton University. CS-140 Fall Dynamic Types

Assignment 1: grid. Due November 20, 11:59 PM Introduction

Lecturer: William W.Y. Hsu. Programming Languages

SEMANTIC ANALYSIS TYPES AND DECLARATIONS

COMP322 - Introduction to C++

OBJECT ORIENTED PROGRAMMING USING C++ CSCI Object Oriented Analysis and Design By Manali Torpe

+ Abstract Data Types

CMSC 132: Object-Oriented Programming II

So far, system calls have had easy syntax. Integer, character string, and structure arguments.

CS152: Programming Languages. Lecture 23 Advanced Concepts in Object-Oriented Programming. Dan Grossman Spring 2011

Today. CSE341: Programming Languages. Late Binding in Ruby Multiple Inheritance, Interfaces, Mixins. Ruby instance variables and methods

CSE 504: Compiler Design. Runtime Environments

Intermediate Code, Object Representation, Type-Based Optimization

COSC 2P95. Procedural Abstraction. Week 3. Brock University. Brock University (Week 3) Procedural Abstraction 1 / 26

Data Abstraction. Hwansoo Han

CSE 504. Expression evaluation. Expression Evaluation, Runtime Environments. One possible semantics: Problem:

Inheritance and Substitution (Budd chapter 8, 10)

Subtyping (Dynamic Polymorphism)

Naming in OOLs and Storage Layout Comp 412

Run-time Environments

Run-time Environments

What is Polymorphism? Quotes from Deitel & Deitel s. Why polymorphism? How? How? Polymorphism Part 1

1 Shyam sir JAVA Notes

Outline for Today. Euler Tour Trees Revisited. The Key Idea. Dynamic Graphs. Implementation Details. Dynamic connectivity in forests.

G Programming Languages - Fall 2012

Balanced Trees Part One


OOPs Concepts. 1. Data Hiding 2. Encapsulation 3. Abstraction 4. Is-A Relationship 5. Method Signature 6. Polymorphism 7. Constructors 8.

CS558 Programming Languages

Homework I - Solution

Chapter 8 :: Composite Types

G Programming Languages Spring 2010 Lecture 9. Robert Grimm, New York University

CS558 Programming Languages

CPS 506 Comparative Programming Languages. Programming Language

Polymorphism Part 1 1

Object typing and subtypes

Chapter 1: Object-Oriented Programming Using C++

type conversion polymorphism (intro only) Class class

Transcription:

Runtime Environments, Part II

Announcements Programming Project 3 checkpoint feedback emailed out; let me know ASAP if you didn't hear back from us. Programming Project 3 due Wednesday at 11:59PM. OH every day until then. Ask questions on Piazzza! Ask questions via email!

Where We Are Source Code Lexical Analysis Syntax Analysis Semantic Analysis IR Generation IR Optimization Code Generation Optimization Machine Code

Implementing Objects

Objects are Hard It is difficult to build an expressive and efficient object-oriented language. Certain concepts are difficult to implement efficiently: Dynamic dispatch (virtual functions) Interfaces Multiple Inheritance Dynamic type checking (i.e. instanceof) Interfaces are so tricky to get right we won't ask you to implement them in PP4.

Encoding C-Style structs A struct is a type containing a collection of named values. Most common approach: lay each field out in the order it's declared.

Encoding C-Style structs A struct is a type containing a collection of named values. Most common approach: lay each field out in the order it's declared. struct MyStruct { int myint; char mychar; double mydouble; ;

Encoding C-Style structs A struct is a type containing a collection of named values. Most common approach: lay each field out in the order it's declared. struct MyStruct { int myint; char mychar; double mydouble; ; 4 Bytes 1 8 Bytes

Encoding C-Style structs A struct is a type containing a collection of named values. Most common approach: lay each field out in the order it's declared. struct MyStruct { int myint; char mychar; double mydouble; ; 4 Bytes 1 3 Bytes 8 Bytes

Accessing Fields Once an object is laid out in memory, it's just a series of bytes. How do we know where to look to find a particular field? 4 Bytes 1 3 Bytes 8 Bytes Idea: Keep an internal table inside the compiler containing the offsets of each field. To look up a field, start at the base address of the object and advance forward by the appropriate offset.

Accessing Fields Once an object is laid out in memory, it's just a series of bytes. How do we know where to look to find a particular field? 4 Bytes 1 3 Bytes 8 Bytes Idea: Keep an internal table inside the compiler containing the offsets of each field. To look up a field, start at the base address of the object and advance forward by the appropriate offset.

Field Lookup struct MyStruct { int x; char y; double z; ; 4 Bytes 1 3 Bytes 8 Bytes

Field Lookup struct MyStruct { int x; char y; double z; ; 4 Bytes 1 3 Bytes 8 Bytes MyStruct* ms = new MyStruct; ms->x = 137; ms->y = 'A'; ms->z = 2.71

Field Lookup struct MyStruct { int x; char y; double z; ; 4 Bytes 1 3 Bytes 8 Bytes MyStruct* ms = new MyStruct; ms->x = 137; store 137 0 bytes after ms ms->y = 'A'; store 'A' 4 bytes after ms ms->z = 2.71 store 2.71 8 bytes after ms

OOP without Member Functions Consider the following Decaf code: class Base { int x; int y; class Derived extends Base { int z; What will Derived look like in memory?

Memory Layouts with Inheritance

Memory Layouts with Inheritance class Base { int x; int y; ;

Memory Layouts with Inheritance class Base { int x; int y; ; 4 Bytes 4 Bytes

Memory Layouts with Inheritance class Base { int x; int y; ; 4 Bytes 4 Bytes

Memory Layouts with Inheritance class Base { int x; int y; ; 4 Bytes 4 Bytes class Derived extends Base { int z; ;

Memory Layouts with Inheritance class Base { int x; int y; ; 4 Bytes 4 Bytes 4 Bytes 4 Bytes 4 Bytes class Derived extends Base { int z; ;

Memory Layouts with Inheritance class Base { int x; int y; ; 4 Bytes 4 Bytes 4 Bytes 4 Bytes 4 Bytes class Derived extends Base { int z; ;

Field Lookup With Inheritance

Field Lookup With Inheritance 4 Bytes 4 Bytes class Base { int x; int y; ; class Derived extends Base { int z; ; 4 Bytes 4 Bytes 4 Bytes

Field Lookup With Inheritance 4 Bytes 4 Bytes class Base { int x; int y; ; class Derived extends Base { int z; ; 4 Bytes 4 Bytes 4 Bytes Base ms = new Base; ms.x = 137; ms.y = 42;

Field Lookup With Inheritance 4 Bytes 4 Bytes class Base { int x; int y; ; class Derived extends Base { int z; ; 4 Bytes 4 Bytes 4 Bytes Base ms = new Base; ms.x = 137; store 137 0 bytes after ms ms.y = 42; store 42 4 bytes after ms

Field Lookup With Inheritance 4 Bytes 4 Bytes class Base { int x; int y; ; class Derived extends Base { int z; ; 4 Bytes 4 Bytes 4 Bytes Base ms = new Derived; ms.x = 137; ms.y = 42;

Field Lookup With Inheritance 4 Bytes 4 Bytes class Base { int x; int y; ; class Derived extends Base { int z; ; 4 Bytes 4 Bytes 4 Bytes Base ms = new Derived; ms.x = 137; ms.y = 42;

Field Lookup With Inheritance 4 Bytes 4 Bytes class Base { int x; int y; ; class Derived extends Base { int z; ; 4 Bytes 4 Bytes 4 Bytes Base ms = new Derived; ms.x = 137; store 137 0 bytes after ms ms.y = 42; store 42 4 bytes after ms

Field Lookup With Inheritance 4 Bytes 4 Bytes class Base { int x; int y; ; class Derived extends Base { int z; ; 4 Bytes 4 Bytes 4 Bytes Base ms = new Base; ms.x = 137; store 137 0 bytes after ms ms.y = 42; store 42 4 bytes after ms Base ms = new Derived; ms.x = 137; store 137 0 bytes after ms ms.y = 42; store 42 4 bytes after ms

Single Inheritance in Decaf The memory layout for a class D that extends B is given by the memory layout for B followed by the memory layout for the members of D. Actually a bit more complex; we'll see why later. Rationale: A pointer of type B pointing at a D object still sees the B object at the beginning. Operations done on a D object through the B reference guaranteed to be safe; no need to check what B points at dynamically.

What About Member Functions? Member functions are mostly like regular functions, but with two complications: How do we know what the receiver object is and how to access it? How do we know which function to call at runtime (dynamic dispatch)?

this is Tricky Inside a member function, the name this refers to the current receiver object. This information (pun intended) needs to be communicated into the function. Idea: Treat this as an implicit first parameter. Every n-argument member function is really an (n+1)-argument member function whose first parameter is the this pointer.

this is Clever class MyClass { int x; void myfunction(int arg) { this.x = arg; MyClass m = new MyClass; m.myfunction(137);

this is Clever class MyClass { int x; void myfunction(int arg) { this.x = arg; MyClass m = new MyClass; m.myfunction(137);

this is Clever class MyClass { int x; void MyClass_myFunction(MyClass this, int arg){ this.x = arg; MyClass m = new MyClass; m.myfunction(137);

this is Clever class MyClass { int x; void MyClass_myFunction(MyClass this, int arg){ this.x = arg; MyClass m = new MyClass; m.myfunction(137);

this is Clever class MyClass { int x; void MyClass_myFunction(MyClass this, int arg){ this.x = arg; MyClass m = new MyClass; MyClass_myFunction(m, 137);

this rules When generating code to call a member function, remember to pass some object as the this parameter representing the receiver object. Inside of a member function, treat this as just another parameter to the member function. When implicitly referring to a field of this, use this extra parameter as the object in which the field should be looked up.

Implementing Dynamic Dispatch Dynamic dispatch means determining which function to call at runtime based on the dynamic type of the object a method is invoked on. How do we set up our runtime environment so that we can efficiently support this?

An Initial Idea At compile-time, get a list of every defined class. To compile a dynamic dispatch, emit IR code for the following logic: s if (the object has type A) call A's version of the function else if (the object has type B) call B's version of the function else if (the object has type N) call N's version of the function.

This is a Bad Idea This previous idea has several serious problems. What are they? It's slow. Number of checks is O(C), where C is the number of classes the dispatch might refer to. Gets slower the more classes there are. It's infeasible in most languages. What if we link across multiple source files? What if we support dynamic class loading?

An Observation When laying out fields in an object, we gave every field an offset. Derived classes have the base class fields in the same order at the beginning. Layout of Base Base.x Base.y Layout of Derived Base.x Base.y Derived.z Can we do something similar with functions?

Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived");

Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); Base.sayHi Derived.sayHi

Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); Base.x Base.sayHi Derived.sayHi

Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); Base.x Base.x Derived.y Base.sayHi Derived.sayHi

Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); sayhi sayhi Base.x Base.x Derived.y Base.sayHi Derived.sayHi

Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); sayhi sayhi Base.x Base.x Derived.y Base.sayHi Derived.sayHi

Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); sayhi sayhi Base.x Base.x Derived.y Base.sayHi Base b = new Base; b.sayhi(); Derived.sayHi

Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); sayhi sayhi Base.x Base.x Derived.y Base.sayHi Derived.sayHi Base b = new Base; b.sayhi(); Let fn = the pointer 0 bytes after b Call fn(b)

Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); sayhi sayhi Base.x Base.x Derived.y Base.sayHi Base b = new Derived; b.sayhi(); Derived.sayHi

Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); sayhi sayhi Base.x Base.x Derived.y Base.sayHi Base b = new Derived; b.sayhi(); Derived.sayHi

Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); sayhi sayhi Base.x Base.x Derived.y Base.sayHi Derived.sayHi Base b = new Derived; b.sayhi(); Let fn = the pointer 0 bytes after b Call fn(b)

More Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); Base clone() { Derived clone() { return new Base; return new Derived;

More Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); Base clone() { Derived clone() { return new Base; return new Derived; Base.sayHi Base.clone Derived.sayHi Derived.clone

More Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); Base clone() { Derived clone() { return new Base; return new Derived; Base.sayHi Base.clone sayhi clone Base.x Derived.sayHi Derived.clone

More Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); Base clone() { Derived clone() { return new Base; return new Derived; Base.sayHi Base.clone sayhi clone Base.x Derived.sayHi Derived.clone

More Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); Base clone() { Derived clone() { return new Base; return new Derived; Base.sayHi Base.clone sayhi clone Base.x Derived.sayHi Derived.clone sayhi clone Base.x Derived.y

More Virtual Function Tables class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); Base clone() { Derived clone() { return new Base; return new Derived; Base.sayHi Base.clone sayhi clone Base.x Derived.sayHi Derived.clone sayhi clone Base.x Derived.y

Virtual Function Tables A virtual function table (or vtable) is an array of pointers to the member function implementations for a particular class. To invoke a member function: Determine (statically) its index in the vtable. Follow the pointer at that index in the object's vtable to the code for the function. Invoke that function.

This is a Pretty Good Idea Advantages: Time to determine function to call is O(1). (and a good O(1) too!) What are the disadvantages? Object creation is slower. Each new object needs to have O(M) pointers set, where M is the number of member functions. Object sizes are larger. Each object needs to have space for O(M) pointers.

This is a Pretty Good Idea Advantages: Time to determine function to call is O(1). (and a good O(1) too!) What are the disadvantages? Object creation is slower. Each new object needs to have O(M) pointers set, where M is the number of member functions. Object sizes are larger. Each object needs to have space for O(M) pointers.

A Common Optimization class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); Base clone() { Derived clone() { return new Base; return new Derived; Base.sayHi Base.clone sayhi clone Base.x Derived.sayHi Derived.clone sayhi clone Base.x Derived.y

A Common Optimization class Base { class Derived extends Base { int x; int y; void sayhi() { void sayhi() { Print("Base"); Print("Derived"); Base clone() { Derived clone() { return new Base; return new Derived; Base.sayHi Base.clone sayhi clone Vtable* Base.x Derived.sayHi Derived.clone sayhi clone Vtable* Base.x Derived.y

Objects in Memory Base.sayHi Base.clone Derived.sayHi Derived.clone sayhi clone sayhi clone Vtable* Base.x Vtable* Base.x Vtable* Base.x Derived.y Vtable* Base.x Derived.y

Dynamic Dispatch in O(1) Create a single instance of the vtable for each class. Have each object store a pointer to the vtable. Can follow the pointer to the table in O(1). Can index into the table in O(1). Can set the vtable pointer of a new object in O(1). Increases the size of each object by O(1). This is the solution used in most C++ and Java implementations.

Vtable Requirements We've made implicit assumptions about our language that allow vtables to work correctly. What are they? Method calls known statically. We can determine at compile-time which methods are intended at each call (even if we're not sure which method is ultimately invoked). Single inheritance. Don't need to worry about building a single vtable for multiple different classes.

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived.";

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; >

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); >

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); >

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); > Hi! I'm Base.

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); > Hi! I'm Base.

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); $d = new Derived(); $d->sayhello(); > Hi! I'm Base.

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); $d = new Derived(); $d->sayhello(); > Hi! I'm Base.

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); $d = new Derived(); $d->sayhello(); > Hi! I'm Base. Hi! I'm Derived.

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); $d = new Derived(); $d->sayhello(); > Hi! I'm Base. Hi! I'm Derived.

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); $d = new Derived(); $d->sayhello(); $b->missingfunction(); > Hi! I'm Base. Hi! I'm Derived.

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); $d = new Derived(); $d->sayhello(); $b->missingfunction(); > Hi! I'm Base. Hi! I'm Derived.

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); $d = new Derived(); $d->sayhello(); $b->missingfunction(); > Hi! I'm Base. Hi! I'm Derived. ERROR: Base::missingFunction is not defined

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); $d = new Derived(); $d->sayhello(); $b->missingfunction(); > Hi! I'm Base. Hi! I'm Derived. ERROR: Base::missingFunction is not defined

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); $d = new Derived(); $d->sayhello(); $b->missingfunction(); $fnname = "sayhello"; $b->$fnname(); > Hi! I'm Base. Hi! I'm Derived. ERROR: Base::missingFunction is not defined

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); $d = new Derived(); $d->sayhello(); $b->missingfunction(); $fnname = "sayhello"; $b->$fnname(); > Hi! I'm Base. Hi! I'm Derived. ERROR: Base::missingFunction is not defined

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); $d = new Derived(); $d->sayhello(); $b->missingfunction(); $fnname = "sayhello"; $b->$fnname(); > Hi! I'm Base. Hi! I'm Derived. ERROR: Base::missingFunction is not defined Hi! I'm Base.

Inheritance in PHP class Base { public function sayhello() { echo "Hi! I'm Base."; class Derived extends Base { public function sayhello() { echo "Hi! I'm Derived."; $b = new Base(); $b->sayhello(); $d = new Derived(); $d->sayhello(); $b->missingfunction(); $fnname = "sayhello"; $b->$fnname(); > Hi! I'm Base. Hi! I'm Derived. ERROR: Base::missingFunction is not defined Hi! I'm Base.

Why don't vtables work in PHP? Call-by-string bypasses the vtable optimization. Impossible to statically determine contents of any string. Would have to determine index into vtable at runtime. No static type information on objects. Impossible to statically determine whether a given method exists at all. Plus a few others: eval keyword executes arbitrary PHP code; could introduce new classes or methods.

Inheritance without Vtables class Base { class Derived extends Base { int x; int y; void sayhi() { Derived clone() { Print("Hi!"); return new Derived; Base clone() { return new Base;

Base.sayHi Base.clone Inheritance without Vtables class Base { class Derived extends Base { int x; int y; void sayhi() { Derived clone() { Print("Hi!"); return new Derived; Base clone() { return new Base; Derived.clone

Inheritance without Vtables class Base { class Derived extends Base { int x; int y; void sayhi() { Derived clone() { Print("Hi!"); return new Derived; Base clone() { return new Base; Base.sayHi Base.clone Vtable* Base.x Derived.clone

Inheritance without Vtables class Base { class Derived extends Base { int x; int y; void sayhi() { Derived clone() { Print("Hi!"); return new Derived; Base clone() { return new Base; Base.sayHi Base.clone sayhi clone Vtable* Base.x Derived.clone

Inheritance without Vtables class Base { class Derived extends Base { int x; int y; void sayhi() { Derived clone() { Print("Hi!"); return new Derived; Base clone() { return new Base; Base.sayHi Base.clone sayhi clone Vtable* Base.x Derived.clone

Inheritance without Vtables class Base { class Derived extends Base { int x; int y; void sayhi() { Derived clone() { Print("Hi!"); return new Derived; Base clone() { return new Base; Base.sayHi Base.clone sayhi clone Vtable* Base.x Derived.clone Vtable* Base.x Derived.y

Inheritance without Vtables class Base { class Derived extends Base { int x; int y; void sayhi() { Derived clone() { Print("Hi!"); return new Derived; Base clone() { return new Base; Base.sayHi Base.clone sayhi clone Vtable* Base.x Derived.clone sayhi clone Vtable* Base.x Derived.y

Inheritance without Vtables class Base { class Derived extends Base { int x; int y; void sayhi() { Derived clone() { Print("Hi!"); return new Derived; Base clone() { return new Base; Base.sayHi Base.clone sayhi clone Vtable* Base.x Derived.clone sayhi clone Vtable* Base.x Derived.y

Inheritance without Vtables class Base { class Derived extends Base { int x; int y; void sayhi() { Derived clone() { Print("Hi!"); return new Derived; Base clone() { return new Base; Base.sayHi Base.clone "sayhi" "clone" Class Info Method Table Info* Base.x Derived.clone "sayhi" "clone" Class Info Method Table Info* Base.x Derived.y

Inheritance without Vtables class Base { class Derived extends Base { int x; int y; void sayhi() { Derived clone() { Print("Hi!"); return new Derived; Base clone() { return new Base; Base.sayHi Base.clone "sayhi" "clone" Class Info Method Table Info* Base.x Derived.clone "clone" Class Info Method Table Info* Base.x Derived.y

Inheritance without Vtables class Base { class Derived extends Base { int x; int y; void sayhi() { Derived clone() { Print("Hi!"); return new Derived; Base clone() { return new Base; Base.sayHi Base.clone "sayhi" "clone" Class Info Method Table Info* Base.x Derived.clone "clone" Class Info Method Table Parent Class Info* Base.x Derived.y

A General Inheritance Framework Each object stores a pointer to a descriptor for its class. Each class descriptor stores A pointer to the base class descriptor(s). A pointer to a method lookup table. To invoke a method: Follow the pointer to the method table. If the method exists, call it. Otherwise, navigate to the base class and repeat. This is slow but can be optimized in many cases; we'll see this later.

Vtables and Interfaces interface Engine { void vroom(); interface Visible { void draw(); class PaintedEngine implements Engine, Visible { void vroom() { /* */ void draw() { /* */ class JetEngine implements Engine { void vroom() { /* */ class Paint implements Visible { void draw() { /* */ Engine e1 = new PaintedEngine; Engine e2 = new JetEngine; e1.vroom(); e2.vroom(); Visible v1 = new PaintedEngine; Visibie v2 = new Paint; v1.draw(); v2.draw();

Vtables and Interfaces interface Engine { void vroom(); interface Visible { void draw(); class PaintedEngine implements Engine, Visible { void vroom() { /* */ void draw() { /* */ class JetEngine implements Engine { void vroom() { /* */ class Paint implements Visible { void draw() { /* */ PaintedEngine vtable vroom draw Engine e1 = new PaintedEngine; Engine e2 = new JetEngine; e1.vroom(); e2.vroom(); Visible v1 = new PaintedEngine; Visibie v2 = new Paint; v1.draw(); v2.draw();

Vtables and Interfaces interface Engine { void vroom(); interface Visible { void draw(); class PaintedEngine implements Engine, Visible { void vroom() { /* */ void draw() { /* */ class JetEngine implements Engine { void vroom() { /* */ class Paint implements Visible { void draw() { /* */ Engine e1 = new PaintedEngine; Engine e2 = new JetEngine; e1.vroom(); e2.vroom(); Visible v1 = new PaintedEngine; Visibie v2 = new Paint; v1.draw(); v2.draw(); PaintedEngine vtable vroom draw JetEngine vtable vroom

Vtables and Interfaces interface Engine { void vroom(); interface Visible { void draw(); class PaintedEngine implements Engine, Visible { void vroom() { /* */ void draw() { /* */ class JetEngine implements Engine { void vroom() { /* */ class Paint implements Visible { void draw() { /* */ Engine e1 = new PaintedEngine; Engine e2 = new JetEngine; e1.vroom(); e2.vroom(); Visible v1 = new PaintedEngine; Visibie v2 = new Paint; v1.draw(); v2.draw(); PaintedEngine vtable vroom vroom draw JetEngine vtable (empty) Paint vtable draw

Interfaces with Vtables Interfaces complicate vtable layouts because they require interface methods to have consistent positions across all vtables. This can fill vtables with useless entries. For this reason, interfaces are typically not implemented using pure vtables. We'll see two approaches for implementing interfaces efficiently.

Interfaces via String Lookup Idea: A hybrid approach. Use vtables for standard (non-interface) dispatch. Use the more general, string-based lookup for interfaces.

Object Layout with Interfaces class Charlie implements Sheen { interface Sheen { int awesome; void win(); void win() { Print("WINNING!"); void beameme() { Print("AWWWW YEAH");

Object Layout with Interfaces class Charlie implements Sheen { interface Sheen { int awesome; void win(); void win() { Print("WINNING!"); void beameme() { Print("AWWWW YEAH"); Charlie.win Charlie.beAMeme win beameme Vtable* awesome

Object Layout with Interfaces class Charlie implements Sheen { interface Sheen { int awesome; void win(); void win() { Print("WINNING!"); void beameme() { Print("AWWWW YEAH"); Charlie.win Charlie.beAMeme NameTable win beameme Vtable* awesome

Object Layout with Interfaces class Charlie implements Sheen { interface Sheen { int awesome; void win(); void win() { Print("WINNING!"); void beameme() { Print("AWWWW YEAH"); "win" Charlie.win Charlie.beAMeme NameTable win beameme Vtable* awesome

Object Layout with Interfaces class Charlie implements Sheen { interface Sheen { int awesome; void win(); void win() { Print("WINNING!"); void beameme() { Print("AWWWW YEAH"); "win" Charlie.win Charlie.beAMeme NameTable win beameme Vtable* awesome

Analysis of the Approach Dynamic dispatch through object types still O(1). Interface dispatches take O(Mn), where M is the number of methods and n is the length of the method name. Can easily speed up to O(n) expected by replacing a list of strings with a hash table.

Optimizing Interface Dispatch Assign a unique number to each interface method. Replace hash table of strings with hash table of integers. Vtable effectively now a hash table instead of an array. Cost to do an interface dispatch now O(1). (But still more expensive than a standard dynamic dispatch.) Would this work in PHP? No; can still do string-based lookups directly.

Optimizing Interface Dispatch Assign a unique number to each interface method. Replace hash table of strings with hash table of integers. Vtable effectively now a hash table instead of an array. Cost to do an interface dispatch now O(1). (But still more expensive than a standard dynamic dispatch.) Would this work in PHP? No; can still do string-based lookups directly.

Optimizing Even Further Vtable lookups are (comparatively) fast compared to hash table lookups. Can often do vtable lookup in two instructions! Hashing strings is (comparatively) very slow; hashing integers is (comparatively) slow. Can we eliminate the hash lookups in some cases?

An Observation interface Meme { void run(); class Nyan implements Meme { class Troll implements Meme { void run() { void run() { Print("Nyan!"); Print("Problem?"); Meme n = new Nyan; for (i = 0; i < 100; ++i) n.run(); Meme t = new Troll; for (i = 0; i < 100; ++i) t.run();

An Observation interface Meme { void run(); class Nyan implements Meme { class Troll implements Meme { void run() { void run() { Print("Nyan!"); Print("Problem?"); Meme n = new Nyan; for (i = 0; i < 100; ++i) n.run(); Meme t = new Troll; for (i = 0; i < 100; ++i) t.run(); Nyan.run Troll.run

An Observation interface Meme { void run(); class Nyan implements Meme { class Troll implements Meme { void run() { void run() { Print("Nyan!"); Print("Problem?"); Meme n = new Nyan; for (i = 0; i < 100; ++i) n.run(); Meme t = new Troll; for (i = 0; i < 100; ++i) t.run(); Nyan.run Troll.run

Inline Caching A particular interface dispatch site often refers to the same method on the majority of its calls. Idea: Have each call site cache the type of the first object the call is made on, along with the address of the method that's resolved. When doing an interface dispatch, check whether the type of the receiver matches the cached type. If so, just use the known method. If not, fall back to the standard string-based dispatch. This is called inline caching.

Tradeoffs in Inline Caching For monomorphic call sites (only one object type actually getting used), inline caching can be a huge performance win. For polymorphic call sites (multiple object types getting used), inline caching can slow down the program. (Why?) A more advanced technique called polymorphic inline caching tries to balance the two by maintaining a small collection of known types. This optimization is used by many JITs for object-oriented languages, including Java.

Summary of String-Based Lookup Preserves the runtime speed of dispatch through objects. Allows flexible dispatch through interfaces. Can be optimized if method calls can be resolved statically. Can be optimized further using variants of inline caching.

Vtables Revisited Recall: Why do interfaces complicate vtable layouts? Answer: Interface methods must have a consistent position in all vtables. Idea: What if we have multiple vtables per object, one for each interface? Allows interface methods to be positioned independently of one another. Allows for fast vtable lookups relative to string-based approach. This is much harder than it seems, but it's what's used in C++.

Interfaces and Vtables interface Meme { void run(); interface Cat { void meow(); class Nyan implements Meme, Cat { void run() { Print("Nyan!"); void meow() { Print("Nyan!"); void eat() { Print("Yummy Pop Tart!");

Interfaces and Vtables interface Meme { void run(); interface Cat { void meow(); class Nyan implements Meme, Cat { void run() { Print("Nyan!"); void meow() { Print("Nyan!"); void eat() { Print("Yummy Pop Tart!"); Vtable* eat run meow Nyan.eat Nyan.run Nyan.meow

Interfaces and Vtables interface Meme { void run(); interface Cat { void meow(); class Nyan implements Meme, Cat { void run() { Print("Nyan!"); void meow() { Print("Nyan!"); void eat() { Print("Yummy Pop Tart!"); Vtable* eat run meow run Nyan.eat Nyan.run Nyan.meow

Interfaces and Vtables interface Meme { void run(); interface Cat { void meow(); class Nyan implements Meme, Cat { void run() { Print("Nyan!"); void meow() { Print("Nyan!"); void eat() { Print("Yummy Pop Tart!"); Vtable* eat run meow run meow Nyan.eat Nyan.run Nyan.meow

Interfaces and Vtables interface Meme { void run(); interface Cat { void meow(); class Nyan implements Meme, Cat { void run() { Print("Nyan!"); void meow() { Print("Nyan!"); void eat() { Print("Yummy Pop Tart!"); Nyan Vtable Meme Vtable Cat Vtable eat run meow run meow Nyan.eat Nyan.run Nyan.meow

Interfaces and Vtables interface Meme { void run(); interface Cat { void meow(); class Nyan implements Meme, Cat { void run() { Print("Nyan!"); void meow() { Print("Nyan!"); void eat() { Print("Yummy Pop Tart!"); Nyan Vtable Meme Vtable Cat Vtable eat run meow run meow Nyan.eat Nyan.run Nyan.meow

The Problem class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow();

The Problem class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow();

The Problem class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow(); Nyan Vtable Meme Vtable Cat Vtable

The Problem class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow(); Nyan Vtable Meme Vtable Cat Vtable

The Problem class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow(); Nyan Vtable Meme Vtable Cat Vtable Mewtwo Vtable Cat Vtable

The Problem class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow(); Nyan Vtable Meme Vtable Cat Vtable Mewtwo Vtable Cat Vtable

The Problem class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow(); Nyan Vtable Meme Vtable Cat Vtable Mewtwo Vtable Cat Vtable

The Problem The offset from the base of the object to a particular interface vtable depends on the dynamic type of the object. We cannot generate IR code to do an interface dispatch without knowing where the vtable is. We don't seem to have gotten anywhere...

A Partial Solution class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow();

A Partial Solution class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow();

A Partial Solution class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow(); Nyan Vtable Meme Vtable Cat Vtable

A Partial Solution class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow(); Nyan Vtable Meme Vtable Cat Vtable

A Partial Solution class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow(); Nyan Vtable Meme Vtable Cat Vtable

A Partial Solution class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow(); Nyan Vtable Meme Vtable Cat Vtable Mewtwo Vtable Cat Vtable

A Partial Solution class Nyan implements Meme, Cat { /* */ class Mewtwo implements Cat { /* */ Cat c1 = new Nyan; Cat c2 = new Mewtwo; c1.meow(); c2.meow(); Nyan Vtable Meme Vtable Cat Vtable Mewtwo Vtable Cat Vtable

A Partial Solution When upcasting an object to an interface type, change where the pointer points so that it sees the vtable pointer for that interface. We can now assume an interface reference refers directly to the vtable. But there's a serious problem with this implementation...

Looking in the Wrong Place interface Cat { void meow(); class Garfield implements Cat { int totalsleep; void meow() { totalsleep --; Print("I'm tired."); Cat g = new Garfield; g.meow();

Looking in the Wrong Place interface Cat { void meow(); class Garfield implements Cat { int totalsleep; void meow() { totalsleep --; Print("I'm tired."); Cat g = new Garfield; g.meow();

Looking in the Wrong Place interface Cat { void meow(); class Garfield implements Cat { int totalsleep; void meow() { totalsleep --; Print("I'm tired."); Garfield Vtable Cat Vtable totalsleep Cat g = new Garfield; g.meow();

Looking in the Wrong Place interface Cat { void meow(); class Garfield implements Cat { int totalsleep; void meow() { totalsleep --; Print("I'm tired."); Garfield Vtable Cat Vtable totalsleep Cat g = new Garfield; g.meow();

Looking in the Wrong Place interface Cat { void meow(); class Garfield implements Cat { int totalsleep; void meow() { totalsleep --; Print("I'm tired."); Garfield Vtable Cat Vtable totalsleep Cat g = new Garfield; g.meow();

Looking in the Wrong Place interface Cat { void meow(); class Garfield implements Cat { int totalsleep; void meow() { totalsleep --; Print("I'm tired."); Garfield Vtable Cat Vtable totalsleep Cat g = new Garfield; g.meow(); Garfield::meow(Garfield* this) Look up the integer 8 bytes past 'this' Read its value into memory Subtract one from the value Store the value back into memory

Looking in the Wrong Place interface Cat { void meow(); class Garfield implements Cat { int totalsleep; void meow() { totalsleep --; Print("I'm tired."); Garfield Vtable Cat Vtable totalsleep Cat g = new Garfield; g.meow(); Garfield::meow(Garfield* this) Look up the integer 8 bytes past 'this' Read its value into memory Subtract one from the value Store the value back into memory

Looking in the Wrong Place Interface pointers cannot be used directly as the this pointer in methods calls. Pointing into the middle of an object, not the base of the object. All field offsets will refer to the wrong parts of memory. How can we correct this?

Adding in Deltas Garfield Vtable Cat Vtable totalsleep meow Garfield.meow Cat g = new Garfield; g.meow(); Garfield::meow(Garfield* this) Look up the integer 8 bytes past 'this' Read its value into memory Subtract one from the value Store the value back into memory

Adding in Deltas Garfield Vtable Cat Vtable totalsleep Delta: -4 meow Garfield.meow Cat g = new Garfield; g.meow(); Garfield::meow(Garfield* this) Look up the integer 8 bytes past 'this' Read its value into memory Subtract one from the value Store the value back into memory

Vtable Deltas Augment each interface vtable with the offset in bytes the pointer must be corrected to get back to the base of the object. A dynamic dispatch then looks like this: Look up the address of the function to call by following the vtable pointer and looking at the recovered address. Look up the amount to adjust the object pointer in the vtable. Update the object pointer by adding in the given delta. Call the function indicated in the vtable.

Analysis of Vtable Deltas Cost to invoke a method is O(1) regardless of the number of interfaces. Also a fast O(1); typically much better than a hash table lookup. Size of an object increases by O(I), where I is the number of interfaces. Cost to create an object is O(I), where I is the number of interfaces. (Why?)

Comparison of Approaches String-based lookups have small objects and fast object creation but slow dispatch times. Only need to set one vtable pointer in the generated object. Dispatches require some type of string comparisons. Vtable-based lookups have larger objects and slower object creation but faster dispatch times. Need to set multiple vtable pointers in the generated object. Dispatches can be done using simple arithmetic.

Implementing Dynamic Type Checks

Dynamic Type Checks Many languages require some sort of dynamic type checking. Java's instanceof, C++'s dynamic_cast, any dynamically-typed language. May want to determine whether the dynamic type is convertible to some other type, not whether the type is equal. How can we implement this?

class A { void f() { A Pretty Good Approach class B extends A { void f() { class C extends A { void f() { class D extends B { void f() { class E extends C { void f() {

A Pretty Good Approach class A { void f() { class B extends A { void f() { class C extends A { void f() { class D extends B { void f() { class E extends C { void f() { A.f B.f C.f D.f E.f

A Pretty Good Approach class A { void f() { class B extends A { void f() { class C extends A { void f() { class D extends B { void f() { class E extends C { void f() { Parent A.f Parent B.f Parent C.f Parent D.f Parent E.f

A Pretty Good Approach class A { void f() { class B extends A { void f() { class C extends A { void f() { class D extends B { void f() { class E extends C { void f() { Parent A.f Parent B.f Parent C.f Parent D.f Parent E.f

Simple Dynamic Type Checking Have each object's vtable store a pointer to its base class. To check if an object is convertible to type S at runtime, follow the pointers embedded in the object's vtable upward until we find S or reach a type with no parent. Runtime is O(d), where d is the depth of the class in the hierarchy. Can we make this faster?

A Marvelous Idea There is a fantastically clever way of checking convertibility at runtime in O(1) in restricted circumstances. Assume: There aren't too many classes derived from any one class (say, 10). A runtime check of whether an object that is statically of type A is dynamically of type B is only possible if A B. All types are known statically.

A Marvelous Idea Parent A.f Parent B.f Parent C.f Parent Parent Parent Parent D.f E.f F.f G.f

A Marvelous Idea Parent A.f Parent B.f Parent C.f Parent Parent Parent Parent D.f E.f F.f G.f

A Marvelous Idea 1 A.f 2 3 B.f C.f 2 * 5 2 * 7 3 * 11 D.f E.f F.f 3 * 13 G.f

A Marvelous Idea 1 A.f 2 3 B.f C.f 10 14 33 D.f E.f F.f 39 G.f

A Marvelous Idea 1 A.f 2 3 B.f C.f 10 14 33 D.f E.f F.f 39 G.f A myobject = /* */ if (myobject instanceof C) { /* */

A Marvelous Idea 1 A.f 2 3 B.f C.f 10 14 33 D.f E.f F.f 39 G.f A myobject = /* */ if (myobject instanceof C) { /* */

A Marvelous Idea 1 A.f 2 3 B.f C.f 10 14 33 D.f E.f F.f 39 G.f A myobject = /* */ if (myobject->vtable.key % 3 == 0) { /* */

Dynamic Typing through Primes Assign each class a unique prime number. (Can reuse primes across unrelated type hierarchies.) Set the key of that class to be the product of its prime and all the primes of its superclasses. To check at runtime if an object is convertible to type T: Look up the object's key. If T's key divides the object's key, the object is convertible to T. Otherwise, it is not. Assuming product of primes fits into an integer, can do this check in O(1). Also works with multiple inheritance; prototype C++ implementations using this techinique exist.

Next Time Three-Address Code IR. IR Generation.