[v3,2/3] Introduce a new line table flag is_weak

Message ID AS8P193MB1285C58F6F09502252CEC16FE4DF2@AS8P193MB1285.EURP193.PROD.OUTLOOK.COM
State New
Headers
Series Improve debugging of optimized code |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Test passed

Commit Message

Bernd Edlinger July 5, 2024, 9:17 a.m. UTC
  This introduces a new line table flag is_weak.
The line entries at the end of a subroutine range,
use this to indicate that they may possibly
be part of the previous subroutine.

When there is a sequence of line entries at the
same address where an inline range ends, and the
last item has is_stmt = 0, we force all previous
items to have is_weak = 1.

Additionally this adds a "fake" end sequence to the
record_line function, that is line number -1.
That will be used in the next patch.

Finally this adds a handling for empty ranges to
record_block_range.  Currently this function is
not called with empty ranges, but that will be used
in the next patch.

There should be no functional changes after this commit.
---
 gdb/buildsym.c                               | 106 ++++++++++++++++---
 gdb/buildsym.h                               |   3 +
 gdb/jit.c                                    |   1 +
 gdb/symmisc.c                                |   6 +-
 gdb/symtab.h                                 |   5 +
 gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp |   2 +-
 gdb/xcoffread.c                              |   1 +
 7 files changed, 109 insertions(+), 15 deletions(-)
  

Comments

Guinevere Larsen July 12, 2024, 8:14 p.m. UTC | #1
On 7/5/24 6:17 AM, Bernd Edlinger wrote:
> This introduces a new line table flag is_weak.
> The line entries at the end of a subroutine range,
> use this to indicate that they may possibly
> be part of the previous subroutine.
>
> When there is a sequence of line entries at the
> same address where an inline range ends, and the
> last item has is_stmt = 0, we force all previous
> items to have is_weak = 1.
This might be friday brain, but I'm having a hard time following the 
explanation. Could you add a small c code and line table example showing 
what you mean?
>
> Additionally this adds a "fake" end sequence to the
> record_line function, that is line number -1.
> That will be used in the next patch.
>
> Finally this adds a handling for empty ranges to
> record_block_range.  Currently this function is
> not called with empty ranges, but that will be used
> in the next patch.
>
> There should be no functional changes after this commit.
> ---
>   gdb/buildsym.c                               | 106 ++++++++++++++++---
>   gdb/buildsym.h                               |   3 +
>   gdb/jit.c                                    |   1 +
>   gdb/symmisc.c                                |   6 +-
>   gdb/symtab.h                                 |   5 +
>   gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp |   2 +-
>   gdb/xcoffread.c                              |   1 +
>   7 files changed, 109 insertions(+), 15 deletions(-)
>
> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> index 1c762ad81bf..9bf2fff6e29 100644
> --- a/gdb/buildsym.c
> +++ b/gdb/buildsym.c
> @@ -413,6 +413,16 @@ buildsym_compunit::record_block_range (struct block *block,
>         || end_inclusive + 1 != block->end ())
>       m_pending_addrmap_interesting = true;
>   
> +  if (block->inlined_p ())
> +    {
> +      m_inline_end_vector.push_back (end_inclusive + 1);
> +      if (end_inclusive + 1 == start)
> +	{
> +	  end_inclusive = start;
> +	  m_pending_addrmap_interesting = true;
> +	}
> +    }
> +
>     m_pending_addrmap.set_empty (start, end_inclusive, block);
>   }
>   
> @@ -627,19 +637,16 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
>   {
>     m_have_line_numbers = true;
>   
> -  /* Normally, we treat lines as unsorted.  But the end of sequence
> -     marker is special.  We sort line markers at the same PC by line
> -     number, so end of sequence markers (which have line == 0) appear
> -     first.  This is right if the marker ends the previous function,
> -     and there is no padding before the next function.  But it is
> -     wrong if the previous line was empty and we are now marking a
> -     switch to a different subfile.  We must leave the end of sequence
> -     marker at the end of this group of lines, not sort the empty line
> -     to after the marker.  The easiest way to accomplish this is to
> -     delete any empty lines from our table, if they are followed by
> -     end of sequence markers.  All we lose is the ability to set
> -     breakpoints at some lines which contain no instructions
> -     anyway.  */
> +  /* The end of sequence marker is special.  We need to delete any
> +     previous lines at the same PC, otherwise these lines may cause
> +     problems since they might be at the same address as the following
> +     function.  For instance suppose a function calls abort there is no
> +     reason to emit a ret after that point (no joke).
> +     So the label may be at the same address where the following
> +     function begins.  There is also a fake end of sequence marker (-1)
> +     that we emit internally when switching between different CUs
> +     In this case, duplicate line table entries shall not be deleted.
> +     We simply set the is_weak marker in this case.  */
I don't understand why you changed so much this comment. Most of the 
original stuff (if not all of it) is still valid after your changes. 
Your version either doesn't fully explain the logic of the first "if" 
block, or it relies on too much specific knowledge and I no longer 
understand the explanation. Either way, I'd say more of the original 
comment should be kept.
  
Bernd Edlinger July 19, 2024, 6:09 a.m. UTC | #2
On 7/12/24 22:14, Guinevere Larsen wrote:
> On 7/5/24 6:17 AM, Bernd Edlinger wrote:
>> This introduces a new line table flag is_weak.
>> The line entries at the end of a subroutine range,
>> use this to indicate that they may possibly
>> be part of the previous subroutine.
>>
>> When there is a sequence of line entries at the
>> same address where an inline range ends, and the
>> last item has is_stmt = 0, we force all previous
>> items to have is_weak = 1.
> This might be friday brain, but I'm having a hard time following the explanation. Could you add a small c code and line table example showing what you mean?

Ok, here is the line-table from gdb.base/empty-inline.c, see part 3 of the
patch series, line 18-21 is in the inline function, 36-39 in main.
and line 29-31 in a function without an inline block. 

INDEX  LINE   REL-ADDRESS        UNREL-ADDRESS      IS-STMT IS-WEAK PROLOGUE-END EPILOGUE-BEGIN 
0      36     0x0000555555555040 0x0000000000001040 Y       Y                                   
1      36     0x0000555555555040 0x0000000000001040 Y       Y                                   
2      18     0x0000555555555040 0x0000000000001040 Y       Y    <--+ same addr                            
3      20     0x0000555555555040 0x0000000000001040 Y       Y       ^ set is_weak                           
4      21     0x0000555555555040 0x0000000000001040 Y       Y       ^                            
5      36     0x0000555555555040 0x0000000000001040           <-----+ no is-stmt                               
6      37     0x000055555555504a 0x000000000000104a Y       Y                                   
7      18     0x000055555555504a 0x000000000000104a Y       Y   <--+ same addr                               
8      20     0x000055555555504a 0x000000000000104a Y       Y      ^ set is_weak                              
9      21     0x000055555555504a 0x000000000000104a Y       Y      ^                             
10     37     0x000055555555504a 0x000000000000104a          <-----+ no is-stmt                                   
11     38     0x000055555555504f 0x000000000000104f Y                                           
12     39     0x000055555555504f 0x000000000000104f                                             
13     END    0x0000555555555052 0x0000000000001052 Y                                           
14     29     0x0000555555555150 0x0000000000001150 Y                                           
15     30     0x0000555555555150 0x0000000000001150 Y                                           
16     31     0x0000555555555150 0x0000000000001150 Y                                           
17     31     0x0000555555555150 0x0000000000001150          <----- no inline here                                    
18     32     0x0000555555555153 0x0000000000001153                                             
19     END    0x0000555555555154 0x0000000000001154 Y                                           



>>
>> Additionally this adds a "fake" end sequence to the
>> record_line function, that is line number -1.
>> That will be used in the next patch.
>>
>> Finally this adds a handling for empty ranges to
>> record_block_range.  Currently this function is
>> not called with empty ranges, but that will be used
>> in the next patch.
>>
>> There should be no functional changes after this commit.
>> ---
>>   gdb/buildsym.c                               | 106 ++++++++++++++++---
>>   gdb/buildsym.h                               |   3 +
>>   gdb/jit.c                                    |   1 +
>>   gdb/symmisc.c                                |   6 +-
>>   gdb/symtab.h                                 |   5 +
>>   gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp |   2 +-
>>   gdb/xcoffread.c                              |   1 +
>>   7 files changed, 109 insertions(+), 15 deletions(-)
>>
>> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
>> index 1c762ad81bf..9bf2fff6e29 100644
>> --- a/gdb/buildsym.c
>> +++ b/gdb/buildsym.c
>> @@ -413,6 +413,16 @@ buildsym_compunit::record_block_range (struct block *block,
>>         || end_inclusive + 1 != block->end ())
>>       m_pending_addrmap_interesting = true;
>>   +  if (block->inlined_p ())
>> +    {
>> +      m_inline_end_vector.push_back (end_inclusive + 1);
>> +      if (end_inclusive + 1 == start)
>> +    {
>> +      end_inclusive = start;
>> +      m_pending_addrmap_interesting = true;
>> +    }
>> +    }
>> +
>>     m_pending_addrmap.set_empty (start, end_inclusive, block);
>>   }
>>   @@ -627,19 +637,16 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
>>   {
>>     m_have_line_numbers = true;
>>   -  /* Normally, we treat lines as unsorted.  But the end of sequence
>> -     marker is special.  We sort line markers at the same PC by line
>> -     number, so end of sequence markers (which have line == 0) appear
>> -     first.  This is right if the marker ends the previous function,
>> -     and there is no padding before the next function.  But it is
>> -     wrong if the previous line was empty and we are now marking a
>> -     switch to a different subfile.  We must leave the end of sequence
>> -     marker at the end of this group of lines, not sort the empty line
>> -     to after the marker.  The easiest way to accomplish this is to
>> -     delete any empty lines from our table, if they are followed by
>> -     end of sequence markers.  All we lose is the ability to set
>> -     breakpoints at some lines which contain no instructions
>> -     anyway.  */
>> +  /* The end of sequence marker is special.  We need to delete any
>> +     previous lines at the same PC, otherwise these lines may cause
>> +     problems since they might be at the same address as the following
>> +     function.  For instance suppose a function calls abort there is no
>> +     reason to emit a ret after that point (no joke).
>> +     So the label may be at the same address where the following
>> +     function begins.  There is also a fake end of sequence marker (-1)
>> +     that we emit internally when switching between different CUs
>> +     In this case, duplicate line table entries shall not be deleted.
>> +     We simply set the is_weak marker in this case.  */
> I don't understand why you changed so much this comment. Most of the original stuff (if not all of it) is still valid after your changes. Your version either doesn't fully explain the logic of the first "if" block, or it relies on too much specific knowledge and I no longer understand the explanation. Either way, I'd say more of the original comment should be kept.
> 

The change makes more sense together with the part 3 of the patch series.
Removing line table entries at the end of a compilation unit is ok,
but removing line table entries at the end of an inline function is not
really ok any more.

Part 3 will change the dwarf parser to use the end of sequence marker (0)
only at the real end of CU, where it is safe to delete line table entries
at the same address.
But in other cases it will use the "fake" end of sequence maker (-1) which
just sets the is_weak flag.


Thanks
Bernd.
  
Guinevere Larsen July 19, 2024, 12:44 p.m. UTC | #3
On 7/19/24 3:09 AM, Bernd Edlinger wrote:
> On 7/12/24 22:14, Guinevere Larsen wrote:
>> On 7/5/24 6:17 AM, Bernd Edlinger wrote:
>>> This introduces a new line table flag is_weak.
>>> The line entries at the end of a subroutine range,
>>> use this to indicate that they may possibly
>>> be part of the previous subroutine.
>>>
>>> When there is a sequence of line entries at the
>>> same address where an inline range ends, and the
>>> last item has is_stmt = 0, we force all previous
>>> items to have is_weak = 1.
>> This might be friday brain, but I'm having a hard time following the explanation. Could you add a small c code and line table example showing what you mean?
> Ok, here is the line-table from gdb.base/empty-inline.c, see part 3 of the
> patch series, line 18-21 is in the inline function, 36-39 in main.
> and line 29-31 in a function without an inline block.
>
> INDEX  LINE   REL-ADDRESS        UNREL-ADDRESS      IS-STMT IS-WEAK PROLOGUE-END EPILOGUE-BEGIN
> 0      36     0x0000555555555040 0x0000000000001040 Y       Y
> 1      36     0x0000555555555040 0x0000000000001040 Y       Y
> 2      18     0x0000555555555040 0x0000000000001040 Y       Y    <--+ same addr
> 3      20     0x0000555555555040 0x0000000000001040 Y       Y       ^ set is_weak
> 4      21     0x0000555555555040 0x0000000000001040 Y       Y       ^
> 5      36     0x0000555555555040 0x0000000000001040           <-----+ no is-stmt
> 6      37     0x000055555555504a 0x000000000000104a Y       Y
> 7      18     0x000055555555504a 0x000000000000104a Y       Y   <--+ same addr
> 8      20     0x000055555555504a 0x000000000000104a Y       Y      ^ set is_weak
> 9      21     0x000055555555504a 0x000000000000104a Y       Y      ^
> 10     37     0x000055555555504a 0x000000000000104a          <-----+ no is-stmt
> 11     38     0x000055555555504f 0x000000000000104f Y
> 12     39     0x000055555555504f 0x000000000000104f
> 13     END    0x0000555555555052 0x0000000000001052 Y
> 14     29     0x0000555555555150 0x0000000000001150 Y
> 15     30     0x0000555555555150 0x0000000000001150 Y
> 16     31     0x0000555555555150 0x0000000000001150 Y
> 17     31     0x0000555555555150 0x0000000000001150          <----- no inline here
> 18     32     0x0000555555555153 0x0000000000001153
> 19     END    0x0000555555555154 0x0000000000001154 Y
>
Ah yes, this line table helps a lot. please add at least a snippet of it 
in the commit message so its easier to follow once its committed.
>
>>> Additionally this adds a "fake" end sequence to the
>>> record_line function, that is line number -1.
>>> That will be used in the next patch.
>>>
>>> Finally this adds a handling for empty ranges to
>>> record_block_range.  Currently this function is
>>> not called with empty ranges, but that will be used
>>> in the next patch.
>>>
>>> There should be no functional changes after this commit.
>>> ---
>>>    gdb/buildsym.c                               | 106 ++++++++++++++++---
>>>    gdb/buildsym.h                               |   3 +
>>>    gdb/jit.c                                    |   1 +
>>>    gdb/symmisc.c                                |   6 +-
>>>    gdb/symtab.h                                 |   5 +
>>>    gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp |   2 +-
>>>    gdb/xcoffread.c                              |   1 +
>>>    7 files changed, 109 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
>>> index 1c762ad81bf..9bf2fff6e29 100644
>>> --- a/gdb/buildsym.c
>>> +++ b/gdb/buildsym.c
>>> @@ -413,6 +413,16 @@ buildsym_compunit::record_block_range (struct block *block,
>>>          || end_inclusive + 1 != block->end ())
>>>        m_pending_addrmap_interesting = true;
>>>    +  if (block->inlined_p ())
>>> +    {
>>> +      m_inline_end_vector.push_back (end_inclusive + 1);
>>> +      if (end_inclusive + 1 == start)
>>> +    {
>>> +      end_inclusive = start;
>>> +      m_pending_addrmap_interesting = true;
>>> +    }
>>> +    }
>>> +
>>>      m_pending_addrmap.set_empty (start, end_inclusive, block);
>>>    }
>>>    @@ -627,19 +637,16 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
>>>    {
>>>      m_have_line_numbers = true;
>>>    -  /* Normally, we treat lines as unsorted.  But the end of sequence
>>> -     marker is special.  We sort line markers at the same PC by line
>>> -     number, so end of sequence markers (which have line == 0) appear
>>> -     first.  This is right if the marker ends the previous function,
>>> -     and there is no padding before the next function.  But it is
>>> -     wrong if the previous line was empty and we are now marking a
>>> -     switch to a different subfile.  We must leave the end of sequence
>>> -     marker at the end of this group of lines, not sort the empty line
>>> -     to after the marker.  The easiest way to accomplish this is to
>>> -     delete any empty lines from our table, if they are followed by
>>> -     end of sequence markers.  All we lose is the ability to set
>>> -     breakpoints at some lines which contain no instructions
>>> -     anyway.  */
>>> +  /* The end of sequence marker is special.  We need to delete any
>>> +     previous lines at the same PC, otherwise these lines may cause
>>> +     problems since they might be at the same address as the following
>>> +     function.  For instance suppose a function calls abort there is no
>>> +     reason to emit a ret after that point (no joke).
>>> +     So the label may be at the same address where the following
>>> +     function begins.  There is also a fake end of sequence marker (-1)
>>> +     that we emit internally when switching between different CUs
>>> +     In this case, duplicate line table entries shall not be deleted.
>>> +     We simply set the is_weak marker in this case.  */
>> I don't understand why you changed so much this comment. Most of the original stuff (if not all of it) is still valid after your changes. Your version either doesn't fully explain the logic of the first "if" block, or it relies on too much specific knowledge and I no longer understand the explanation. Either way, I'd say more of the original comment should be kept.
>>
> The change makes more sense together with the part 3 of the patch series.
> Removing line table entries at the end of a compilation unit is ok,
> but removing line table entries at the end of an inline function is not
> really ok any more.
>
> Part 3 will change the dwarf parser to use the end of sequence marker (0)
> only at the real end of CU, where it is safe to delete line table entries
> at the same address.
> But in other cases it will use the "fake" end of sequence maker (-1) which
> just sets the is_weak flag.

This was going to be something I mentioned on patch 3, but since I'm 
sending this reply first, let me say it now.

It seems to me that you split your work along the line of "groundwork 
for A, B and C / actual fixes for A, B and C". In gdb, we prefer to do 
this only when the groundwork for a single fix is very complex and has a 
high chance of breaking existing functionality. It is usually better to 
split as "groundwork + fix for A / groundwork + fix for B / groundwork + 
fix for C".

Specifically, it sounds to me like fixing the linetable with the is_weak 
flag is a completely separate bit of work to handling stepping into 
0-range inline functions. Both help with debugging optimized code, so it 
makes sense to be a series, but splitting along the lines of each bug 
being fixed makes it easier for testing and reviewing, both now and in 
the future if something changes or a bug happens.
  

Patch

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 1c762ad81bf..9bf2fff6e29 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -413,6 +413,16 @@  buildsym_compunit::record_block_range (struct block *block,
       || end_inclusive + 1 != block->end ())
     m_pending_addrmap_interesting = true;
 
+  if (block->inlined_p ())
+    {
+      m_inline_end_vector.push_back (end_inclusive + 1);
+      if (end_inclusive + 1 == start)
+	{
+	  end_inclusive = start;
+	  m_pending_addrmap_interesting = true;
+	}
+    }
+
   m_pending_addrmap.set_empty (start, end_inclusive, block);
 }
 
@@ -627,19 +637,16 @@  buildsym_compunit::record_line (struct subfile *subfile, int line,
 {
   m_have_line_numbers = true;
 
-  /* Normally, we treat lines as unsorted.  But the end of sequence
-     marker is special.  We sort line markers at the same PC by line
-     number, so end of sequence markers (which have line == 0) appear
-     first.  This is right if the marker ends the previous function,
-     and there is no padding before the next function.  But it is
-     wrong if the previous line was empty and we are now marking a
-     switch to a different subfile.  We must leave the end of sequence
-     marker at the end of this group of lines, not sort the empty line
-     to after the marker.  The easiest way to accomplish this is to
-     delete any empty lines from our table, if they are followed by
-     end of sequence markers.  All we lose is the ability to set
-     breakpoints at some lines which contain no instructions
-     anyway.  */
+  /* The end of sequence marker is special.  We need to delete any
+     previous lines at the same PC, otherwise these lines may cause
+     problems since they might be at the same address as the following
+     function.  For instance suppose a function calls abort there is no
+     reason to emit a ret after that point (no joke).
+     So the label may be at the same address where the following
+     function begins.  There is also a fake end of sequence marker (-1)
+     that we emit internally when switching between different CUs
+     In this case, duplicate line table entries shall not be deleted.
+     We simply set the is_weak marker in this case.  */
   if (line == 0)
     {
       std::optional<int> last_line;
@@ -659,15 +666,84 @@  buildsym_compunit::record_line (struct subfile *subfile, int line,
       if (!last_line.has_value () || *last_line == 0)
 	return;
     }
+  else if (line == -1)
+    {
+      line = 0;
+      auto e = subfile->line_vector_entries.end ();
+      while (e > subfile->line_vector_entries.begin ())
+	{
+	  e--;
+	  if (e->unrelocated_pc () != pc)
+	    break;
+	  e->is_weak = 1;
+	}
+    }
 
   linetable_entry &e = subfile->line_vector_entries.emplace_back ();
   e.line = line;
   e.is_stmt = (flags & LEF_IS_STMT) != 0;
+  e.is_weak = false;
   e.set_unrelocated_pc (pc);
   e.prologue_end = (flags & LEF_PROLOGUE_END) != 0;
   e.epilogue_begin = (flags & LEF_EPILOGUE_BEGIN) != 0;
 }
 
+
+/* Patch the is_stmt bits at the given inline end address.
+   The line table has to be already sorted.  */
+
+static void
+patch_inline_end_pos (struct subfile *subfile, struct objfile *objfile,
+		      CORE_ADDR end)
+{
+  std::vector<linetable_entry> &items = subfile->line_vector_entries;
+  int a = 2, b = items.size () - 1;
+
+  /* We need at least two items with pc = end in the table.
+     The lowest usable items are at pos 0 and 1, the highest
+     usable items are at pos b - 2 and b - 1.  */
+  if (a > b
+      || end < items[1].pc (objfile)
+      || end > items[b - 2].pc (objfile))
+    return;
+
+  /* Look for the first item with pc > end in the range [a,b].
+     The previous element has pc = end or there is no match.
+     We set a = 2, since we need at least two consecutive elements
+     with pc = end to do anything useful.
+     We set b = items.size () - 1, since we are not interested
+     in the last element which should be an end of sequence
+     marker with line = 0 and is_stmt = true.  */
+  while (a < b)
+    {
+      int c = (a + b) / 2;
+
+      if (end < items[c].pc (objfile))
+	b = c;
+      else
+	a = c + 1;
+    }
+
+  a--;
+  if (items[a].pc (objfile) != end || items[a].is_stmt)
+    return;
+
+  /* When there is a sequence of line entries at the same address
+     where an inline range ends, and the last item has is_stmt = 0,
+     we force all previous items to have is_weak = true as well.  */
+  do
+    {
+      /* We stop at the first line entry with a different address,
+	 or when we see an end of sequence marker.  */
+      a--;
+      if (items[a].pc (objfile) != end || items[a].line == 0)
+	break;
+
+      items[a].is_weak = true;
+    }
+  while (a > 0);
+}
+
 
 /* Subroutine of end_compunit_symtab to simplify it.  Look for a subfile that
    matches the main source file's basename.  If there is only one, and
@@ -892,6 +968,10 @@  buildsym_compunit::end_compunit_symtab_with_blockvector
 	     relationships, this is why std::stable_sort is used.  */
 	  std::stable_sort (subfile->line_vector_entries.begin (),
 			    subfile->line_vector_entries.end ());
+
+	  for (int i = 0; i < m_inline_end_vector.size (); i++)
+	    patch_inline_end_pos (subfile, m_objfile,
+				  m_inline_end_vector[i]);
 	}
 
       /* Allocate a symbol table if necessary.  */
diff --git a/gdb/buildsym.h b/gdb/buildsym.h
index c1eed247d25..edf76c8b17c 100644
--- a/gdb/buildsym.h
+++ b/gdb/buildsym.h
@@ -446,6 +446,9 @@  struct buildsym_compunit
 
   /* Pending symbols that are local to the lexical context.  */
   struct pending *m_local_symbols = nullptr;
+
+  /* Pending inline end range addresses.  */
+  std::vector<CORE_ADDR> m_inline_end_vector;
 };
 
 
diff --git a/gdb/jit.c b/gdb/jit.c
index 797be95a8da..2f714268920 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -495,6 +495,7 @@  jit_symtab_line_mapping_add_impl (struct gdb_symbol_callbacks *cb,
 	(unrelocated_addr (map[i].pc));
       stab->linetable->item[i].line = map[i].line;
       stab->linetable->item[i].is_stmt = true;
+      stab->linetable->item[i].is_weak = false;
     }
 }
 
diff --git a/gdb/symmisc.c b/gdb/symmisc.c
index b4e0360041e..567ec270f3c 100644
--- a/gdb/symmisc.c
+++ b/gdb/symmisc.c
@@ -265,6 +265,8 @@  dump_symtab_1 (struct symtab *symtab, struct ui_file *outfile)
 	  gdb_puts (paddress (gdbarch, l->item[i].pc (objfile)), outfile);
 	  if (l->item[i].is_stmt)
 	    gdb_printf (outfile, "\t(stmt)");
+	  if (l->item[i].is_weak)
+	    gdb_printf (outfile, "\t(weak)");
 	  gdb_printf (outfile, "\n");
 	}
     }
@@ -973,12 +975,13 @@  maintenance_print_one_line_table (struct symtab *symtab, void *data)
       /* Leave space for 6 digits of index and line number.  After that the
 	 tables will just not format as well.  */
       struct ui_out *uiout = current_uiout;
-      ui_out_emit_table table_emitter (uiout, 7, -1, "line-table");
+      ui_out_emit_table table_emitter (uiout, 8, -1, "line-table");
       uiout->table_header (6, ui_left, "index", _("INDEX"));
       uiout->table_header (6, ui_left, "line", _("LINE"));
       uiout->table_header (18, ui_left, "rel-address", _("REL-ADDRESS"));
       uiout->table_header (18, ui_left, "unrel-address", _("UNREL-ADDRESS"));
       uiout->table_header (7, ui_left, "is-stmt", _("IS-STMT"));
+      uiout->table_header (7, ui_left, "is-weak", _("IS-WEAK"));
       uiout->table_header (12, ui_left, "prologue-end", _("PROLOGUE-END"));
       uiout->table_header (14, ui_left, "epilogue-begin", _("EPILOGUE-BEGIN"));
       uiout->table_body ();
@@ -999,6 +1002,7 @@  maintenance_print_one_line_table (struct symtab *symtab, void *data)
 	  uiout->field_core_addr ("unrel-address", objfile->arch (),
 				  CORE_ADDR (item->unrelocated_pc ()));
 	  uiout->field_string ("is-stmt", item->is_stmt ? "Y" : "");
+	  uiout->field_string ("is-weak", item->is_weak ? "Y" : "");
 	  uiout->field_string ("prologue-end", item->prologue_end ? "Y" : "");
 	  uiout->field_string ("epilogue-begin", item->epilogue_begin ? "Y" : "");
 	  uiout->text ("\n");
diff --git a/gdb/symtab.h b/gdb/symtab.h
index a5631a27b5e..9b2b297fb3a 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1656,6 +1656,9 @@  struct linetable_entry
   /* True if this PC is a good location to place a breakpoint for LINE.  */
   bool is_stmt : 1;
 
+  /* True if this PC is at a subroutine range end.  */
+  bool is_weak : 1;
+
   /* True if this location is a good location to place a breakpoint after a
      function prologue.  */
   bool prologue_end : 1;
@@ -2412,6 +2415,8 @@  struct symtab_and_line
   /* If the line number information is valid, then this indicates if this
      line table entry had the is-stmt flag set or not.  */
   bool is_stmt = false;
+  /* True if this PC is at a subroutine range end.  */
+  bool is_weak = false;
 
   /* The probe associated with this symtab_and_line.  */
   probe *prob = NULL;
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
index 1a3d53c2116..403bc6b3f72 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
@@ -168,7 +168,7 @@  gdb_test_multiple "maint info line-table gdb.dwarf2/dw2-ranges-base.c" \
 	-re ".*linetable: \\(\\(struct linetable \\*\\) 0x0\\):\r\nNo line table.\r\n" {
 	    exp_continue
 	}
-	-re ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+REL-ADDRESS\[ \t\]+UNREL-ADDRESS\[ \t\]+IS-STMT\[ \t\]PROLOGUE-END\[ \t\]EPILOGUE-BEGIN *\r\n" {
+	-re ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+REL-ADDRESS\[ \t\]+UNREL-ADDRESS\[ \t\]+IS-STMT\[ \t\]IS-WEAK\[ \t\]PROLOGUE-END\[ \t\]EPILOGUE-BEGIN *\r\n" {
 	    exp_continue
 	}
     }
diff --git a/gdb/xcoffread.c b/gdb/xcoffread.c
index 639dd5b8adc..1642bc12dfb 100644
--- a/gdb/xcoffread.c
+++ b/gdb/xcoffread.c
@@ -431,6 +431,7 @@  arrange_linetable (std::vector<linetable_entry> &old_linetable)
 	  linetable_entry &e = fentries.emplace_back ();
 	  e.line = ii;
 	  e.is_stmt = true;
+	  e.is_weak = false;
 	  e.set_unrelocated_pc (old_linetable[ii].unrelocated_pc ());
 	}
     }