* arm-linux-tdep.c (arm_linux_thumb2_be_breakpoint)
authorDaniel Jacobowitz <drow@false.org>
Mon, 1 Feb 2010 16:13:17 +0000 (16:13 +0000)
committerDaniel Jacobowitz <drow@false.org>
Mon, 1 Feb 2010 16:13:17 +0000 (16:13 +0000)
(arm_linux_thumb2_le_breakpoint): New constants.
(arm_linux_init_abi): Set thumb2_breakpoint and
thumb2_breakpoint_size.
* arm-tdep.c (thumb_insn_size, thumb_advance_itstate): New functions.
(thumb_get_next_pc): Add a comment.  Rename IT to ITSTATE.
Implement support for single stepping through IT blocks if
a 32-bit Thumb breakpoint instruction is available.
(arm_breakpoint_from_pc): If a 32-bit Thumb breakpoint instruction
is available, use it when needed.
(arm_remote_breakpoint_from_pc): New function.
(arm_gdbarch_init): Register arm_remote_breakpoint_from_pc.
* arm-tdep.h (struct gdbarch_tdep): Correct thumb_breakpoint
comment.  Add thumb2_breakpoint and thumb2_breakpoint_size.

gdbserver/
* linux-arm-low.c (thumb_breakpoint_len): Delete.
(thumb2_breakpoint): New.
(arm_breakpoint_at): Check for Thumb-2 breakpoints.

testsuite/
* gdb.arch/thumb2-it.S, gdb.arch/thumb2-it.exp: New files.

gdb/ChangeLog
gdb/arm-linux-tdep.c
gdb/arm-tdep.c
gdb/arm-tdep.h
gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-arm-low.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.arch/thumb2-it.S [new file with mode: 0644]
gdb/testsuite/gdb.arch/thumb2-it.exp [new file with mode: 0644]

index ee0a8f3..72866ca 100644 (file)
@@ -1,5 +1,22 @@
 2010-02-01  Daniel Jacobowitz  <dan@codesourcery.com>
 
+       * arm-linux-tdep.c (arm_linux_thumb2_be_breakpoint)
+       (arm_linux_thumb2_le_breakpoint): New constants.
+       (arm_linux_init_abi): Set thumb2_breakpoint and
+       thumb2_breakpoint_size.
+       * arm-tdep.c (thumb_insn_size, thumb_advance_itstate): New functions.
+       (thumb_get_next_pc): Add a comment.  Rename IT to ITSTATE.
+       Implement support for single stepping through IT blocks if
+       a 32-bit Thumb breakpoint instruction is available.
+       (arm_breakpoint_from_pc): If a 32-bit Thumb breakpoint instruction
+       is available, use it when needed.
+       (arm_remote_breakpoint_from_pc): New function.
+       (arm_gdbarch_init): Register arm_remote_breakpoint_from_pc.
+       * arm-tdep.h (struct gdbarch_tdep): Correct thumb_breakpoint
+       comment.  Add thumb2_breakpoint and thumb2_breakpoint_size.
+
+2010-02-01  Daniel Jacobowitz  <dan@codesourcery.com>
+
        * arch-utils.c (default_remote_breakpoint_from_pc): New function.
        * arch-utils.h (default_remote_breakpoint_from_pc): Declare.
        * gdbarch.c, gdbarch.h: Regenerated.
index 661a5bd..af409f6 100644 (file)
@@ -74,6 +74,14 @@ static const char arm_linux_thumb_be_breakpoint[] = {0xde, 0x01};
 
 static const char arm_linux_thumb_le_breakpoint[] = {0x01, 0xde};
 
+/* Because the 16-bit Thumb breakpoint is affected by Thumb-2 IT blocks,
+   we must use a length-appropriate breakpoint for 32-bit Thumb
+   instructions.  See also thumb_get_next_pc.  */
+
+static const char arm_linux_thumb2_be_breakpoint[] = { 0xf7, 0xf0, 0xa0, 0x00 };
+
+static const char arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa0 };
+
 /* Description of the longjmp buffer.  */
 #define ARM_LINUX_JB_ELEMENT_SIZE      INT_REGISTER_SIZE
 #define ARM_LINUX_JB_PC                        21
@@ -851,6 +859,7 @@ arm_linux_init_abi (struct gdbarch_info info,
       else
        tdep->arm_breakpoint = arm_linux_arm_be_breakpoint;
       tdep->thumb_breakpoint = arm_linux_thumb_be_breakpoint;
+      tdep->thumb2_breakpoint = arm_linux_thumb2_be_breakpoint;
     }
   else
     {
@@ -859,9 +868,11 @@ arm_linux_init_abi (struct gdbarch_info info,
       else
        tdep->arm_breakpoint = arm_linux_arm_le_breakpoint;
       tdep->thumb_breakpoint = arm_linux_thumb_le_breakpoint;
+      tdep->thumb2_breakpoint = arm_linux_thumb2_le_breakpoint;
     }
   tdep->arm_breakpoint_size = sizeof (arm_linux_arm_le_breakpoint);
   tdep->thumb_breakpoint_size = sizeof (arm_linux_thumb_le_breakpoint);
+  tdep->thumb2_breakpoint_size = sizeof (arm_linux_thumb2_le_breakpoint);
 
   if (tdep->fp_model == ARM_FLOAT_AUTO)
     tdep->fp_model = ARM_FLOAT_FPA;
index d1488dc..e740083 100644 (file)
@@ -2256,17 +2256,50 @@ bitcount (unsigned long val)
   return nbits;
 }
 
+/* Return the size in bytes of the complete Thumb instruction whose
+   first halfword is INST1.  */
+
+static int
+thumb_insn_size (unsigned short inst1)
+{
+  if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
+    return 4;
+  else
+    return 2;
+}
+
+static int
+thumb_advance_itstate (unsigned int itstate)
+{
+  /* Preserve IT[7:5], the first three bits of the condition.  Shift
+     the upcoming condition flags left by one bit.  */
+  itstate = (itstate & 0xe0) | ((itstate << 1) & 0x1f);
+
+  /* If we have finished the IT block, clear the state.  */
+  if ((itstate & 0x0f) == 0)
+    itstate = 0;
+
+  return itstate;
+}
+
+/* Find the next PC after the current instruction executes.  In some
+   cases we can not statically determine the answer (see the IT state
+   handling in this function); in that case, a breakpoint may be
+   inserted in addition to the returned PC, which will be used to set
+   another breakpoint by our caller.  */
+
 static CORE_ADDR
 thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
+  struct address_space *aspace = get_frame_address_space (frame);
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
   unsigned long pc_val = ((unsigned long) pc) + 4;     /* PC after prefetch */
   unsigned short inst1;
   CORE_ADDR nextpc = pc + 2;           /* default is next instruction */
   unsigned long offset;
-  ULONGEST status, it;
+  ULONGEST status, itstate;
 
   inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
 
@@ -2279,18 +2312,100 @@ thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
      block is active.  These bits read as zero on earlier
      processors.  */
   status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
-  it = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
+  itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
 
