
Go backward to Flow graphs and Basic Blocks
Go up to Top
Go forward to The Correctness of LR(0) parsing
Some Implementation Details
- When we first encounter a reference to a variable in a basic block,
we need to know that it has not been previously assigned
a value number.
- Setting all variable value numbers to an unused value
(like -1) initially will do the trick.
- When we finish processing one block and want to begin another,
we need to forget all the value numbers that had been assigned.
- To avoid having to reset every variable's declaration
descriptor, we can keep a list of variables that have
been assigned values in the current basic block.
- Such a list can also reduce the effort required to handle
aliasing.
When we process an assignment,
we can just go through the list of variables to which value
numbers have been assigned and un-assign value number
associated with possible aliases found in the list.
- The whole point of value numbering is to avoid recalculating
CSEs. To do this, we have to be able to:
- know enough to leave a CSE's value in a temporary
after we first compute it, and
- find the temporary holding the value when we encounter
the CSE again.
- To make this easier, we will allocate operand descriptors for
expressions while we are value numbering rather than while
we are generating code.
- Each time we assign a new value number, we will allocate
a new operand descriptor.
- We will store a pointer to the operand descriptor with the
new value number in the hash table, and in the syntax
tree node for the root of each copy of the CSE.
- We will add "not-yet-calculated" to our existing
operand descriptor types (i.e. areg, dreg, basedvar)
and set this as the type of the descriptors we create.
- When the code generator reaches an expression node, it
will only generate code for the expression if the
operand descriptor pointed to by the expression's root
node is "not-yet-calculated". Otherwise, it will
just assume the value is in the temporary described by
the operand descriptor.
- Finally, if the value of a CSE is put in a register, we have to
know how long it needs to be kept so that we can reuse the
register as soon as possible. We will take a very simple
approach to this register/temporary allocation problem.
- Add an extra field to the operand descriptor
type to
count how many copies of the associated
expression (that are not subtrees
of some larger CSE) were encountered.
- The "not subtrees" part means that you don't increment
a CSE's counter when you find a tree that matches it.
Instead, when you find a node that represent a first
instance of a new CSE, you increment the counters of all
its children.
- When generating code, each time you think about generating
code for a tree that shares a given operand descriptor
you will decrement the use counter. When its counter
becomes 0 you can free the descriptor.
Computer Science 434
Department of Computer Science
Williams College
