In this code fragment “endIndex” is a literal and does not need to be spilled; the comparison will test against the literal directly. However, if “endIndex” is a computed value then it must be available for each iteration. In that case, “endIndex” will not be run out to scratchpad unless the EBB that forms the loop body is so long that it would fall off the end anyway if the EBB were straight line code rather than a loop. That is, the fact that it’s a loop doesn’t matter to the spilling.
Looping doesn’t matter because belt values carried over a control flow join (such as the bottom of the loop) are kept in the belt; the inbound branches carry the values as arguments and the join makes the belt congruent regardless of where control flow came from; this is a hardware version of the “phi” operation used in Static Single Assignment code representation. Irrelevant belt positions are discarded and “endIndex” will start the next iteration at or near the front of the belt.
When live values (including loop-carried vales, as here) drop off the belt before passing through a join, they are spilled to scratchpad and then filled for the next use. In the hardware, the technology used for the scratchpad is in fact the same as is used for a large register file; the only difference is how you get at the bits, and that save-restore for function calls is automatic. Consequently there is no power or latency improvement in using a register over using scratchpad, and adding a register for loops would needlessly complicate the compiler and access logic.