https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96017
--- Comment #8 from Peter Bergner <bergner at gcc dot gnu.org> --- Interesting, if I rewrite the test case so that foo is a parameter and not a global var, then we get the code we want: extern void slowpath(int *); int test (int *val, int foo) { int ret = foo; if (__builtin_expect(*val != 0, 0)) slowpath(val); return ret; } 0: addis 2,12,.TOC.-.LCF0@ha addi 2,2,.TOC.-.LCF0@l lwz 9,0(3) cmpwi 0,9,0 bne 0,.L11 mr 3,4 blr .p2align 4,,15 .L11: mflr 0 std 0,16(1) stdu 1,-48(1) std 4,32(1) bl slowpath nop ld 4,32(1) addi 1,1,48 ld 0,16(1) mr 3,4 mtlr 0 blr At first, I thought that split_live_ranges_for_shrink_wrap() split this nicely, but what I found is that IRA assigned a volatile register to a pseudo that is live across a call. LRA then sees that is illegal and spills the pseudo which ends up giving us what we want, but I don't like that. IRA should never assign a volatile register to a pseudo that is live across a call. Yet another thing to look into.