Just a bit curious, but what's the reason behind the verifier banning jumping to the entry block?
Is it more of an implementation limitation, in that some parts were a lot easier to implement if you can safely assume that entry has no other jumps. Or is it some opinionated decision.
Optimizing direct recursion by just substituting a call to self with a jump to the entry block is a very intuitive and simple optimization which a lot of people would want to do. You can of course still do it by defining a second block which the entry block immediately jumps to though.
The main reason is that it establishes a useful invariant: every loop has a preheader (a block outside the loop that enters the loop). This is useful for some transforms, for example LICM: if the whole function body is a loop and there is loop-invariant code, it's useful to have a block to hoist the code to.
This isn't fundamental and we could work around it by, e.g., creating a preheader during a transform pass when needed. But in general it's useful to establish invariants and normalize/canonicalize to them to simplify the compiler when possible. Simplicity begets robustness/reliability and also allows us to write more general/powerful optimizations (as a general statement). As you noted it's not too bad to work around this one by creating a separate entry block yourself.
This is a somewhat common thing for what it's worth: e.g., LLVM has the LoopSimplify pass that establishes a preheader for every loop (as well as some other properties) and notes that this "makes several analyses and transformations simpler and more effective". While LLVM tends to have a bunch of passes that mutate the IR in-place, Cranelift has more of an emphasis on compilation speed so tries to enforce code being in the proper structure at construction time, avoiding expensive control-flow mutations.
Last updated: Dec 23 2024 at 12:05 UTC