Do the following:
- Give each expression node a unique number en. You can do this as you walk over the tree, on-the-fly.
- For leaf node numbered el, generate "MOV el, operand"
- For each interior node "OP" numbered er, with binary children es and et, generate "OP er, es, et". Use the obvious generalization to handle operators with arbitrary numbers of children.
This will produce "naive" code, in the sense that the virtual register numbers can be arbitrarily large (e.g., determined by the size of the program).
A more sophisticated version would keep a pool of node numbers, assign the lowest available number from the pool to each node as you encounter them from left to right, and put the numbers for the OP-instruction input operands back in the pool (as they are now "free") as each OP instruction is generated. This will produce in practice a much smaller set of virtual register set numbers.
If you want to get clever, after you have done the above, apply register coloring to the generated code, to enable using a fixed number of registers.