diff options
author | Daniel Jacobowitz <drow@false.org> | 2010-02-01 16:13:17 +0000 |
---|---|---|
committer | Daniel Jacobowitz <drow@false.org> | 2010-02-01 16:13:17 +0000 |
commit | 177321bd85d5a3d4ca05d320696d8af43bc0fc05 (patch) | |
tree | ffd4365fcfcbd8de561b1f7d37570927d180b9d6 | |
parent | a1dcb23a280b453738e569f0f7674dbd8ca39933 (diff) | |
download | gdb-177321bd85d5a3d4ca05d320696d8af43bc0fc05.zip gdb-177321bd85d5a3d4ca05d320696d8af43bc0fc05.tar.gz gdb-177321bd85d5a3d4ca05d320696d8af43bc0fc05.tar.bz2 |
* 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.
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.
-rw-r--r-- | gdb/ChangeLog | 17 | ||||
-rw-r--r-- | gdb/arm-linux-tdep.c | 11 | ||||
-rw-r--r-- | gdb/arm-tdep.c | 170 | ||||
-rw-r--r-- | gdb/arm-tdep.h | 9 | ||||
-rw-r--r-- | gdb/gdbserver/ChangeLog | 6 | ||||
-rw-r--r-- | gdb/gdbserver/linux-arm-low.c | 9 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/thumb2-it.S | 139 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/thumb2-it.exp | 140 |
9 files changed, 494 insertions, 11 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index ee0a8f3..72866ca 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -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. diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c index 661a5bd..af409f6 100644 --- a/gdb/arm-linux-tdep.c +++ b/gdb/arm-linux-tdep.c @@ -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; diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index d1488dc..e740083 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -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); /* ??? */ diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h index 1e83834..e6220fe 100644 --- a/gdb/arm-tdep.h +++ b/gdb/arm-tdep.h @@ -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. */ diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index fc6b6e2..e62f630 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -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. diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c index e0afd3a..54668f8 100644 --- a/gdb/gdbserver/linux-arm-low.c +++ b/gdb/gdbserver/linux-arm-low.c @@ -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 { diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index d499ad71..05ecd88 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -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 index 0000000..a5aab8c --- /dev/null +++ b/gdb/testsuite/gdb.arch/thumb2-it.S @@ -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 index 0000000..5ef8475 --- /dev/null +++ b/gdb/testsuite/gdb.arch/thumb2-it.exp @@ -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} +} |