1. Each expression has a type. (A) e -- If the static type of e is t1 if t1 is a subtype of A then ignore the type cast. if A is a subtype of t1 then the type of (A) e is A. else type error Rest of the type rules are standard. Code generation: Each class has a special field classID (could be String containing the fully qualified name); we will instead store a unique integer for each class. We assign the classIDs such that, if class B extends class A, then A.classID < B.classID. new A() -- If size of class A is n bytes, we allocate n+4 bytes (for the additional classID) and set classID to A.classID. (A) e -- t1 = [e]; if (t1.classID > A.classID) error. 2. A flow graph is reducible iif when we remove all the back edges the resulting flow graph is acyclic: Proof by contradiction: Say the graph is reducible, we remove the backedges and the resulting flow graph has a cycle. Add the backedges. Do T1/T2 transformation repeatedly -- would not lead to a single node. 3. See Section 18.10 Muchnick 4. At each program point, maintain the set of copies: Intraprocedural copy propagation: CPin (i) = meet CPout(j) // for all predecessor j of i CPout (i) = COPY(i) + (CPin (i) - Kill (i)) Context insensitive analysis: CPin (p) = meet CPout (all calls to p) CPout (call p) = CPout (p) CPin, CPout: for each statement i return a set of variable names.