diff --git a/gdb/block.c b/gdb/block.c
index 511689c9738..d9c4435c9e2 100644
--- a/gdb/block.c
+++ b/gdb/block.c
@@ -197,7 +197,20 @@ blockvector_for_pc_sect (CORE_ADDR pc, struct obj_section *section,
     return NULL;
 
   if (pblock)
-    *pblock = b;
+    {
+      struct symtab_and_line sal = find_pc_sect_line (pc, section, 0);
+      if (sal.line != 0 && sal.pc == pc && sal.is_weak)
+	{
+	  const struct block *b2 = find_block_in_blockvector (bl, pc - 1);
+	  const struct block *b0 = b;
+	  while (b0->superblock () && !b0->function ())
+	    b0 = b0->superblock ();
+	  if (b0->contains (b2))
+	    b = b2;
+	}
+      *pblock = b;
+    }
+
   return bl;
 }
 
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 60fd8b45eb5..e40679611fe 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -10733,10 +10733,6 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  return false;
 	}
 
-      /* Empty range entries have no effect.  */
-      if (range_beginning == range_end)
-	continue;
-
       /* Only DW_RLE_offset_pair needs the base address added.  */
       if (rlet == DW_RLE_offset_pair)
 	{
@@ -10855,10 +10851,6 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu, dwarf_tag tag,
 	  return 0;
 	}
 
-      /* Empty range entries have no effect.  */
-      if (range_beginning == range_end)
-	continue;
-
       range_beginning = (unrelocated_addr) ((CORE_ADDR) range_beginning
 					    + (CORE_ADDR) *base);
       range_end = (unrelocated_addr) ((CORE_ADDR) range_end
@@ -11080,8 +11072,8 @@ dwarf2_get_pc_bounds (struct die_info *die, unrelocated_addr *lowpc,
   if (ret == PC_BOUNDS_NOT_PRESENT || ret == PC_BOUNDS_INVALID)
     return ret;
 
-  /* partial_die_info::read has also the strict LOW < HIGH requirement.  */
-  if (high <= low)
+  /* partial_die_info::read has also the same low < high requirement.  */
+  if (low > high || (low == high && die->tag != DW_TAG_inlined_subroutine))
     return PC_BOUNDS_INVALID;
 
   /* When using the GNU linker, .gnu.linkonce. sections are used to
@@ -18027,21 +18019,9 @@ class lnp_state_machine
 
   /* Additional bits of state we need to track.  */
 
-  /* The last file that we called dwarf2_start_subfile for.
-     This is only used for TLLs.  */
-  unsigned int m_last_file = 0;
   /* The last file a line number was recorded for.  */
   struct subfile *m_last_subfile = NULL;
 
-  /* The address of the last line entry.  */
-  unrelocated_addr m_last_address;
-
-  /* Set to true when a previous line at the same address (using
-     m_last_address) had LEF_IS_STMT set in m_flags.  This is reset to false
-     when a line entry at a new address (m_address different to
-     m_last_address) is processed.  */
-  bool m_stmt_at_address = false;
-
   /* When true, record the lines we decode.  */
   bool m_currently_recording_lines = true;
 
@@ -18199,7 +18179,8 @@ dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
 
 static void
 dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
-		   unrelocated_addr address, struct dwarf2_cu *cu)
+		   unrelocated_addr address, struct dwarf2_cu *cu,
+		   bool end_sequence)
 {
   if (subfile == NULL)
     return;
@@ -18212,7 +18193,8 @@ dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
 		  paddress (gdbarch, (CORE_ADDR) address));
     }
 
-  dwarf_record_line_1 (gdbarch, subfile, 0, address, LEF_IS_STMT, cu);
+  dwarf_record_line_1 (gdbarch, subfile, end_sequence ? 0 : -1, address,
+		       LEF_IS_STMT, cu);
 }
 
 void
@@ -18240,38 +18222,17 @@ lnp_state_machine::record_line (bool end_sequence)
   /* For now we ignore lines not starting on an instruction boundary.
      But not when processing end_sequence for compatibility with the
      previous version of the code.  */