-  /* On GNU/Linux, where this routine is used, we use an undefined
-     instruction as a breakpoint.  Unlike BKPT, IT can disable execution
-     of the undefined instruction.  So we might miss the breakpoint!  */
-  if (((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0) || (it & 0x0f))
-    error (_("Stepping through Thumb-2 IT blocks is not yet supported"));
+  /* If-Then handling.  On GNU/Linux, where this routine is used, we
+     use an undefined instruction as a breakpoint.  Unlike BKPT, IT
+     can disable execution of the undefined instruction.  So we might
+     miss the breakpoint if we set it on a skipped conditional
+     instruction.  Because conditional instructions can change the
+     flags, affecting the execution of further instructions, we may
+     need to set two breakpoints.  */
 
-  if (it & 0x0f)
+  if (gdbarch_tdep (gdbarch)->thumb2_breakpoint != NULL)
+    {
+      if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
+       {
+         /* An IT instruction.  Because this instruction does not
+            modify the flags, we can accurately predict the next
+            executed instruction.  */
+         itstate = inst1 & 0x00ff;
+         pc += thumb_insn_size (inst1);
+
+         while (itstate != 0 && ! condition_true (itstate >> 4, status))
+           {
+             inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
+             pc += thumb_insn_size (inst1);
+             itstate = thumb_advance_itstate (itstate);
+           }
+
+         return pc;
+       }
+      else if (itstate != 0)
+       {
+         /* We are in a conditional block.  Check the condition.  */
+         if (! condition_true (itstate >> 4, status))
+           {
+             /* Advance to the next executed instruction.  */
+             pc += thumb_insn_size (inst1);
+             itstate = thumb_advance_itstate (itstate);
+
+             while (itstate != 0 && ! condition_true (itstate >> 4, status))
+               {
+                 inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
+                 pc += thumb_insn_size (inst1);
+                 itstate = thumb_advance_itstate (itstate);
+               }
+
+             return pc;
+           }
+         else if ((itstate & 0x0f) == 0x08)
+           {
+             /* This is the last instruction of the conditional
+                block, and it is executed.  We can handle it normally
+                because the following instruction is not conditional,
+                and we must handle it normally because it is
+                permitted to branch.  Fall through.  */
+           }
+         else
+           {
+             int cond_negated;
+
+             /* There are conditional instructions after this one.
+                If this instruction modifies the flags, then we can
+                not predict what the next executed instruction will
+                be.  Fortunately, this instruction is architecturally
+                forbidden to branch; we know it will fall through.
+                Start by skipping past it.  */
+             pc += thumb_insn_size (inst1);
+             itstate = thumb_advance_itstate (itstate);
+
+             /* Set a breakpoint on the following instruction.  */
+             gdb_assert ((itstate & 0x0f) != 0);
+             insert_single_step_breakpoint (gdbarch, aspace, pc);
+             cond_negated = (itstate >> 4) & 1;
+
+             /* Skip all following instructions with the same
+                condition.  If there is a later instruction in the IT
+                block with the opposite condition, set the other
+                breakpoint there.  If not, then set a breakpoint on
+                the instruction after the IT block.  */
+             do
+               {
+                 inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
+                 pc += thumb_insn_size (inst1);
+                 itstate = thumb_advance_itstate (itstate);
+               }
+             while (itstate != 0 && ((itstate >> 4) & 1) == cond_negated);
+
+             return pc;
+           }
+       }
+    }
+  else if (itstate & 0x0f)
     {
       /* We are in a conditional block.  Check the condition.  */
-      int cond = it >> 4;
+      int cond = itstate >> 4;
 
       if (! condition_true (cond, status))
        {
@@ -2301,6 +2416,8 @@ thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
          else
            return pc + 2;
        }
+
+      /* Otherwise, handle the instruction normally.  */
     }
 
   if ((inst1 & 0xff00) == 0xbd00)      /* pop {rlist, pc} */
@@ -4749,10 +4866,29 @@ static const unsigned char *
 arm_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr)
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
 
   if (arm_pc_is_thumb (*pcptr))
     {
       *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
+
+      /* If we have a separate 32-bit breakpoint instruction for Thumb-2,
+        check whether we are replacing a 32-bit instruction.  */
+      if (tdep->thumb2_breakpoint != NULL)
+       {
+         gdb_byte buf[2];
+         if (target_read_memory (*pcptr, buf, 2) == 0)
+           {
+             unsigned short inst1;
+             inst1 = extract_unsigned_integer (buf, 2, byte_order_for_code);
+             if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
+               {
+                 *lenptr = tdep->thumb2_breakpoint_size;
+                 return tdep->thumb2_breakpoint;
+               }
+           }
+       }
+
       *lenptr = tdep->thumb_breakpoint_size;
       return tdep->thumb_breakpoint;
     }
@@ -4763,6 +4899,20 @@ arm_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr)
     }
 }
 
