This optimization is performed inherent to each of the other optimization passes. Initially, when basic blocks are computed, a directed search from the entry node detects all the reachable blocks. Any unreachable code is subsequently removed. Also, as a consequence of other optimizations, certain code may become unreachable. In the following example, constant folding would lead 2 instructions being unreachable.
Unoptimized ldc (s.32) t1, 1 btrue t1, L1 ldc (s.32) t2, 4 /* Unreachable */ cpy (s.32) r3, t2 /* Unreachable */ /* Some reachable code */ L1 : ldc (s.32) t3, 2 cpy (s.32) r3, t3 Optimized jmp L1 /* Some reachable code */ L1 : ldc (s.32) t3, 2 cpy (s.32) r3, t3
In my implementation, I would remove this unreachable code as and when they get generated. Hence, there is no separate unreachable code elimination phase of the optimizer.