I'm working on a compiler for a stack machine (specifically CIL) and I've parsed the code into a graph of basic blocks. From here I'm looking to apply SSA to the methods, but it's not going too well. My first attempt (while working with a flat listing, rather than the graph) was to iterate over the code and keep a stack of SSA ids (that is, for the assign targets), pushing them when I produce an assignment, popping them when they're used. This works just fine for a single basic block, but I simply can't figure out how to handle producing Φ functions.
The idea I've been tossing around is to attach a stack position to the SSA ids and then look at what's still on the stack when the code paths converge, but this doesn't seem like the Right Way (TM) of doing things.
Is there a simple algorithm for tracking the stack manipulations across multiple code paths and determining the collisions when they converge?