CSE — computing a*b once instead of twice — appears in every optimizer as either local CSE or Global Value Numbering; LLVM's GVN pass and its NewGVN rewrite are direct descendants of the technique you are building. Doing it correctly requires the side-effect and aliasing discipline that separates real compilers from toy ones.