SI-7006 Eliminate NOPs and unreachable code

We were generating NOPs in two cases

1) If jump elision failed to eliminate a JUMP-only block that happened to JUMP to its next block then the code emitter would spit out a NOP. The answer was to fix jump elision to catch more jump-only blocks. Along the way a O(N*M) method was reduced to O(N)

2) Partly from jump elision and partly from other sources we were leaving behind unreachable code. ASM turned that into NOPs. The answer here was to get rid of the unreachable exception handler elision that would leave behind other unreachable code and replace it with full blown (and simpler) unreachable block elision.

PR is broken into several commits to make review a bit easier.