To get more precise, we benefit from knowing the nesting depth of each block. This plus reducible control flow is enough to reliably find loops. We also know exactly which edges are loop backedges; it’s easiest when these are explicitly annotated but perhaps it would be possible to derive that info from other loop info. (In Ion we have a dedicated “backedge block” per loop, which makes it obvious what we should do, but which other compilers likely wouldn’t have.)