I attached a JMH benchmark showcasing the problem. The results on my machine:
Benchmark Mode Cnt Score Error Units
FoldCompare.foldableComparesExternal avgt 3 26,630 ± 13,493 ns/op
FoldCompare.foldableComparesInline avgt 3 57,526 ± 10,844 ns/op
FoldCompare.unsignedCompare avgt 3 26,499 ± 11,984 ns/op
C2 has code to merge handwritten range checks (e.g. value >= 0 && value < 100) into one CmpU (value <u 100). See https://github.com/openjdk/jdk/blob/c3a632dca75d2fad0a60e03e7b4fc64edb1e906e/src/hotspot/share/opto/ifnode.cpp#L687.
However, this doesn't work if the range check is directly written in a loop (see the foldableComparesInline method in the benchmark). It works if the exactly same check is extracted into an extra method.
From my analysis, the problem stems from the !region->has_phi() check here https://github.com/openjdk/jdk/blob/c3a632dca75d2fad0a60e03e7b4fc64edb1e906e/src/hotspot/share/opto/ifnode.cpp#L760C56-L760C74. In the case of the extracted method, the method gets first inlined, and projections both point to a region that then directly points to another region, but is not associated with a Phi. In contrast, the manually inlined check directly points to a region with a Phi, bailing out at this check.
My understanding of the existing code isn't good enough to tell how this can be improved. I assume the Phi check is too conservative, but I also assume that it is there for a valid reason.