cpsc 213
play

CPSC 213 Switch Statements, Understanding Pointers - 2nd ed: 3.6.7, - PowerPoint PPT Presentation

Readings for Next Two Lectures Text CPSC 213 Switch Statements, Understanding Pointers - 2nd ed: 3.6.7, 3.10 - 1st ed: 3.6.6, 3.11 Introduction to Computer Systems Unit 1f Dynamic Control Flow Polymorphism and Switch Statements 1 2


  1. Readings for Next Two Lectures ‣ Text CPSC 213 •Switch Statements, Understanding Pointers - 2nd ed: 3.6.7, 3.10 - 1st ed: 3.6.6, 3.11 Introduction to Computer Systems Unit 1f Dynamic Control Flow Polymorphism and Switch Statements 1 2 Back to Procedure Calls ‣ Static Method Invocations and Procedure Calls •target method/procedure address is known statically ‣ in Java public class A { • static methods are class methods static void ping () {} } - invoked by naming the class, not an object Polymorphism public class Foo { static void foo () { A.ping (); } } ‣ in C void ping () {} •specify procedure name void foo () { ping (); } 3 4

  2. Polymorphism Polymorphic Dispatch ‣ Invoking a method on an object in Java ‣ Method address is determined dynamically • variable that stores the object has a static type • compiler can not hardcode target address in procedure call • object reference is dynamic and so is its type • instead, compiler generates code to lookup procedure address at runtime - object’s type must implement the type of the referring variable • address is stored in memory in the object’s class jump table - but object’s type may override methods of this base type ‣ Class Jump table ‣ Polymorphic Dispatch • every class is represented by class object • target method address depends on the type of the referenced object • the class object stores the class’s jump table • one call site can invoke different methods at different times • the jump table stores the address of every method implemented by the class • objects store a pointer to their class object class A { static void foo (A a) { void ping () {} Which ping gets called? a.ping (); ‣ Static and dynamic of method invocation void pong () {} a.pong (); } } • address of jump table is determined dynamically static void bar () { • method’s offset into jump table is determined statically foo (new A()); class B extends A { foo (new B()); void ping () {} } void wiff () {} } 5 6 Example of Java Dispatch Dynamic Jumps in C class A { void ping () {} void pong () {} } ‣ Function pointer Class A A.ping () {} class B extends A { •a variable that stores a pointer to a procedure ping void ping () {} pong void wiff () {} •declared A.pong () {} } - <return-type> (*<variable-name>)(<formal-argument-list>); Class B static void foo (A a) { •used to make dynamic call B.ping () {} a.ping (); ping a.pong (); - <variable-name> (<actual-argument-list>); pong } ‣ Example wiff B.wiff () {} static void bar () { foo (new A()); foo (new B()); } void ping () {} void foo () { an A a B void (*aFunc) (); foo (a) r5 r5 a a aFunc = ping; r[0] ← m[r[5]] # r0 = a calls ping aFunc (); r[1] ← m[r[0]] # r1 = a.class pc ← m[r[1]+0*4] # a.ping () } pc ← m[r[1]+1*4] # a.pong () Runtime Stack 7 8

  3. Simplified Polymorphism in C (SA-dynamic-call.c) •and B ... Declaration of B’s jump table and code ‣ Use a struct to store jump table struct B { void (*ping)(); •drawing on previous example of A ... void (*pong)(); Declaration of A’s jump table and code void (*wiff)(); }; struct A { void (*ping) (); void B_ping () { printf ("B_ping\n"); } void (*pong) (); void B_wiff () { printf ("B_wiff\n"); } }; void A_ping () { printf ("A_ping\n"); } Create an instance of B’s jump table void A_pong () { printf ("A_pong\n"); } struct B* new_B () { struct B* b = (struct B*) malloc (sizeof (struct B)); Create an instance of A’s jump table b->ping = B_ping; b->pong = A_pong; struct A* new_A () { b->wiff = B_wiff; struct A* a = (struct A*) malloc (sizeof (struct A)); return b; a->ping = A_ping; } a->pong = A_pong; return a; } 9 10 Dispatch Diagram for C (data layout) •invoking ping and pong on an A and a B ... void foo (struct A* a) { a->ping (); struct A void A_ping () {} A.ping () {} a->pong (); void A_pong () {} ping } void B_ping () {} pong void B_wiff () {} A.pong () {} void bar () { foo (new_A ()); foo ((struct A*) new_B ()); Struct B B.ping () {} } ping pong wiff B.wiff () {} struct A { struct B { void (*ping) (); void (*ping)(); void (*pong) (); void (*pong)(); }; void (*wiff)(); }; struct A* new_A () { struct A* a = (struct A*) malloc ( sizeof (struct A) ); struct B* new_B () { a->ping = A_ping; struct B* b = (struct B*) malloc ( sizeof (struct B) ); a->pong = A_pong; b->ping = B_ping; return a; b->pong = A_pong; } b->wiff = B_wiff; return b; } 11 12

  4. Dispatch Diagram for C (the dispatch) ISA for Polymorphic Dispatch void foo (struct A* a) { r[0] ← m[r[5]] # r0 = a pc ← m[r[1]+0*4] # a->ping () struct A void A_ping () {} a->ping (); A.ping () {} pc ← m[r[1]+1*4] # a->ping () void A_pong () {} a->pong (); ping void B_ping () {} } pong void B_wiff () {} A.pong () {} ‣ How do we compile Struct B B.ping () {} ping • a->ping () ? void foo (struct A* a) { pong ‣ Pseudo code a->ping (); B.wiff () {} wiff a->pong (); } • pc ← m[r[1]+0*4] void bar () { ‣ Current jumps supported by ISA foo (new_A ()); foo ( (struct A*) new_B ()); } Name Semantics Assembly Machine jump absolute pc ← a j a b--- aaaaaaaa indirect jump pc ← r[ t ] + ( o == pp *2) j o (r t ) ctpp foo (a) r5 r5 a ‣ We will benefit from a new instruction in the ISA a r[0] ← m[r[5]] # r0 = a pc ← m[r[1]+0*4] # a->ping () pc ← m[r[1]+1*4] # a->ping () • that jumps to an address that is stored in memory Runtime Stack 13 14 ‣ Double-indirect jump instruction (b+o) •jump to address stored in memory using base+offset addressing Name Semantics Assembly Machine jump absolute pc ← a j a b--- aaaaaaaa indirect jump pc ← r[ t ] + ( o == pp *2) j o (r t ) ctpp dbl-ind jump b+o pc ← m[r[ t ] + ( o == pp *2)] j * o (r t ) dtpp Switch Statements 15 16

  5. Switch Statement Human vs Compiler int i; void bar () { ‣ Benefits for humans int j; if (i==0) j=10; void foo () { else if (i==1) • the syntax models a common idiom: choosing one computation from a set switch (i) { j = 11; ‣ But, switch statements have interesting restrictions case 0: j=10; break; else if (i==2) case 1: j=11; break; j = 12; • case labels must be static , cardinal values case 2: j=12; break; else if (i==3) case 3: j=13; break; j = 13; - a cardinal value is a number that specifies a position relative to the beginning of an ordered set default: j=14; break; else } j = 14; - for example, integers are cardinal values, but strings are not } } • case labels must be compared for equality to a single dynamic expression ‣ Semantics the same as simplified nested if statements - some languages permit the expression to be an inequality ‣ Do these restrictions benefit humans? • where condition of each if tests the same variable • unless you leave the break the end of the case block • have you ever wanted to do something like this? ‣ So, why bother putting this in the language? switch (treeName) { switch (i,j) { • is it for humans, facilitate writing and reading of code? case "larch": case i>0: • is it for compilers, permitting a more efficient implementation? case "cedar": case i==0 & j>a: case "hemlock": case i<0 & j==a: ‣ Implementing switch statements } default: • we already know how to implement if statements; is there anything more to consider? } 17 18 Why Compilers like Switch Statements Happy Compilers mean Happy People switch (i) { label jumpTable[4] = { L0, L1, L2, L3 }; ‣ Notice what we have case 0: j=10; break; if (i >3) goto DEFAULT; case 1: j=11; break; • switch condition evaluates to a number goto jumpTable[i]; case 2: j=12; break; L0: j = 10; • each case arm has a distinct number case 3: j=13; break; goto CONT; ‣ And so, the implementation has a simplified form default: j=14; break; L1: j = 11; } goto CONT; • build a table with the address of every case arm, indexed by case value L2: j = 12; goto CONT; • switch by indexing into this table and jumping to matching case arm L3: j = 13; ‣ For example goto CONT; DEFAULT: switch (i) { label jumpTable[4] = { L0, L1, L2, L3 }; j = 14; case 0: j=10; break; if (i >3) goto DEFAULT; goto CONT; case 1: j=11; break; goto jumpTable[i]; CONT: case 2: j=12; break; L0: j = 10; case 3: j=13; break; goto CONT; ‣ Computation can be much more efficient default: j=14; break; L1: j = 11; if (i==0) } goto CONT; j=10; • compare the running time to if-based alternative L2: j = 12; else if (i==1) ‣ But, could it all go horribly wrong? goto CONT; j = 11; L3: j = 13; else if (i==2) • construct a switch statement where this implementation technique j = 12; goto CONT; is a really bad idea else if (i==3) DEFAULT: ‣ Guidelines for writing efficient switch statements j = 13; j = 14; else goto CONT; j = 14; CONT: 19 20

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend