On 12/8/20 11:35 AM, Richard Biener wrote:
> 
> +         {
> +           /* Remove a nonbind marker when the outer scope of the
> +              inline function is completely removed.  */
> +           if (gimple_debug_nonbind_marker_p (stmt)
> +               && BLOCK_ABSTRACT_ORIGIN (b))
> +             {
> +               while (TREE_CODE (b) == BLOCK
> +                      && !inlined_function_outer_scope_p (b))
> +                 b = BLOCK_SUPERCONTEXT (b);
> 
> So given we never remove a inlined_function_outer_scope_p BLOCK from
> the block tree can we assert that we find such a BLOCK?  If we never
> elide those BLOCKs how can it happen that we elide it in the end?
> 
> +               if (TREE_CODE (b) == BLOCK)
> +                 {
> +                   if (TREE_USED (b))
> 

For gimple_debug_inline_entry_p inlined_function_outer_scope_p (b)
is always true, and the code always proceeds to gsi_remove the
gimple_debug_inline_entry_p.

But for gimple_debug_begin_stmt_p all paths are actually taken.

> Iff we ever elide such block then this will still never be
> false since we've otherwise removed the BLOCK from the block

I can assure you TREE_USED (b) can be false here.

> tree already when we arrive here.  Iff we did that, then the
> above search for a inlined_function_outer_scope_p BLOCK
> might find the "wrong" inline instance.
> 

Indeed, that is a good question.

I tried to find out if there are constraints that are
preserved by remove_unused_scope_block_p, that can be
used to prove that the BLOCK_SUPERCONTEXT walking loop
does not fine the "wrong" inline instance.

diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
index 9ea24a1..997ccee 100644
--- a/gcc/tree-ssa-live.c
+++ b/gcc/tree-ssa-live.c
@@ -525,6 +525,10 @@ remove_unused_scope_block_p (tree scope, bool 
in_ctor_dtor_block)
            *t = BLOCK_SUBBLOCKS (*t);
            while (BLOCK_CHAIN (*t))
              {
+               gcc_assert (supercontext == BLOCK_SUPERCONTEXT 
(BLOCK_SUPERCONTEXT (*t)));
+               if (!TREE_USED (*t))
+                 gcc_assert (!inlined_function_outer_scope_p 
(BLOCK_SUPERCONTEXT (*t)));
+               gcc_assert (!TREE_USED (BLOCK_SUPERCONTEXT (*t)));
                BLOCK_SUPERCONTEXT (*t) = supercontext;
                t = &BLOCK_CHAIN (*t);
              }


So I think I can prove that the above assertions hold,
at least when this block is not taken:

   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE
            && !optinfo_wants_inlining_info_p ())
     {
       /* Even for -g0 don't prune outer scopes from artificial
          functions, otherwise diagnostics using tree_nonartificial_location
          will not be emitted properly.  */
       if (inlined_function_outer_scope_p (scope))
         {
           tree ao = BLOCK_ORIGIN (scope);
           if (ao
               && TREE_CODE (ao) == FUNCTION_DECL
               && DECL_DECLARED_INLINE_P (ao)
               && lookup_attribute ("artificial", DECL_ATTRIBUTES (ao)))
             unused = false;
         }
     }

in that case it should be irrelevant, since we wont have debug_begin_stmt_p
if debug_info_level == DINFO_LEVEL_NONE.

So the above assertions mean, that *if* !TREE_USED (b)
which is the precondition for the while loop, then we know that
BLOCK_SUPERCONTEXT (b) *was* not a inlined_function_outer_scope_p,
and it was replaced by BLOCK_SUPERCONTEXT (BLOCKL_SUPERCONTEXT (b))
so the loop skips one step, but the result is still the same
inline block.

However what concerns me, is that the assertion
if (TREE_USED (*t))
   gcc_assert (!inlined_function_outer_scope_p (BLOCK_SUPERCONTEXT (*t))
does not hold.  I think that means, that it can happen, that
a USED block is moved out of the inline block.  And while I have
no test case for it, that sheds a big question on the correctness
of the debug info when that happens.

But I think that is a different issue if I at all.


> So I think we only eliminate the inline scopes if all
> subblocks are unused but then if there's a used outer
> inline instance your patch will assign that outer inline
> instances BLOCK to debug stmts formerly belonging to the
> elided inline instance, no?  (we also mess with
> BLOCK_SUPERCONTEXT during BLOCK removal so I'm not sure
> the walking works as expected)
> 

I did not look at before, and just expected it to behave
reasonably, that is just elide *unused* lexical scopes, and
*empty* subroutines, but that it seems to move *used* lexical
scopes to the grandfather scope, while *removing* the now
*emptied* original inline block is odd.

Or maybe I missed something...


> I guess we could, during the remove_unused_scope_block_p
> DFS walk, record and pass down the "current"
> inlined_function_outer_scope_p BLOCK and for BLOCKs
> we elide record that somehow?  (since we elide the block
> we could abuse fragment_origin/chain for this)
> Then in the patched loop do
> 

I would rather not move *used* blocks out of an inline scope,
whether or not that appears to be unused.

But maybe as a follow up, maybe I find even a test case, where the
debug info is broken, I have debugged a lot optimized code, and never
encountered something like that, but that proves not much.

>         if (b && !TREE_USED (b))
>           {
>             if (BLOCK_ABSTRACT_ORIGIN (b))
>               {
>                 if (!TREE_USED (BLOCK_FRAGMENT_CHAIN (b))
>                   {
>                     /* inline instance removed */
>                     gsi_remove ();
>                   }
>                 else
>                   {
>                     /* inline instance kept, use it */
>                     gimple_set_block (stmt, BLOCK_FRAGMENT_CHAIN (b));
>                   }
>               }
>            else 
>              gimple_set_block (stmt, NULL);
>          }
> 
> ?  Not sure what happens if the inline instance ends up split into
> a hot/cold part - then the "punning" of the BLOCK of the stmt to
> the outermost scope could cause gdb to set bogus breakpoints?
> 
> Thanks,
> Richard.
> 


Yes, it does indeed happen.  I have a gdb patch for that already posted
for review: 
https://sourceware.org/pipermail/gdb-patches/2020-November/173614.html

It is important to only break at the entry_point_pc, not when crossing
a sub-block boundary of the same subroutine.

Another deficiency that is not completely solvable is line breakpoints
at the exit of a subroutine, the dwarf format specifies the block ranges
just as byte ranges, while the line table can hold different lines
at the same PC, from different scopes, there have different view numbers.
So I would be happy to have the complete call stack, maybe some frames too
much, instead of some frames missing in the middle...


Thanks
Bernd.

Reply via email to