https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121093
--- Comment #3 from Richard Biener <rguenth at gcc dot gnu.org> --- t.c.047t.cddce1:Marking useful stmt: # DEBUG INLINE_ENTRY p2 t.c.047t.cddce1:Marking useful stmt: # DEBUG INLINE_ENTRY p1 t.c.047t.cddce1: # DEBUG INLINE_ENTRY p1D.2958 we eliminate the inline scope because it has no live subblocks: /* Innermost blocks with no live variables nor statements can be always eliminated. */ else if (!nsubblocks) ; the block and its single subblock has no active stmt and no active variable. But as you say it has an active debug stmt. What we do is prune debug stmts referencing said blocks - we go from (I've added block-num dumping): [t.c:13:9] [0]# DEBUG BEGIN_STMT [t.c:13:9] [0]# DEBUG a => a_3(D) [t.c:5:12] [2]# DEBUG INLINE_ENTRY p2 [t.c:7:9] [3]# DEBUG BEGIN_STMT [t.c:13:16] [0]# DEBUG a => NULL [t.c:13:16] [0]# DEBUG a => [t.c:7:17] a_3(D) + 2 [t.c:1:12] [4]# DEBUG INLINE_ENTRY p1 [t.c:3:9] [5]# DEBUG BEGIN_STMT [t.c:3:17] [5]_5 = a_3(D) + 3; [t.c:13:16 discrim 1] [0]# DEBUG a => NULL [t.c:13:16 discrim 3] [0]return _5; to [t.c:13:9] [0]# DEBUG BEGIN_STMT [t.c:13:9] [0]# DEBUG a => a_3(D) [t.c:13:16] [0]# DEBUG a => NULL [t.c:13:16] [0]# DEBUG a => [t.c:7:17] a_3(D) + 2 [t.c:1:12] [4]# DEBUG INLINE_ENTRY p1 [t.c:3:9] [5]# DEBUG BEGIN_STMT [t.c:3:17] [5]_5 = a_3(D) + 3; [t.c:13:16 discrim 1] [0]# DEBUG a => NULL [t.c:13:16 discrim 3] [0]return _5; and there's no reference to p2() left, neither in lines nor blocks. IMO that's reasonable - there's no actual stmt and no active variable so eliminating completely dead code from debug looks reasonable to me in this case. I do not get any references to locations from p2. After early inlining we have [t.c:13:9] [0]# DEBUG BEGIN_STMT [t.c:13:9] [0]# DEBUG a => a_3(D) [t.c:5:12] [2]# DEBUG INLINE_ENTRY p2 [t.c:7:9] [3]# DEBUG BEGIN_STMT [t.c:7:17] [3]_4 = a_3(D) + 2; _7 = _4; [t.c:13:16] [0]# DEBUG a => NULL [t.c:13:16] [0]_1 = _7; [t.c:13:16] [0]# DEBUG a => _1 [t.c:1:12] [4]# DEBUG INLINE_ENTRY p1 [t.c:3:9] [5]# DEBUG BEGIN_STMT [t.c:3:17] [5]_5 = _1 + 1; _8 = _5; [t.c:13:16 discrim 1] [0]# DEBUG a => NULL [t.c:13:16 discrim 1] [0]_6 = _8; [t.c:13:16 discrim 3] [0]return _6; the debug stmts for the parameter setup have locations at the caller and the wrong block though? Renaming p3's parameter to b shows it better: [t.c:13:9] [0]# DEBUG BEGIN_STMT [t.c:13:9] [0]# DEBUG a => b_3(D) ^^^ we reference a, but that's from scope 2 (or 3?)? [t.c:5:12] [2]# DEBUG INLINE_ENTRY p2 [t.c:7:9] [3]# DEBUG BEGIN_STMT [t.c:7:17] [3]_4 = b_3(D) + 2; _7 = _4; [t.c:13:16] [0]# DEBUG a => NULL [t.c:13:16] [0]_1 = _7; [t.c:13:16] [0]# DEBUG a => _1 [t.c:1:12] [4]# DEBUG INLINE_ENTRY p1 [t.c:3:9] [5]# DEBUG BEGIN_STMT [t.c:3:17] [5]_5 = _1 + 1; _8 = _5; [t.c:13:16 discrim 1] [0]# DEBUG a => NULL [t.c:13:16 discrim 1] [0]_6 = _8; [t.c:13:16 discrim 3] [0]return _6; in the end I'm not sure what's "wrong" here and why you think you are missing p2 - p2 is not executed, you shouldn't get any profile on it.