Relooper goes the other direction though. We don't need to produce high-level control flow. For a transpilation-to-Zig use case the algorithm can be extremely dumb. Naively translate every loop (or any other control flow containing nontrivial internal gotos) into a labeled switch, then partition the bodies at each goto statement, making a new label for each such location.
The only thing you're really losing is that the goto is bounded by an enclosing function, but that's also the case in C, C++ and other languages written by non-masochists. From a developer ergonomics perspective you _might_ want a proper goto instead of having to rely on a clunky general-purpose transformation as described above (though I've yet to see that use case), but from a transpilation perspective it's extremely easy to just treat labeled switch as a bunch of gotos, replace other control flow with gotos, and lower an entire function into something that compiles optimally.
Comments
Relooper goes the other direction though. We don't need to produce high-level control flow. For a transpilation-to-Zig use case the algorithm can be extremely dumb. Naively translate every loop (or any other control flow containing nontrivial internal gotos) into a labeled switch, then partition the bodies at each goto statement, making a new label for each such location.
The only thing you're really losing is that the goto is bounded by an enclosing function, but that's also the case in C, C++ and other languages written by non-masochists. From a developer ergonomics perspective you _might_ want a proper goto instead of having to rely on a clunky general-purpose transformation as described above (though I've yet to see that use case), but from a transpilation perspective it's extremely easy to just treat labeled switch as a bunch of gotos, replace other control flow with gotos, and lower an entire function into something that compiles optimally.