The of InstructionStack keeps track of which instructions pushed a
certain element of the stack. It is a stack of sets of instructions. You can
think of it like this: (1, {4,6}, 8) means that instruction 1 pushed the item
on the bottom of the stack, instructions 4 and 6 both push the second element
on the stack (depending on control flow), and instruction 8 pushed the
element on top of the stack. We use this information at a call site to
determine what instruction(s) pushed the receiver object onto the stack.
Special thanks to Jan Vitek for helping me come up with this algorithm.
This class is an InstructionVisitor that updates the instruction
stack representation appropriately. When there is a merge in control flow,
two InstructionStacks are merged using the merge
method.
This class is also used to determine whether the object at a given stack
depth is "preexistent". An object preexists if we can guarantee that it was
created outside of the method in which it is used. While it is possible to
determine which fields are preexistent (see "Inlining of Virtual Methods" by
Detlefs and Ageson in ECOOP99), we only keep track of local variables that
preexist.
We determine which local variables preexist as follows. Initially, only the
local variables for method parameters preexist. When a store is encoutered,
we determine if the set of instructions on top of the stack consist of loads
preexistent variables. If so, then the variable being stored into is
preexistent. However, objects that are the result of an allocation
(constructor call) in the method are preexist. Thus, we maintain the preexist
information as a set. If the set is null, then the object does not preexist.
If the set is empty, then it preexists and came from at least one argument.
If the set is non-empty, then it contains the type(s) of the constructor(s)
from which it originated. Pretty neat, huh?
Returns the set of Instructions at depth n of the
instruction stack. Depth 0 is the top of the stack. The bottom of the
stack is at depth stackSize - 1.
Returns a Set representing whether or not the instructions at
a given depth push a preexistent object onto the stack. If the list is
null, then the push is not preexistent. If the list is empty,
then the push is preexistent. If the list is non-empty, it contains the
Type(s) of objects that are known to be on the stack. These
types are the results of calls to constructors.