-  else if (m_op_index == 0 || end_sequence)
-    {
-      /* When we switch files we insert an end maker in the first file,
-	 switch to the second file and add a new line entry.  The
-	 problem is that the end marker inserted in the first file will
-	 discard any previous line entries at the same address.  If the
-	 line entries in the first file are marked as is-stmt, while
-	 the new line in the second file is non-stmt, then this means
-	 the end marker will discard is-stmt lines so we can have a
-	 non-stmt line.  This means that there are less addresses at
-	 which the user can insert a breakpoint.
-
-	 To improve this we track the last address in m_last_address,
-	 and whether we have seen an is-stmt at this address.  Then
-	 when switching files, if we have seen a stmt at the current
-	 address, and we are switching to create a non-stmt line, then
-	 discard the new line.  */
-      bool file_changed
-	= m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
-      bool ignore_this_line
-	= ((file_changed && !end_sequence && m_last_address == m_address
-	    && ((m_flags & LEF_IS_STMT) == 0)
-	    && m_stmt_at_address)
-	   || (!end_sequence && m_line == 0));
-
-      if ((file_changed && !ignore_this_line) || end_sequence)
+  else if ((m_op_index == 0 && m_line != 0) || end_sequence)
+    {
+      if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
+	  || end_sequence)
 	{
 	  dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
-			     m_currently_recording_lines ? m_cu : nullptr);
+			     m_currently_recording_lines ? m_cu : nullptr,
+			     end_sequence || (m_flags & LEF_IS_STMT) != 0);
 	}
 
-      if (!end_sequence && !ignore_this_line)
+      if (!end_sequence)
 	{
 	  linetable_entry_flags lte_flags = m_flags;
 	  if (producer_is_codewarrior (m_cu))
@@ -18291,15 +18252,6 @@ lnp_state_machine::record_line (bool end_sequence)
 	  m_last_line = m_line;
 	}
     }
-
-  /* Track whether we have seen any IS_STMT true at m_address in case we
-     have multiple line table entries all at m_address.  */
-  if (m_last_address != m_address)
-    {
-      m_stmt_at_address = false;
-      m_last_address = m_address;
-    }
-  m_stmt_at_address |= (m_flags & LEF_IS_STMT) != 0;
 }
 
 lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