+static void
+arm_remote_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr,
+                              int *kindptr)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  arm_breakpoint_from_pc (gdbarch, pcptr, kindptr);
+
+  if (arm_pc_is_thumb (*pcptr) && *kindptr == 4)
+    /* The documented magic value for a 32-bit Thumb-2 breakpoint, so
+       that this is not confused with a 32-bit ARM breakpoint.  */
+    *kindptr = 3;
+}
+
 /* Extract from an array REGBUF containing the (raw) register state a
    function return value of type TYPE, and copy that, in virtual
    format, into VALBUF.  */
@@ -6091,6 +6241,8 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   /* Breakpoint manipulation.  */
   set_gdbarch_breakpoint_from_pc (gdbarch, arm_breakpoint_from_pc);
+  set_gdbarch_remote_breakpoint_from_pc (gdbarch,
+                                        arm_remote_breakpoint_from_pc);
 
   /* Information about registers, etc.  */
   set_gdbarch_deprecated_fp_regnum (gdbarch, ARM_FP_REGNUM);   /* ??? */
index 1e83834..e6220fe 100644 (file)
@@ -167,9 +167,16 @@ struct gdbarch_tdep
 
   const char *arm_breakpoint;  /* Breakpoint pattern for an ARM insn.  */
   int arm_breakpoint_size;     /* And its size.  */
-  const char *thumb_breakpoint;        /* Breakpoint pattern for an ARM insn.  */
+  const char *thumb_breakpoint;        /* Breakpoint pattern for a Thumb insn.  */
   int thumb_breakpoint_size;   /* And its size.  */
 
+  /* If the Thumb breakpoint is an undefined instruction (which is
+     affected by IT blocks) rather than a BKPT instruction (which is
+     not), then we need a 32-bit Thumb breakpoint to preserve the
+     instruction count in IT blocks.  */
+  const char *thumb2_breakpoint;
+  int thumb2_breakpoint_size;
+
   int jb_pc;                   /* Offset to PC value in jump buffer. 
                                   If this is negative, longjmp support
                                   will be disabled.  */
index fc6b6e2..e62f630 100644 (file)
@@ -1,3 +1,9 @@
+2010-02-01  Daniel Jacobowitz  <dan@codesourcery.com>
+
+       * linux-arm-low.c (thumb_breakpoint_len): Delete.
+       (thumb2_breakpoint): New.
+       (arm_breakpoint_at): Check for Thumb-2 breakpoints.
+
 2010-01-29  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * linux-low.c (get_stop_pc): Check for SIGTRAP.
index e0afd3a..54668f8 100644 (file)
@@ -203,7 +203,7 @@ arm_set_pc (struct regcache *regcache, CORE_ADDR pc)
 static const unsigned long arm_breakpoint = 0xef9f0001;
 #define arm_breakpoint_len 4
 static const unsigned short thumb_breakpoint = 0xde01;
