Here's an approximate order of steps that I'm thinking about:

Block0():
	n0 = 4
	goto Block1(n0, 0)

Block1(n1, i0):
	j0 = i0 + n0
	i1 = i0 + 1
	i1 < 5 Block1(n1, i1) else Block2()

Block2():
	nop

=>
n0 = U
n1 = U
i0 = U
j0 = U
i1 = U

=>
n0 = [4, 4]
n1 = U
i0 = U
j0 = U
i1 = U

=>
n0 = [4, 4]
n1 = [4, 4]
i0 = [0, 0]
j0 = U
i1 = U

=>
n0 = [4, 4]
n1 = [4, 4]
i0 = [0, 0]
j0 = [4, 4]
i1 = U

=>
n0 = [4, 4]
n1 = [4, 4]
i0 = [0, 0]
j0 = [4, 4]
i1 = [1, 1]

/* here's the branch */
n1 = minmax([4, 4], [4, 4]) = [4, 4]
i0 = minmax(i0, maxmin(i1, [-inf, 4])) = minmax(i0, [1, 4]) = [0, 4]
i0 changed, so recalculate block?
maxmin() here is to signify that the absolute highest value i0 could have
is the branch condition value, with i1 giving the lowest possible value.
Not entirely sure if that's all it takes, but at least this fairly trivial
example seems to work. Should check how comparison results fare, like
"b0 = r1 < 5
 b0 == 1 Block0 ..."

One (radical) option would be to ban comparisons completely and just force the
compiler to output branches everywhere, at which point it would then probably be
necessary to pattern match the hardware instructions or something to
'reimplement' comparisons.

=>
n0 = [4, 4]
n1 = [4, 4]
i0 = [0, 4]
j0 = [4, 4]
i1 = [1, 1]

=>
n0 = [4, 4]
n1 = [4, 4]
i0 = [0, 4]
j0 = [4, 8]
i1 = [1, 5]

/* here's the branch again */
n1 = minmax([4, 4], [4, 4]) = [4, 4] // no change
i0 = minmax(i0, maxmin(i1, [-inf, 4])) = minmax(i0, [1, 4]) = [0, 4]
no change so don't recalculate block?


===

The above works alright, but the compiler is likely easier to implement if the
optimizer accepts stuff like

	b0 = i5 < 5
	bnz b0 Block1(...) else Block2(...)

bnz/bez are the weakest for of branch and doesn't provide any good info on
limits (full blown SCEV as in GCC would likely be required for that), but we can
add a pass that tries to move 'trivial' branches to

	i5 < 5 Block1(...) else Block2(...)

as this gives more context to the blocks that follow, even if just a little bit.
Could still be useful for the range analysis.

In the weak case, bnz/beq, we must perform a weak widening of all parameters in
the blocks, i.e. if the new range max is larger, it must be increased to
infinity, and if the new range min is smaller, it must be decreased to negative
infinity. If any ranges changed, mark the block as not done yet and should be
recomputed. Note that the weak widening applies to the stronger conditonal
branches as well for all parameters except where the condition register is used.