@@ -18313,8 +18265,7 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
        This is currently used by MIPS code,
        cf. `mips_adjust_dwarf2_line'.  */
     m_address ((unrelocated_addr) gdbarch_adjust_dwarf2_line (arch, 0, 0)),
-    m_flags (lh->default_is_stmt ? LEF_IS_STMT : (linetable_entry_flags) 0),
-    m_last_address (m_address)
+    m_flags (lh->default_is_stmt ? LEF_IS_STMT : (linetable_entry_flags) 0)
 {
 }
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 71514d5ba66..7598c9e3032 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -996,7 +996,8 @@ prepare_one_step (thread_info *tp, struct step_command_fsm *sm)
 	      if (sym->aclass () == LOC_BLOCK)
 		{
 		  const block *block = sym->value_block ();
-		  if (block->end () < tp->control.step_range_end)
+		  if (block->end () < tp->control.step_range_end
+		      && block->end () > tp->control.step_range_start)
 		    tp->control.step_range_end = block->end ();
 		}
 	    }
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 1f32a63ad54..097b42c4e92 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -8114,6 +8114,31 @@ process_event_stop_test (struct execution_control_state *ecs)
       return;
     }
 
+  /* Handle the case when subroutines have multiple ranges.
+     When we step from one part to the next part of the same subroutine,
+     all subroutine levels are skipped again which begin here.
+     Compensate for this by removing all skipped subroutines,
+     which were already executing from the user's perspective.  */
+
+  if (get_stack_frame_id (frame)
+      == ecs->event_thread->control.step_stack_frame_id
+      && inline_skipped_frames (ecs->event_thread)
+      && ecs->event_thread->control.step_frame_id.artificial_depth > 0
+      && ecs->event_thread->control.step_frame_id.code_addr_p)
+    {
+      const struct block *prev, *curr;
+      int depth = 0;
+      prev = block_for_pc (ecs->event_thread->control.step_frame_id.code_addr);
+      curr = block_for_pc (ecs->event_thread->stop_pc ());
+      while (curr && curr->inlined_p () && !curr->contains (prev))
+	{
+	  depth ++;
+	  curr = curr->superblock ();
+	}
+      while (inline_skipped_frames (ecs->event_thread) > depth)
+	step_into_inline_frame (ecs->event_thread);
+    }
+
   /* Look for "calls" to inlined functions, part one.  If the inline
      frame machinery detected some skipped call sites, we have entered
      a new inline function.  */
@@ -8172,6 +8197,8 @@ process_event_stop_test (struct execution_control_state *ecs)
       infrun_debug_printf ("stepping through inlined function");
 
       if (ecs->event_thread->control.step_over_calls == STEP_OVER_ALL
+	  || ecs->event_thread->stop_pc () != stop_pc_sal.pc
+	  || !stop_pc_sal.is_stmt
 	  || inline_frame_is_marked_for_skip (false, ecs->event_thread))
 	keep_going (ecs);
       else
@@ -8220,7 +8247,8 @@ process_event_stop_test (struct execution_control_state *ecs)
 	  end_stepping_range (ecs);
 	  return;
 	}
-      else if (*curr_frame_id == original_frame_id)
+      else if (get_stack_frame_id (frame)
+	       == ecs->event_thread->control.step_stack_frame_id)
 	{
 	  /* We are not at the start of a statement, and we have not changed
 	     frame.
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 41d71beec2a..957fe971d3c 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3290,7 +3290,10 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
 	 0) instead of a real line.  */
 
       if (prev && prev->line
-	  && (!best || prev->unrelocated_pc () > best->unrelocated_pc ()))
+	  && (!best || prev->unrelocated_pc () > best->unrelocated_pc ()
+		    || (prev->unrelocated_pc () == best->unrelocated_pc ()
+			&& (best->pc (objfile) == pc
+			    ? !best->is_stmt : best->is_weak))))
 	{
 	  best = prev;
 	  best_symtab = iter_s;
@@ -3309,7 +3312,7 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
 		     && (tmp - 1)->unrelocated_pc () == tmp->unrelocated_pc ()
 		     && (tmp - 1)->line != 0 && !tmp->is_stmt)
 		--tmp;
-	      if (tmp->is_stmt)
+	      if (tmp->is_stmt && (tmp->pc (objfile) == pc || !tmp->is_weak))
 		best = tmp;
 	    }
 
@@ -3333,18 +3336,14 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
 	 We used to return alt->line - 1 here, but that could be
 	 anywhere; if we don't have line number info for this PC,
 	 don't make some up.  */
-      val.pc = pc;
-    }
-  else if (best->line == 0)
-    {
-      /* If our best fit is in a range of PC's for which no line
-	 number info is available (line number is zero) then we didn't
-	 find any valid line information.  */
+      if (notcurrent)
+	pc++;
       val.pc = pc;
     }
   else
     {
       val.is_stmt = best->is_stmt;
+      val.is_weak = best->is_weak;
       val.symtab = best_symtab;
       val.line = best->line;
       val.pc = best->pc (objfile);
diff --git a/gdb/testsuite/gdb.base/empty-inline.c b/gdb/testsuite/gdb.base/empty-inline.c
new file mode 100644
index 00000000000..4ecb3ff14a3
--- /dev/null
+++ b/gdb/testsuite/gdb.base/empty-inline.c
@@ -0,0 +1,39 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2024 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+static int test0 (void)
+{
+  asm (""); /* line 20 */
+  return 1; /* line 21 */
+}
+
+int __attribute__((noinline, noclone))
+#ifdef __CET__
+  __attribute__((nocf_check))
+#endif
+test1 (int x)
+{
+  asm ("");
+  return x+1; /* line 31 */
+}
+
+int
+main()
+{ test1 (test0 ()); /* line 36 */
+  test1 (test0 ()); /* line 37 */
+  return 0;         /* line 38 */
+}
diff --git a/gdb/testsuite/gdb.base/empty-inline.exp b/gdb/testsuite/gdb.base/empty-inline.exp
new file mode 100644
index 00000000000..0d9cfe922cf
--- /dev/null
+++ b/gdb/testsuite/gdb.base/empty-inline.exp
@@ -0,0 +1,56 @@
+# Copyright 2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+standard_testfile .c
+
+if [get_compiler_info] {
+    return -1
+}
+
+if { [test_compiler_info gcc*] && ![supports_statement_frontiers] } {
+    return -1
+}
+
+global srcfile testfile
+
+set options {debug nowarnings optimize=-O2}
+if { [supports_statement_frontiers] } {
+    lappend options additional_flags=-gstatement-frontiers
+}
+
+if { [prepare_for_testing "failed to prepare" $binfile \
+      $srcfile $options] } {
+    return -1
+}
+
+if ![runto_main] {
+    return
+}
+
+gdb_test "frame 0" "\\s*\\#0\\s+main.*${srcfile}:36.*" "in main"
+gdb_test_multiple "step" "step into test0" {
+    -re ".*test0.*${srcfile}:20.*$::gdb_prompt $" {
+	gdb_test "step" ".*line 21.*" $gdb_test_name
+    }
+    -re ".*test0.*${srcfile}:21.*$::gdb_prompt $" {
+	pass $gdb_test_name
+    }
+}
+gdb_test "frame 1" "\\s*\\#1\\s+main.*${srcfile}:36.*" "frame1"
+gdb_test "step" ".*test1.*${srcfile}:31.*" "step into test1"
+gdb_test "frame 1" "\\s*\\#1.*in main.*${srcfile}:36.*" "frame2"
+gdb_test "step" ".*main.*${srcfile}:37.*" "step back to main"
+gdb_test "next" ".*return 0;.*" "step over test0+1"
+gdb_test "frame 0" "\\s*\\#0\\s+main.*${srcfile}:38.*" "in main again"
diff --git a/gdb/testsuite/gdb.cp/empty-inline.cc b/gdb/testsuite/gdb.cp/empty-inline.cc
new file mode 100644
index 00000000000..a960d5f7ec0
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/empty-inline.cc
@@ -0,0 +1,33 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2024 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* PR 25987 */
+struct MyClass;
+struct ptr {
+    MyClass* get() { return t; }     /* line 21 */
+    MyClass* t;
+};
+struct MyClass { void call(); };
+void MyClass::call() {
+    *(volatile char*)-1 = 1;         /* line 26 */
+}
+static void intermediate(ptr p) {
+    p.get()->call();                 /* line 29 */
+}
+int main() {
+    intermediate(ptr{new MyClass});
+}
diff --git a/gdb/testsuite/gdb.cp/empty-inline.exp b/gdb/testsuite/gdb.cp/empty-inline.exp
new file mode 100644
index 00000000000..7848c963f59
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/empty-inline.exp
@@ -0,0 +1,50 @@
+# Copyright 2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# PR 25987
+standard_testfile .cc
+
+if {![supports_statement_frontiers]} {
+    return -1
+}
+
+set options {c++ debug nowarnings optimize=-Og}
+lappend options additional_flags=-gstatement-frontiers
+if { [prepare_for_testing "failed to prepare" $testfile \
+      $srcfile $options] } {
+    return -1
+}
+
+if ![runto_main] {
+    return
+}
+
+gdb_test "bt" "\\s*\\#0\\s+main.*" "in main"
+#break at the empty inline function ptr::get
+gdb_test "b get" ".*" "break at get"
+gdb_test "c" ".*" "continue to get"
+#call frame 1 is at line 29
+gdb_test "bt" [multi_line "\\s*\\#0\\s+ptr::get\[^\r\]*${srcfile}:21" \
+			  "\\s*\\#1\\s+intermediate\[^\r\]*${srcfile}:29" \
+			  ".*"] \
+	      "at get"
+#print a local value here
+gdb_test "p t" ".*(\\\$1 = \\(MyClass \\*\\) 0x|value has been optimized out).*" "print t"
+gdb_test "c" ".*SIGSEGV.*" "continue to SIGSEGV"
+#call frame 1 is at line 29
+gdb_test "bt" [multi_line "\\s*\\#0\\s+\[^\r\]*MyClass::call\[^\r\]*${srcfile}:26" \
+			  "\\s*\\#1\\s+0x\[^\r\]*intermediate\[^\r\]*${srcfile}:29" \
+			  ".*"] \
+	      "at call"
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.cc b/gdb/testsuite/gdb.cp/step-and-next-inline.cc
index ac92206fd7f..7f8ead878ec 100644
--- a/gdb/testsuite/gdb.cp/step-and-next-inline.cc
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.cc
@@ -47,8 +47,7 @@ tree_check (tree *t, int i)
 
 int __attribute__((noinline, noclone))
 get_alias_set (tree *t)
-{
-  if (t != NULL
+{ if (t != NULL
       && TREE_TYPE (t).z != 1
       && TREE_TYPE (t).z != 2
       && TREE_TYPE (t).z != 3)
@@ -60,7 +59,6 @@ tree xx;
 
 int
 main()
-{
-  get_alias_set (&xx);  /* Beginning of main */
+{ get_alias_set (&xx);
   return 0;
 } // main
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
index 446cd825947..c9fa77f98e0 100644
--- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
@@ -15,7 +15,7 @@
 
 standard_testfile .cc
 
-if {[test_compiler_info gcc*] && ![supports_statement_frontiers] } {
+if { [test_compiler_info gcc*] && ![supports_statement_frontiers] } {
     return -1
 }
 
@@ -24,13 +24,6 @@ if {[test_compiler_info gcc*] && ![supports_statement_frontiers] } {
 proc do_test { use_header } {
     global srcfile testfile
 
-    if { $use_header } {
-	# This test will not pass due to poor debug information
-	# generated by GCC (at least upto 10.x).  See
-	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
-	return
-    }
-
     set options {c++ debug nowarnings optimize=-O2}
     if { [supports_statement_frontiers] } {
 	lappend options additional_flags=-gstatement-frontiers
@@ -53,128 +46,29 @@ proc do_test { use_header } {
 
     with_test_prefix $prefix {
 
-    set main_location [gdb_get_line_number "Beginning of main" $srcfile]
-
-    if ![runto $main_location qualified] {
+    if ![runto_main] {
 	return
     }
 
     gdb_test "bt" "\\s*\\#0\\s+main.*" "in main"
-    set line1 {\t\{}
-    set line2 {\t  if \(t != NULL}
-    gdb_test_multiple "step" "step into get_alias_set" {
-	-re -wrap $line1 {
-	    gdb_test "next" $line2 $gdb_test_name
-	}
-	-re -wrap $line2 {
-	    pass $gdb_test_name
-	}
-    }
+    gdb_test "step" ".*" "step into get_alias_set"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 1"
-
-    # It's possible that this first failure (when not using a header
-    # file) is GCC's fault, though the remaining failures would best
-    # be fixed by adding location views support (though it could be
-    # that some easier heuristic could be figured out).  Still, it is
-    # not certain that the first failure wouldn't also be fixed by
-    # having location view support, so for now it is tagged as such.
-    set have_kfail [expr [test_compiler_info gcc*] && !$use_header]
-
-    set ok 1
-    gdb_test_multiple "next" "next step 1" {
-	-re -wrap "if \\(t->x != i\\)" {
-	    set ok 0
-	    send_gdb "next\n"
-	    exp_continue
-	}
-	-re -wrap ".*TREE_TYPE.* != 1" {
-	    if { $ok } {
-		pass $gdb_test_name
-	    } else {
-		if { $have_kfail } {
-		    setup_kfail "*-*-*" symtab/25507
-		}
-		fail $gdb_test_name
-	    }
-	}
-    }
+    gdb_test "next" ".*TREE_TYPE.*" "next step 1"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 2"
-
-    set ok 1
-    gdb_test_multiple "next" "next step 2" {
-	-re -wrap "return x;" {
-	    set ok 0
-	    send_gdb "next\n"
-	    exp_continue
-	}
-	-re -wrap ".*TREE_TYPE.* != 2" {
-	    if { $ok } {
-		pass $gdb_test_name
-	    } else {
-		if { $have_kfail } {
-		    setup_kfail "*-*-*" symtab/25507
-		}
-		fail $gdb_test_name
-	    }
-	}
-    }
+    gdb_test "next" ".*TREE_TYPE.*" "next step 2"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 3"
-
-    set ok 1
-    gdb_test_multiple "next" "next step 3" {
-	-re -wrap "return x;" {
-	    set ok 0
-	    send_gdb "next\n"
-	    exp_continue
-	}
-	-re -wrap ".*TREE_TYPE.* != 3\\)" {
-	    if { $ok } {
-		pass $gdb_test_name
-	    } else {
-		if { $have_kfail } {
-		    setup_kfail "*-*-*" symtab/25507
-		}
-		fail $gdb_test_name
-	    }
-	}
-    }
+    gdb_test "next" ".*TREE_TYPE.*" "next step 3"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 4"
-
-    set ok 1
-    gdb_test_multiple "next" "next step 4" {
-	-re -wrap "(if \\(t != NULL|\} // get_alias_set)" {
-	    send_gdb "next\n"
-	    exp_continue
-	}
-	-re -wrap "return x;" {
-	    set ok 0
-	    send_gdb "next\n"
-	    exp_continue
-	}
-	-re -wrap "return 0.*" {
-	    if { $ok } {
-		pass $gdb_test_name
-	    } else {
-		if { $have_kfail } {
-		    setup_kfail "*-*-*" symtab/25507
-		}
-		fail $gdb_test_name
-	    }
-	}
-    }
+    gdb_test "next" ".*" "next step 4"
     gdb_test "bt" \
 	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
 	"not in inline 5"
 
-    if {!$use_header} {
-	# With the debug from GCC 10.x (and earlier) GDB is currently
-	# unable to successfully complete the following tests when we
-	# are not using a header file.
-	kfail symtab/25507 "stepping tests"
+    if ![test_compiler_info gcc*] {
 	return
     }
 
@@ -194,22 +88,84 @@ proc do_test { use_header } {
     gdb_test "step" ".*if \\(t->x != i\\).*" "step 2"
     gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
 	"in inline 1 pass 2"
-    gdb_test "step" ".*TREE_TYPE.*" "step 3"
+    gdb_test "step" ".*return x.*" "step 3"
+    gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
+	"return from inline 1 pass 2"
+    gdb_test "step" ".*TREE_TYPE.*" "step 4"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 2 pass 2"
-    gdb_test "step" ".*if \\(t->x != i\\).*" "step 4"
+    gdb_test "step" ".*if \\(t->x != i\\).*" "step 5"
     gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
 	"in inline 2 pass 2"
-    gdb_test "step" ".*TREE_TYPE.*" "step 5"
+    gdb_test "step" ".*return x.*" "step 6"
+    gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
+	"return from inline 2 pass 2"
+    gdb_test "step" ".*TREE_TYPE.*" "step 7"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 3 pass 2"
-    gdb_test "step" ".*if \\(t->x != i\\).*" "step 6"
+    gdb_test "step" ".*if \\(t->x != i\\).*" "step 8"
     gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
 	"in inline 3 pass 2"
-    gdb_test "step" "return 0.*" "step 7"
+    gdb_test "step" ".*return x.*" "step 9"
+    gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
+	"return from inline 3 pass 2"
+    gdb_test "step" "return 0.*" "step 10"
     gdb_test "bt" \
 	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
 	"not in inline 4 pass 2"
+
+    clean_restart ${executable}
+
+    if ![runto_main] {
+	return
+    }
+
+    gdb_test "bt" "\\s*\\#0\\s+main.*" "in main pass 3"
+    gdb_test "step" ".*" "step into get_alias_set pass 3"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"in get_alias_set pass 3"
+    gdb_test "step" ".*TREE_TYPE.*" "step 1 pass 3"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 1 pass 3"
+    gdb_test "step" ".*if \\(t->x != i\\).*" "step 2 pass 3"
+    gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
+	"in inline 1 pass 3"
+    gdb_test_multiple "p t->x = 2" "change value pass 3" {
+	-re ".*value has been optimized out.*$::gdb_prompt $" {
+	    gdb_test "p xx.x = 2" ".* = 2.*" $gdb_test_name
+	}
+	-re ".* = 2.*$::gdb_prompt $" {
+	    pass $gdb_test_name
+	}
+    }
+    gdb_test "step" ".*abort.*" "step 3, pass 3"
+    gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
+	"abort from inline 1 pass 3"
+
+    clean_restart ${executable}
+
+    if ![runto_main] {
+	return
+    }
+
+    gdb_test "bt" "\\s*\\#0\\s+main.*" "in main pass 4"
+    gdb_test "skip tree_check" ".*" "skip tree_check pass 4"
+    gdb_test "step" ".*" "step into get_alias_set pass 4"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"in get_alias_set pass 4"
+    gdb_test "step" ".*TREE_TYPE.*" "step 1 pass 4"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 1 pass 4"
+    gdb_test "step" ".*TREE_TYPE.*" "step 2 pass 4"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 2 pass 4"
+    gdb_test "step" ".*TREE_TYPE.*" "step 3 pass 4"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 3 pass 4"
+    gdb_test "step" "return 0.*" "step 4 pass 4"
+    gdb_test "bt" \
+	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
+	"not in inline 4 pass 4"
     }
 }
 