-#define thumb_breakpoint_len 2
+static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
 
 /* For new EABI binaries.  We recognize it regardless of which ABI
    is used for gdbserver, so single threaded debugging should work
@@ -227,6 +227,13 @@ arm_breakpoint_at (CORE_ADDR where)
       (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
       if (insn == thumb_breakpoint)
        return 1;
+
+      if (insn == thumb2_breakpoint[0])
+       {
+         (*the_target->read_memory) (where + 2, (unsigned char *) &insn, 2);
+         if (insn == thumb2_breakpoint[1])
+           return 1;
+       }
     }
   else
     {
index d499ad7..05ecd88 100644 (file)
@@ -1,3 +1,7 @@
+2010-02-01  Daniel Jacobowitz  <dan@codesourcery.com>
+
+       * gdb.arch/thumb2-it.S, gdb.arch/thumb2-it.exp: New files.
+
 2010-01-29  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * gdb.base/call-strs.exp, gdb.base/default.exp,
diff --git a/gdb/testsuite/gdb.arch/thumb2-it.S b/gdb/testsuite/gdb.arch/thumb2-it.S
new file mode 100644 (file)
index 0000000..a5aab8c
--- /dev/null
@@ -0,0 +1,139 @@
+/* Thumb-2 IT blocks test program.
+
+   Copyright 2010 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+       .syntax unified
+       .text
+       .p2align 2
+       .code 16
+
+#ifndef __thumb2__
+
+       .type main,%function
+       .thumb_func
+       .globl main
+main:
+       mov     r0, #0
+       bx      lr      @ No Thumb-2
+
+#else
+
+       .type main,%function
+       .thumb_func
+       .globl main
+main:
+       mov     r0, #0
+       bx      lr      @ Thumb-2 OK
+
+       @ One conditional instruction, executed.
+       .type it_1,%function
+       .thumb_func
+it_1:
+       mov     r0, #0  @ Setup
+       cmp     r0, #0  @ Setup
+       it      eq      @ IT instruction, Expected == 1
+       addeq   r0, #1  @ Reached
+       bx      lr      @ Done
+
+       @ One conditional instruction, skipped.
+       .type it_2,%function
+       .thumb_func
+it_2:
+       mov     r0, #0  @ Setup
+       cmp     r0, #0  @ Setup
+       it      ne      @ IT instruction, Expected == 0
+       addne   r0, #1  @ Not reached
+       bx      lr      @ Done, Check $r0 == 0
+
+       @ Block of four, alternating, starting with executed.
+       .type it_3,%function
+       .thumb_func
+it_3:
+       mov     r0, #0  @ Setup
+       cmp     r0, #0  @ Setup
+       itete   ge      @ IT instruction, Expected == 2
+       addge   r0, #1  @ Reached
+       addlt   r0, #2  @ Not reached
+       addge   r0, #4  @ Reached
+       addlt   r0, #8  @ Not reached
+       bx      lr      @ Done, Check $r0 == 5
+
+       @ Block of four, changing flags.
+       .type it_4,%function
+       .thumb_func
+it_4:
+       mov     r0, #0  @ Setup
+       cmp     r0, #0  @ Setup
+       itttt   ge      @ IT instruction, Expected == 2
+       addge   r0, #1  @ Reached
+       cmpge   r0, #10 @ Reached
+       addge   r0, #4  @ Not reached
+       addge   r0, #8  @ Not reached
+       bx      lr      @ Done, Check $r0 == 1
+
+       @ Block of two, ending with taken branch.
+       .type it_5,%function
+       .thumb_func
+it_5:
+       mov     r0, #0  @ Setup
+       cmp     r0, #0  @ Setup
+       itt     ge      @ IT instruction, Expected == 2
+       addge   r0, #1  @ Reached
+       bge     .L5     @ Reached
+       add     r0, #2  @ Never reached
+.L5:   bx      lr      @ Done, Check $r0 == 1
+
+       @ Block of two, ending with untaken branch.
+       .type it_6,%function
+       .thumb_func
+it_6:
+       mov     r0, #0  @ Setup
+       cmp     r0, #0  @ Setup
+       ite     ge      @ IT instruction, Expected == 2
+       addge   r0, #1  @ Reached
+       blt     .L6     @ Not reached
+       add     r0, #2  @ Reached
+.L6:   bx      lr      @ Done, Check $r0 == 3
+
+       @ Block of four, taken, of different sizes
+       .type it_7,%function
+       .thumb_func
+it_7:
+       mov     r0, #0  @ Setup
+       cmp     r0, #0  @ Setup
+       itttt   ge      @ IT instruction, Expected == 4
+       addge.n r0, #1  @ Reached
+       addge.w r0, #2  @ Reached
+       addge.n r0, #4  @ Reached
+       addge.w r0, #8  @ Reached
+       bx      lr      @ Done, Check $r0 == 15
+
+       @ Block of four, only first executed.
+       .type it_3,%function
+       .thumb_func
+it_8:
+       mov     r0, #0  @ Setup
+       cmp     r0, #0  @ Setup
+       iteee   ge      @ IT instruction, Expected == 1
+       addge   r0, #1  @ Reached
+       addlt   r0, #2  @ Not reached
+       addlt   r0, #4  @ Not reached
+       addlt   r0, #8  @ Not reached
+       bx      lr      @ Done, Check $r0 == 1
+
+#endif /* __thumb2__ */
diff --git a/gdb/testsuite/gdb.arch/thumb2-it.exp b/gdb/testsuite/gdb.arch/thumb2-it.exp
new file mode 100644 (file)
index 0000000..5ef8475
--- /dev/null
@@ -0,0 +1,140 @@
+# Copyright 2010 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/>.
+
+# Test single stepping over Thumb-2 IT blocks.
+
+if {![istarget arm*-*eabi*]} then {
+    verbose "Skipping Thumb-2 tests."
+    return
+}
+
+set testfile "thumb2-it"
+set srcfile ${testfile}.S
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable debug] != "" } {
+    untested thumb2-it.exp
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if ![runto_main] then {
+    untested thumb2-it.exp
+    return -1
+}
+
+# Make sure that the compiler options allow Thumb-2.
+gdb_test_multiple "list" "list main" {
+    -re ".*@ No Thumb-2.*$gdb_prompt $" {
+       pass "list main"
+       untested thumb2-it.exp
+       return -1
+    }
+    -re ".*@ Thumb-2 OK.*$gdb_prompt $" {
+       pass "list main"
+    }
+}
+
+proc test_it_block { func } {
+    global gdb_prompt
+    global software_step
+
+    if { ! [gdb_breakpoint "*${func}"] } {
+       unresolved "$func, IT block tests"
+       return
+    }
+
+    gdb_test "call ${func}()" "Breakpoint.*@ Setup.*" "$func, call"
+
+    set expected 0
+    set reached 0
+    set steps 0
+    set ok 1
+    while { $ok } {
+       set ok 0
+       set msg "$func, stepi $steps"
+       gdb_test_multiple "stepi" "$msg" {
+           -re ".*@ Setup.*$gdb_prompt $" {
+               pass "$msg"
+               set ok 1
+           }
+           -re ".*@ IT instruction, Expected == (\[0-9\]*)\r\n$gdb_prompt $" {
+               set expected $expect_out(1,string)
+               pass "$msg"
+               set ok 1
+           }
+           -re ".*@ Reached.*$gdb_prompt $" {
+               incr reached
+               pass "$msg"
+               set ok 1
+               if { [regexp {@ Reached, Set ([^\r\n]*)\r\n} $expect_out(0,string) dummy change] } {
+                   gdb_test "set $change" "" "$func, set $change"
+               }
+           }
+           -re ".*@ Not reached.*$gdb_prompt $" {
+               # An instruction in an IT block whose predicate is false when
+               # we reach it.  If using software single step, we should not
+               # stop here.
+               if { $software_step } {
+                   fail "$msg"
+               } else {
+                   pass "$msg"
+                   set ok 1
+               }
+           }
+           -re ".*@ Never reached.*$gdb_prompt $" {
+               # An instruction that should be branched over.
+               fail "$msg"
+           }
+           -re ".*@ Done.*$gdb_prompt $" {
+               pass "$msg"
+               if { $reached == $expected } {
+                   pass "$func, correct instructions reached"
+               } else {
+                   fail "$func, correct instructions reached"
+               }
+               if { [regexp {@ Done, Check ([^\r\n]*)\r\n} $expect_out(0,string) dummy check] } {
+                   gdb_test "print $check" ".* = 1" "$func, $check"
+               }
+           }
+       }
+       if { ! $ok } {
+           break
+       }
+       incr steps
+       continue
+    }
+
+    gdb_test "continue" "" "$func, continue"
+    return
+}
+
+# If we are using software single-stepping in GDB, then GDB will not
+# stop at conditional instructions with a false predicate during stepi.
+# If we are using a simulator or debug interface with hardware single
+# step, then GDB will stop at such instructions.
+if { [istarget arm*-linux*] } {
+    set software_step 1
+} else {
+    set software_step 0
+}
+
+for { set i 1 } { $i <= 8 } { incr i } {
+    test_it_block it_${i}
+}