A simple approach would identify basic blocks in the code and translate them to an IR for an optimizing compiler back-end like LLVM.
Of course, you have to be careful with self-modifying code.