Implementations of this method can provide local optimizations like constant folding and
strength reduction. Implementations should look at the properties and inputs of the current
node and determine if there is a more optimal and always semantically correct replacement.
The return value determines the effect that the canonicalization will have:
Returning an pre-existing node will replace the current node with the given one.
Returning a newly created node (that was not yet added to the graph) will replace the
current node with the given one, after adding it to the graph. If both the replacement and
the replacee are anchored in control flow (fixed nodes), the replacement will be added to the
control flow. It is invalid to replace a non-fixed node with a newly created fixed node
(because its placement in the control flow cannot be determined without scheduling).
Returning null will delete the current node and replace it with null at
all usages. Note that it is not necessary to delete floating nodes that have no more usages
this way - they will be deleted automatically.
A node class can implement this method to convey information about what its effect would be
if some of its inputs were virtualized. All modifications must be made through the supplied
tool, and not directly on the node, because by the time this method is called the
virtualized/non-virtualized state is still speculative and might not hold because of loops,