#include "gdbcore.h"
#include "gdbcmd.h"
#include "objfiles.h"
+#include "prologue-value.h"
#include "trad-frame.h"
/* ARC header files. */
#include "features/arc-v2.c"
#include "features/arc-arcompact.c"
-/* The frame unwind cache for the ARC. Current structure is a stub, because
- it should be filled in during the prologue analysis. */
+/* The frame unwind cache for ARC. */
struct arc_frame_cache
{
frame. */
CORE_ADDR prev_sp;
- /* Store addresses for registers saved in prologue. */
+ /* Register that is a base for this frame - FP for normal frame, SP for
+ non-FP frames. */
+ int frame_base_reg;
+
+ /* Offset from the previous SP to the current frame base. If GCC uses
+ `SUB SP,SP,offset` to allocate space for local variables, then it will be
+ done after setting up a frame pointer, but it still will be considered
+ part of prologue, therefore SP will be lesser than FP at the end of the
+ prologue analysis. In this case that would be an offset from old SP to a
+ new FP. But in case of non-FP frames, frame base is an SP and thus that
+ would be an offset from old SP to new SP. What is important is that this
+ is an offset from old SP to a known register, so it can be used to find
+ old SP.
+
+ Using FP is preferable, when possible, because SP can change in function
+ body after prologue due to alloca, variadic arguments or other shenanigans.
+ If that is the case in the caller frame, then PREV_SP will point to SP at
+ the moment of function call, but it will be different from SP value at the
+ end of the caller prologue. As a result it will not be possible to
+ reconstruct caller's frame and go past it in the backtrace. Those things
+ are unlikely to happen to FP - FP value at the moment of function call (as
+ stored on stack in callee prologue) is also an FP value at the end of the
+ caller's prologue. */
+
+ LONGEST frame_base_offset;
+
+ /* Store addresses for registers saved in prologue. During prologue analysis
+ GDB stores offsets relatively to "old SP", then after old SP is evaluated,
+ offsets are replaced with absolute addresses. */
struct trad_frame_saved_reg *saved_regs;
};
"lp_count", "reserved", "limm", "pcl",
};
+/* Functions are sorted in the order as they are used in the
+ _initialize_arc_tdep (), which uses the same order as gdbarch.h. Static
+ functions are defined before the first invocation. */
+
/* Returns an unsigned value of OPERAND_NUM in instruction INSN.
For relative branch instructions returned value is an offset, not an actual
branch target. */
return value;
}
-/* Functions are sorted in the order as they are used in the
- _initialize_arc_tdep (), which uses the same order as gdbarch.h. Static
- functions are defined before the first invocation. */
-
CORE_ADDR
arc_insn_get_branch_target (const struct arc_instruction &insn)
{
return (CORE_ADDR) get_frame_register_unsigned (this_frame, ARC_FP_REGNUM);
}
+/* Helper function that returns valid pv_t for an instruction operand:
+ either a register or a constant. */
+
+static pv_t
+arc_pv_get_operand (pv_t *regs, const struct arc_instruction &insn, int operand)
+{
+ if (insn.operands[operand].kind == ARC_OPERAND_KIND_REG)
+ return regs[insn.operands[operand].value];
+ else
+ return pv_constant (arc_insn_get_operand_value (insn, operand));
+}
+
+/* Determine whether the given disassembled instruction may be part of a
+ function prologue. If it is, the information in the frame unwind cache will
+ be updated. */
+
+static bool
+arc_is_in_prologue (struct gdbarch *gdbarch, const struct arc_instruction &insn,
+ pv_t *regs, struct pv_area *stack)
+{
+ /* It might be that currently analyzed address doesn't contain an
+ instruction, hence INSN is not valid. It likely means that address points
+ to a data, non-initialized memory, or middle of a 32-bit instruction. In
+ practice this may happen if GDB connects to a remote target that has
+ non-zeroed memory. GDB would read PC value and would try to analyze
+ prologue, but there is no guarantee that memory contents at the address
+ specified in PC is address is a valid instruction. There is not much that
+ that can be done about that. */
+ if (!insn.valid)
+ return false;
+
+ /* Branch/jump or a predicated instruction. */
+ if (insn.is_control_flow || insn.condition_code != ARC_CC_AL)
+ return false;
+
+ /* Store of some register. May or may not update base address register. */
+ if (insn.insn_class == STORE || insn.insn_class == PUSH)
+ {
+ /* There is definetely at least one operand - register/value being
+ stored. */
+ gdb_assert (insn.operands_count > 0);
+
+ /* Store at some constant address. */
+ if (insn.operands_count > 1
+ && insn.operands[1].kind != ARC_OPERAND_KIND_REG)
+ return false;
+
+ /* Writeback modes:
+ Mode Address used Writeback value
+ --------------------------------------------------
+ No reg + offset no
+ A/AW reg + offset reg + offset
+ AB reg reg + offset
+ AS reg + (offset << scaling) no
+
+ "PUSH reg" is an alias to "ST.AW reg, [SP, -4]" encoding. However
+ 16-bit PUSH_S is a distinct instruction encoding, where offset and
+ base register are implied through opcode. */
+
+ /* Register with base memory address. */
+ int base_reg = arc_insn_get_memory_base_reg (insn);
+
+ /* Address where to write. arc_insn_get_memory_offset returns scaled
+ value for ARC_WRITEBACK_AS. */
+ pv_t addr;
+ if (insn.writeback_mode == ARC_WRITEBACK_AB)
+ addr = regs[base_reg];
+ else
+ addr = pv_add_constant (regs[base_reg],
+ arc_insn_get_memory_offset (insn));
+
+ if (pv_area_store_would_trash (stack, addr))
+ return false;
+
+ if (insn.data_size_mode != ARC_SCALING_D)
+ {
+ /* Find the value being stored. */
+ pv_t store_value = arc_pv_get_operand (regs, insn, 0);
+
+ /* What is the size of a the stored value? */
+ CORE_ADDR size;
+ if (insn.data_size_mode == ARC_SCALING_B)
+ size = 1;
+ else if (insn.data_size_mode == ARC_SCALING_H)
+ size = 2;
+ else
+ size = ARC_REGISTER_SIZE;
+
+ pv_area_store (stack, addr, size, store_value);
+ }
+ else
+ {
+ if (insn.operands[0].kind == ARC_OPERAND_KIND_REG)
+ {
+ /* If this is a double store, than write N+1 register as well. */
+ pv_t store_value1 = regs[insn.operands[0].value];
+ pv_t store_value2 = regs[insn.operands[0].value + 1];
+ pv_area_store (stack, addr, ARC_REGISTER_SIZE, store_value1);
+ pv_area_store (stack,
+ pv_add_constant (addr, ARC_REGISTER_SIZE),
+ ARC_REGISTER_SIZE, store_value2);
+ }
+ else
+ {
+ pv_t store_value
+ = pv_constant (arc_insn_get_operand_value (insn, 0));
+ pv_area_store (stack, addr, ARC_REGISTER_SIZE * 2, store_value);
+ }
+ }
+
+ /* Is base register updated? */
+ if (insn.writeback_mode == ARC_WRITEBACK_A
+ || insn.writeback_mode == ARC_WRITEBACK_AB)
+ regs[base_reg] = pv_add_constant (regs[base_reg],
+ arc_insn_get_memory_offset (insn));
+
+ return true;
+ }
+ else if (insn.insn_class == MOVE)
+ {
+ gdb_assert (insn.operands_count == 2);
+
+ /* Destination argument can be "0", so nothing will happen. */
+ if (insn.operands[0].kind == ARC_OPERAND_KIND_REG)
+ {
+ int dst_regnum = insn.operands[0].value;
+ regs[dst_regnum] = arc_pv_get_operand (regs, insn, 1);
+ }
+ return true;
+ }
+ else if (insn.insn_class == SUB)
+ {
+ gdb_assert (insn.operands_count == 3);
+
+ /* SUB 0,b,c. */
+ if (insn.operands[0].kind != ARC_OPERAND_KIND_REG)
+ return true;
+
+ int dst_regnum = insn.operands[0].value;
+ regs[dst_regnum] = pv_subtract (arc_pv_get_operand (regs, insn, 1),
+ arc_pv_get_operand (regs, insn, 2));
+ return true;
+ }
+ else if (insn.insn_class == ENTER)
+ {
+ /* ENTER_S is a prologue-in-instruction - it saves all callee-saved
+ registers according to given arguments thus greatly reducing code
+ size. Which registers will be actually saved depends on arguments.
+
+ ENTER_S {R13-...,FP,BLINK} stores registers in following order:
+
+ new SP ->
+ BLINK
+ R13
+ R14
+ R15
+ ...
+ FP
+ old SP ->
+
+ There are up to three arguments for this opcode, as presented by ARC
+ disassembler:
+ 1) amount of general-purpose registers to be saved - this argument is
+ always present even when it is 0;
+ 2) FP register number (27) if FP has to be stored, otherwise argument
+ is not present;
+ 3) BLINK register number (31) if BLINK has to be stored, otherwise
+ argument is not present. If both FP and BLINK are stored, then FP
+ is present before BLINK in argument list. */
+ gdb_assert (insn.operands_count > 0);
+
+ int regs_saved = arc_insn_get_operand_value (insn, 0);
+
+ bool is_fp_saved;
+ if (insn.operands_count > 1)
+ is_fp_saved = (insn.operands[1].value == ARC_FP_REGNUM);
+ else
+ is_fp_saved = false;
+
+ bool is_blink_saved;
+ if (insn.operands_count > 1)
+ is_blink_saved = (insn.operands[insn.operands_count - 1].value
+ == ARC_BLINK_REGNUM);
+ else
+ is_blink_saved = false;
+
+ /* Amount of bytes to be allocated to store specified registers. */
+ CORE_ADDR st_size = ((regs_saved + is_fp_saved + is_blink_saved)
+ * ARC_REGISTER_SIZE);
+ pv_t new_sp = pv_add_constant (regs[ARC_SP_REGNUM], -st_size);
+
+ /* Assume that if the last register (closest to new SP) can be written,
+ then it is possible to write all of them. */
+ if (pv_area_store_would_trash (stack, new_sp))
+ return false;
+
+ /* Current store address. */
+ pv_t addr = regs[ARC_SP_REGNUM];
+
+ if (is_fp_saved)
+ {
+ addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
+ pv_area_store (stack, addr, ARC_REGISTER_SIZE, regs[ARC_FP_REGNUM]);
+ }
+
+ /* Registers are stored in backward order: from GP (R26) to R13. */
+ for (int i = ARC_R13_REGNUM + regs_saved - 1; i >= ARC_R13_REGNUM; i--)
+ {
+ addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
+ pv_area_store (stack, addr, ARC_REGISTER_SIZE, regs[i]);
+ }
+
+ if (is_blink_saved)
+ {
+ addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
+ pv_area_store (stack, addr, ARC_REGISTER_SIZE,
+ regs[ARC_BLINK_REGNUM]);
+ }
+
+ gdb_assert (pv_is_identical (addr, new_sp));
+
+ regs[ARC_SP_REGNUM] = new_sp;
+
+ if (is_fp_saved)
+ regs[ARC_FP_REGNUM] = regs[ARC_SP_REGNUM];
+
+ return true;
+ }
+
+ /* Some other architectures, like nds32 or arm, try to continue as far as
+ possible when building a prologue cache (as opposed to when skipping
+ prologue), so that cache will be as full as possible. However current
+ code for ARC doesn't recognize some instructions that may modify SP, like
+ ADD, AND, OR, etc, hence there is no way to guarantee that SP wasn't
+ clobbered by the skipped instruction. Potential existence of extension
+ instruction, which may do anything they want makes this even more complex,
+ so it is just better to halt on a first unrecognized instruction. */
+
+ return false;
+}
+
/* Copy of gdb_buffered_insn_length_fprintf from disasm.c. */
static int ATTRIBUTE_PRINTF (2, 3)
return di;
}
+/* Analyze the prologue and update the corresponding frame cache for the frame
+ unwinder for unwinding frames that doesn't have debug info. In such
+ situation GDB attempts to parse instructions in the prologue to understand
+ where each register is saved.
+
+ If CACHE is not NULL, then it will be filled with information about saved
+ registers.
+
+ There are several variations of prologue which GDB may encouter. "Full"
+ prologue looks like this:
+
+ sub sp,sp,<imm> ; Space for variadic arguments.
+ push blink ; Store return address.
+ push r13 ; Store callee saved registers (up to R26/GP).
+ push r14
+ push fp ; Store frame pointer.
+ mov fp,sp ; Update frame pointer.
+ sub sp,sp,<imm> ; Create space for local vars on the stack.
+
+ Depending on compiler options lots of things may change:
+
+ 1) BLINK is not saved in leaf functions.
+ 2) Frame pointer is not saved and updated if -fomit-frame-pointer is used.
+ 3) 16-bit versions of those instructions may be used.
+ 4) Instead of a sequence of several push'es, compiler may instead prefer to
+ do one subtract on stack pointer and then store registers using normal
+ store, that doesn't update SP. Like this:
+
+
+ sub sp,sp,8 ; Create space for calee-saved registers.
+ st r13,[sp,4] ; Store callee saved registers (up to R26/GP).
+ st r14,[sp,0]
+
+ 5) ENTER_S instruction can encode most of prologue sequence in one
+ instruction (except for those subtracts for variadic arguments and local
+ variables).
+ 6) GCC may use "millicode" functions from libgcc to store callee-saved
+ registers with minimal code-size requirements. This function currently
+ doesn't support this.
+
+ ENTRYPOINT is a function entry point where prologue starts.
+
+ LIMIT_PC is a maximum possible end address of prologue (meaning address
+ of first instruction after the prologue). It might also point to the middle
+ of prologue if execution has been stopped by the breakpoint at this address
+ - in this case debugger should analyze prologue only up to this address,
+ because further instructions haven't been executed yet.
+
+ Returns address of the first instruction after the prologue. */
+
+static CORE_ADDR
+arc_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR entrypoint,
+ const CORE_ADDR limit_pc, struct arc_frame_cache *cache)
+{
+ if (arc_debug)
+ debug_printf ("arc: analyze_prologue (entrypoint=%s, limit_pc=%s)\n",
+ paddress (gdbarch, entrypoint),
+ paddress (gdbarch, limit_pc));
+
+ /* Prologue values. Only core registers can be stored. */
+ pv_t regs[ARC_LAST_CORE_REGNUM + 1];
+ for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+ regs[i] = pv_register (i, 0);
+ struct pv_area *stack = make_pv_area (ARC_SP_REGNUM,
+ gdbarch_addr_bit (gdbarch));
+ struct cleanup *back_to = make_cleanup_free_pv_area (stack);
+
+ CORE_ADDR current_prologue_end = entrypoint;
+
+ /* Look at each instruction in the prologue. */
+ while (current_prologue_end < limit_pc)
+ {
+ struct arc_instruction insn;
+ struct disassemble_info di = arc_disassemble_info (gdbarch);
+ arc_insn_decode (current_prologue_end, &di, arc_delayed_print_insn,
+ &insn);
+
+ if (arc_debug >= 2)
+ arc_insn_dump (insn);
+
+ /* If this instruction is in the prologue, fields in the cache will be
+ updated, and the saved registers mask may be updated. */
+ if (!arc_is_in_prologue (gdbarch, insn, regs, stack))
+ {
+ /* Found an instruction that is not in the prologue. */
+ if (arc_debug)
+ debug_printf ("arc: End of prologue reached at address %s\n",
+ paddress (gdbarch, insn.address));
+ break;
+ }
+
+ current_prologue_end = arc_insn_get_linear_next_pc (insn);
+ }
+
+ if (cache != NULL)
+ {
+ /* Figure out if it is a frame pointer or just a stack pointer. */
+ if (pv_is_register (regs[ARC_FP_REGNUM], ARC_SP_REGNUM))
+ {
+ cache->frame_base_reg = ARC_FP_REGNUM;
+ cache->frame_base_offset = -regs[ARC_FP_REGNUM].k;
+ }
+ else
+ {
+ cache->frame_base_reg = ARC_SP_REGNUM;
+ cache->frame_base_offset = -regs[ARC_SP_REGNUM].k;
+ }
+
+ /* Assign offset from old SP to all saved registers. */
+ for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+ {
+ CORE_ADDR offset;
+ if (pv_area_find_reg (stack, gdbarch, i, &offset))
+ cache->saved_regs[i].addr = offset;
+ }
+ }
+
+ do_cleanups (back_to);
+ return current_prologue_end;
+}
+
+/* Estimated maximum prologue length in bytes. This should include:
+ 1) Store instruction for each callee-saved register (R25 - R13 + 1)
+ 2) Two instructions for FP
+ 3) One for BLINK
+ 4) Three substract instructions for SP (for variadic args, for
+ callee saved regs and for local vars) and assuming that those SUB use
+ long-immediate (hence double length).
+ 5) Stores of arguments registers are considered part of prologue too
+ (R7 - R1 + 1).
+ This is quite an extreme case, because even with -O0 GCC will collapse first
+ two SUBs into one and long immediate values are quite unlikely to appear in
+ this case, but still better to overshoot a bit - prologue analysis will
+ anyway stop at the first instruction that doesn't fit prologue, so this
+ limit will be rarely reached. */
+
+const static int MAX_PROLOGUE_LENGTH
+ = 4 * (ARC_R25_REGNUM - ARC_R13_REGNUM + 1 + 2 + 1 + 6
+ + ARC_LAST_ARG_REGNUM - ARC_FIRST_ARG_REGNUM + 1);
+
/* Implement the "skip_prologue" gdbarch method.
Skip the prologue for the function at PC. This is done by checking from
/* No prologue info in symbol table, have to analyze prologue. */
/* Find an upper limit on the function prologue using the debug
- information. If the debug information could not be used to provide that
- bound, then pass 0 and arc_scan_prologue will estimate value itself. */
+ information. If there is no debug information about prologue end, then
+ skip_prologue_using_sal will return 0. */
CORE_ADDR limit_pc = skip_prologue_using_sal (gdbarch, pc);
- /* We don't have a proper analyze_prologue function yet, but its result
- should be returned here. Currently GDB will just stop at the first
- instruction of function if debug information doesn't have prologue info;
- and if there is a debug info about prologue - this code path will not be
- taken at all. */
- return (limit_pc == 0 ? pc : limit_pc);
+
+ /* If there is no debug information at all, it is required to give some
+ semi-arbitrary hard limit on amount of bytes to scan during prologue
+ analysis. */
+ if (limit_pc == 0)
+ limit_pc = pc + MAX_PROLOGUE_LENGTH;
+
+ /* Find the address of the first instruction after the prologue by scanning
+ through it - no other information is needed, so pass NULL as a cache. */
+ return arc_analyze_prologue (gdbarch, pc, limit_pc, NULL);
}
/* Implement the "print_insn" gdbarch method.
return align_down (sp, 4);
}
+/* Dump the frame info. Used for internal debugging only. */
+
+static void
+arc_print_frame_cache (struct gdbarch *gdbarch, char *message,
+ struct arc_frame_cache *cache, int addresses_known)
+{
+ debug_printf ("arc: frame_info %s\n", message);
+ debug_printf ("arc: prev_sp = %s\n", paddress (gdbarch, cache->prev_sp));
+ debug_printf ("arc: frame_base_reg = %i\n", cache->frame_base_reg);
+ debug_printf ("arc: frame_base_offset = %s\n",
+ plongest (cache->frame_base_offset));
+
+ for (int i = 0; i <= ARC_BLINK_REGNUM; i++)
+ {
+ if (trad_frame_addr_p (cache->saved_regs, i))
+ debug_printf ("arc: saved register %s at %s %s\n",
+ gdbarch_register_name (gdbarch, i),
+ (addresses_known) ? "address" : "offset",
+ paddress (gdbarch, cache->saved_regs[i].addr));
+ }
+}
+
/* Frame unwinder for normal frames. */
static struct arc_frame_cache *
struct gdbarch *gdbarch = get_frame_arch (this_frame);
CORE_ADDR block_addr = get_frame_address_in_block (this_frame);
- CORE_ADDR prev_pc = get_frame_pc (this_frame);
-
CORE_ADDR entrypoint, prologue_end;
if (find_pc_partial_function (block_addr, NULL, &entrypoint, &prologue_end))
{
struct symtab_and_line sal = find_pc_line (entrypoint, 0);
+ CORE_ADDR prev_pc = get_frame_pc (this_frame);
if (sal.line == 0)
/* No line info so use current PC. */
prologue_end = prev_pc;
}
else
{
+ /* If find_pc_partial_function returned nothing then there is no symbol
+ information at all for this PC. Currently it is assumed in this case
+ that current PC is entrypoint to function and try to construct the
+ frame from that. This is, probably, suboptimal, for example ARM
+ assumes in this case that program is inside the normal frame (with
+ frame pointer). ARC, perhaps, should try to do the same. */
entrypoint = get_frame_register_unsigned (this_frame,
gdbarch_pc_regnum (gdbarch));
- prologue_end = 0;
+ prologue_end = entrypoint + MAX_PROLOGUE_LENGTH;
}
/* Allocate new frame cache instance and space for saved register info.
- * FRAME_OBSTACK_ZALLOC will initialize fields to zeroes. */
+ FRAME_OBSTACK_ZALLOC will initialize fields to zeroes. */
struct arc_frame_cache *cache
= FRAME_OBSTACK_ZALLOC (struct arc_frame_cache);
cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
- /* Should call analyze_prologue here, when it will be implemented. */
+ arc_analyze_prologue (gdbarch, entrypoint, prologue_end, cache);
+
+ if (arc_debug)
+ arc_print_frame_cache (gdbarch, "after prologue", cache, false);
+
+ CORE_ADDR unwound_fb = get_frame_register_unsigned (this_frame,
+ cache->frame_base_reg);
+ if (unwound_fb == 0)
+ return cache;
+ cache->prev_sp = unwound_fb + cache->frame_base_offset;
+
+ for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+ {
+ if (trad_frame_addr_p (cache->saved_regs, i))
+ cache->saved_regs[i].addr += cache->prev_sp;
+ }
+
+ if (arc_debug)
+ arc_print_frame_cache (gdbarch, "after previous SP found", cache, true);
return cache;
}
--- /dev/null
+; This testcase is part of GDB, the GNU debugger.
+
+; Copyright 2017 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/>.
+
+.section .data
+some_variable:
+.long 0xdeadbeef
+
+.section .text
+.global main
+.type main, @function
+
+; Standard prologue.
+
+.align 4
+standard_prologue:
+ push blink
+ sub sp,sp,12
+ st r13, [sp, 0]
+ st r14, [sp, 4]
+ st r18, [sp, 8]
+ add r0, r1, r2
+ ld r18, [sp, 8]
+ ld r14, [sp, 4]
+ ld r13, [sp, 0]
+ add sp,sp,12
+ pop blink
+ j [blink]
+
+; Standard prologue using short instructions.
+
+.align 4
+mini_prologue:
+ push_s blink
+ sub_s sp,sp,12
+ ; ST_S can store only some of the core registers.
+ st_s r13, [sp, 0]
+ st_s r15, [sp, 4]
+ st_s r14, [sp, 8]
+ add r0, r1, r2
+ add sp,sp,16
+ j [blink]
+
+; Standard prologue without `sub sp,sp,INTEGER`.
+
+.align 4
+no_subsp_prologue:
+ push blink
+ push r13
+ push r20
+ push r25
+ add r0, r1, r2
+ pop r25
+ pop r20
+ pop r13
+ pop blink
+ j [blink]
+
+; Standard prologue of leaf function.
+
+.align 4
+leaf_prologue:
+ sub sp,sp,8
+ st r13, [sp, 0]
+ st r15, [sp, 4]
+ add r0, r1, r2
+ ld r13, [sp, 0]
+ ld r15, [sp, 4]
+ j.d [blink]
+ add sp,sp,8
+
+; Prologue with `push fp`.
+
+.align 4
+pushfp_prologue:
+ push r13
+ push r14
+ push fp
+ ; mov fp,sp is part of prologue, but this test will not verify that.
+ ; It will be checked later in the "arg_regs_fp" test.
+ mov fp, sp
+ add r0, r1, r2
+ pop fp
+ pop r14
+ pop r13
+ j [blink]
+
+; Prologue with frame pointer and store relative to FP.
+
+.align 4
+fp_prologue_with_store:
+ push r13
+ push r14
+ push fp
+ mov fp, sp
+ sub_s sp,sp,4
+ st r15,[fp,-4]
+ add r0, r1, r2
+ pop r15
+ pop fp
+ pop r14
+ pop r13
+ j [blink]
+
+; Verify that store of the non-callee saved registers is not part of prologue.
+; Repeat this test for multiple registers, to check boundaries. Also check
+; with both ST and PUSH (aka ST.AW). We have to use multiple functions for
+; this, because GDB would stop analisys at the first instruction that is not
+; part of prologue.
+
+.align 4
+noncallee_saved_regs_r12_st:
+ sub sp,sp,8
+ st r13, [sp, 4]
+ st r12, [sp, 0]
+ add r0, r1, r2
+ j.d [blink]
+ add sp,sp,8
+
+.align 4
+noncallee_saved_regs_r12_push:
+ push r13
+ push r12
+ add r0, r1, r2
+ j.d [blink]
+ add sp,sp,8
+
+.align 4
+noncallee_saved_regs_r2_push:
+ push r13
+ push r2
+ add r0, r1, r2
+ j.d [blink]
+ add sp,sp,8
+
+.align 4
+noncallee_saved_regs_gp_push:
+ push r25
+ push gp
+ add r0, r1, r2
+ j.d [blink]
+ add sp,sp,8
+
+; LP_COUNT is treated like a normal register.
+
+.align 4
+noncallee_saved_regs_lp_count:
+ push r25
+ push lp_count
+ add r0, r1, r2
+ j.d [blink]
+ add sp,sp,8
+
+; BLINK is saved, but after an instruction that is not part of prologue.
+; Currently arc_analyze_prologue stops analisys at the first intstruction
+; that is not a part of prologue. This might be not the best way, but it is
+; what it is right now, so this test confirms this.
+
+.align 4
+noncallee_saved_regs_blink_out_of_prologue:
+ push r25
+ push gp
+ push blink
+ add r0, r1, r2
+ j.d [blink]
+ add sp,sp,12
+
+; Saving arguments register via FP.
+
+.align 4
+arg_regs_fp:
+ push fp
+ mov fp, sp
+ sub sp, sp, 16
+ st r0, [fp, -4]
+ st r1, [fp, -8]
+ st r7, [fp, -12]
+ st r8, [fp, -16]
+ add r0, r1, r2
+ add sp,sp,16
+ pop fp
+ j [blink]
+
+; Like the previous, but with mov_s.
+
+.align 4
+arg_regs_fp_mov_s:
+ push fp
+ mov_s fp, sp
+ sub sp, sp, 8
+ st r0, [fp, -4]
+ ; Not part of the prologue.
+ st r8, [fp, -8]
+ add r0, r1, r2
+ add sp,sp,8
+ pop fp
+ j [blink]
+
+; Saving arguments register without FP.
+
+.align 4
+arg_regs_sp:
+ sub sp, sp, 24
+ st r0, [sp, 0]
+ st r1, [sp, 4]
+ st r7, [sp, 8]
+ ; Normally that would be done before saving args, but it is used as a
+ ; marker that saving arguments relatively to SP is considered part of
+ ; prologue.
+ st r13, [sp, 16]
+ ; Not part of the prologue.
+ st r8, [sp, 12]
+ st r14, [sp, 20]
+ add r0, r1, r2
+ j.d [blink]
+ add sp,sp,24
+
+; ENTER_S that does nothing.
+
+.align 4
+enter_s_nop:
+ ; Effectively a nop.
+ enter_s 0
+ add r0,r1,r2
+ j [blink]
+
+; ENTER_S that stores BLINK.
+
+.align 4
+enter_s_blink:
+ enter_s 32
+ add r0,r1,r2
+ j.d [blink]
+ add sp,sp,4
+
+; ENTER_S that stores FP.
+
+.align 4
+enter_s_fp:
+ enter_s 16
+ add r0,r1,r2
+ j.d [blink]
+ add sp,sp,4
+
+; ENTER_S that stores R13, FP and BLINK.
+
+.align 4
+enter_s_r13:
+ enter_s (32 + 16 + 1)
+ add r0,r1,r2
+ j.d [blink]
+ add sp,sp,12
+
+; ENTER_S that stores R13-R15
+
+.align 4
+enter_s_r15:
+ enter_s 3
+ add r0,r1,r2
+ j.d [blink]
+ add sp,sp,12
+
+; ENTER_S that stores everything it could.
+
+.align 4
+enter_s_all:
+ enter_s (32 + 16 + 14)
+ add r0,r1,r2
+ j.d [blink]
+ add sp,sp,64
+
+; Deeper nesting.
+
+.align 4
+nested_prologue_inner:
+ sub sp,sp,8
+ st r18, [sp, 4]
+ st r13, [sp, 0]
+ add r0, r1, r2
+ ld r18, [sp, 4]
+ ld r13, [sp, 0]
+ j.d [blink]
+ add sp,sp,8
+
+.align 4
+nested_prologue_outer:
+ push blink
+ sub sp,sp,8
+ st r14, [sp, 0]
+ st r15, [sp, 4]
+ bl @nested_prologue_inner
+ add r0, r1, r2
+ ld r14, [sp, 0]
+ ld r15, [sp, 4]
+ add sp,sp,8
+ pop blink
+ j [blink]
+
+; Prologue with maximum length.
+; Expressions like (0xFFFFFFFF + 25) force assembler to use long immediate
+; even for values that don't need it, thus letting us test maksimum prologue
+; length without having huge frames.
+.align 4
+max_length_prologue:
+ ; Variadic args
+ sub sp,sp,(0xFFFFFFFF + 25) ; 24 bytes
+ push blink
+ ; Allocate space for 13 callee-saved and 8 arg regs.
+ sub sp,sp,(0xFFFFFFFF + 1 + 21 * 4)
+ st r13, [sp, 0]
+ st r14, [sp, 4]
+ st r15, [sp, 8]
+ st r16, [sp, 12]
+ st r17, [sp, 16]
+ st r18, [sp, 20]
+ st r19, [sp, 24]
+ st r20, [sp, 28]
+ st r21, [sp, 32]
+ st r22, [sp, 36]
+ st r23, [sp, 40]
+ st r24, [sp, 44]
+ st r25, [sp, 48]
+ st r0, [sp, 52]
+ st r1, [sp, 56]
+ st r2, [sp, 60]
+ st r3, [sp, 64]
+ st r4, [sp, 68]
+ st r5, [sp, 72]
+ st r6, [sp, 76]
+ st r7, [sp, 80]
+ push fp
+ mov fp,sp
+ sub sp,sp,(0xFFFFFFFF + 1 + 16) ; Space for local variables.
+ ; End of prologue.
+ add sp,sp,24 + 21 * 4 + 16
+ j [blink]
+
+; Few tests that test that prologue analysis stops at branch. There are four
+; types of "branches": conditional and non-conditional, relative branches and
+; absolute jumps.
+
+.align 4
+branch_in_prologue:
+ push r13
+ b @.L1
+ ; This store on stack is not a prologue.
+ push r14
+.L1:
+ add r0,r1,r2
+ j.d [blink]
+ add sp,sp,4
+
+.align 4
+cond_branch_in_prologue:
+ sub_s sp,sp,8
+ st_s r13,[sp,4]
+ ; Doesn't matter if branch is taken or not.
+ breq r0,r1,@.L2
+ ; This store on stack is not a prologue.
+ st_s r14,[sp,0]
+.L2:
+ add r0,r1,r2
+ pop fp
+ j.d [blink]
+ add sp,sp,8
+
+.align 4
+jump_in_prologue:
+ push r13
+ j @.L3
+ ; This store on stack is not a prologue.
+ push r14
+.L3:
+ add r0,r1,r2
+ j.d [blink]
+ add sp,sp,4
+
+.align 4
+cond_jump_in_prologue:
+ sub_s sp,sp,8
+ st_s r13,[sp,4]
+ ; It doesn't matter if jump is taken or not - prologue analysis has to
+ ; stop before `jeq` in any case.
+ jeq @.L4
+ ; This store on stack is not a prologue.
+ st_s r14,[sp,0]
+.L4:
+ add r0,r1,r2
+ j.d [blink]
+ add sp,sp,8
+
+.align 4
+predicated_insn:
+ sub_s sp,sp,12
+ st_s r13,[sp,8]
+ st_s r15,[sp,0]
+ ; Use SUB SP,SP,0 because it is otherwise a valid instruction for
+ ; prologue, so it will halt analysis purely because of its predicate.
+ sub.eq sp,sp,0 ; This is not a prologue anymore.
+ st_s r14,[sp,4]
+ add sp,sp,12
+ j [blink]
+
+; Loops should halt prologue analysis.
+
+.align 4
+loop_in_prologue:
+ push r25
+ push lp_count
+ mov lp_count, 4
+ lp @.Lloop_end1
+ push r26 ; Not part of prologue.
+ add r0, r1, r2
+.Lloop_end1:
+ add r1, r1, r2
+ pop r26
+ add sp,sp,8
+ pop r25
+ j [blink]
+
+; Store of a constant value (not a register).
+
+.align 4
+store_constant:
+ sub_s sp,sp,12
+ st_s r13,[sp,8]
+ st 0xdeadbeef,[sp,0]
+ st_s r14,[sp,4]
+ add sp,sp,12
+ j [blink]
+
+; Test that store to immediate address halts prologue analysis.
+.align 4
+st_c_limm:
+ push r15
+ st r14,[@some_variable]
+ push r13
+ add sp,sp,8
+ j [blink]
+
+; Store with AB writeback mode.
+
+.align 4
+st_ab_writeback:
+ sub sp,sp,8
+ st r13,[sp,4]
+ st.ab r14,[sp,-4]
+ st r15,[sp,0]
+ add sp,sp,12
+ j [blink]
+
+; Store of a word with AS writeback mode.
+
+.align 4
+st_as_writeback:
+ sub sp,sp,12
+ st r13,[sp,8]
+ st.as r14,[sp,1] ; ST.AS, hence address is (offset << 2).
+ st r15,[sp,0]
+ add sp,sp,12
+ j [blink]
+
+; Store of a halfword with AS writeback mode.
+
+.align 4
+sth_as_writeback:
+ sub sp,sp,12
+ st r13,[sp,8]
+ sth.as r14,[sp,2] ; STH.AS, hence address is (offset << 1).
+ st r15,[sp,0]
+ add sp,sp,12
+ j [blink]
+
+; Store of a double word with AS writeback mode. Shift is still 2, like ST!
+
+.align 4
+std_as_writeback:
+ sub sp,sp,16
+ st r13,[sp,12]
+#ifdef __ARC_LL64__
+ std.as r14,[sp,1] ; STD.AS, hence address is (offset << 2).
+#else
+ st.as r14,[sp,1] ; STD.AS, hence address is (offset << 2).
+ st.as r15,[sp,2] ; STD.AS, hence address is (offset << 2).
+#endif
+ st r16,[sp,0]
+ add sp,sp,12
+ j [blink]
+
+; Store of the halfword. R14 will not be reported as "saved".
+
+.align 4
+st_halfword:
+ sub sp,sp,12
+ st r13,[sp,8]
+ sth r14,[sp,4]
+ st r15,[sp,0]
+ add sp,sp,12
+ j [blink]
+
+; Store of the halfword. R14 will not be reported as "saved".
+
+.align 4
+sts_halfword:
+ sub sp,sp,12
+ st r13,[sp,8]
+ mov r13,sp
+ sth_s r14,[r13,4]
+ st r15,[sp,0]
+ add sp,sp,12
+ j [blink]
+
+; Store of the byte. R14 will not be reported as "saved".
+
+.align 4
+st_byte:
+ sub sp,sp,12
+ st r13,[sp,8]
+ stb r14,[sp,4]
+ st r15,[sp,0]
+ add sp,sp,12
+ j [blink]
+
+; Store of the byte. R14 will not be reported as "saved".
+
+.align 4
+sts_byte:
+ sub sp,sp,12
+ st r13,[sp,8]
+ mov r13,sp
+ stb_s r14,[r13,4]
+ st r15,[sp,0]
+ add sp,sp,12
+ j [blink]
+
+; Store of the byte. R14 will not be reported as "saved".
+
+.align 4
+sts_byte_sp:
+ sub sp,sp,12
+ st r13,[sp,8]
+ stb_s r14,[sp,4]
+ st r15,[sp,0]
+ add sp,sp,12
+ j [blink]
+
+; Double word store, optionally available for ARC HS.
+
+.align 4
+st_double:
+ sub sp,sp,8
+#ifdef __ARC_LL64__
+ std r14,[sp,0]
+ std.aw r18,[sp,-8]
+ std.aw 0xdeadbeef,[sp,-8]
+#else
+ st r14,[sp,0]
+ st r15,[sp,4]
+ st.aw r19,[sp,-4]
+ st.aw r18,[sp,-4]
+ sub sp,sp,8
+#endif
+ add sp,sp,24
+ j [blink]
+
+; Store relative to some register with a known value.
+
+.align 4
+r_relative_store:
+ sub_s sp,sp,12
+ st_s r13,[sp,8]
+ mov r13,sp
+ ; Check for both mov and mov_s in one testcase.
+ mov_s r12,r13
+ st r15,[r12,0]
+ st_s r14,[sp,4]
+ add sp,sp,12
+ j [blink]
+
+; Store relative to some register with a known value using sub.
+; Like a previous test, but register is assigned via sub, instead of mov.
+
+.align 4
+r_relative_sub_store:
+ ; Following is a complicated way to construct frame like this:
+ ; sub_s sp,sp,12
+ ; st_s r13,[sp,8]
+ ; st_s r14,[sp,4]
+ ; st_s r15,[sp,0]
+ sub_s sp,sp,12
+ st_s r13,[sp,8]
+ sub r13,sp,4
+ st r14,[r13,8]
+ st_s r15,[sp,0]
+ add sp,sp,12
+ j [blink]
+
+; Like r_relative_store, but using st_s c,[b,u7] which has different opcode.
+
+.align 4
+r_relative_store_st_s:
+ sub_s sp,sp,12
+ st_s r13,[sp,8]
+ mov r13,sp
+ st_s r15,[r13,4]
+ st_s r14,[sp,0]
+ add sp,sp,12
+ j [blink]
+
+; Store relative to some register with a unknown value.
+
+.align 4
+r_relative_store_unknown:
+ sub_s sp,sp,12
+ st_s r13,[sp,8]
+ st r15,[gp,0] ; GP value is not relative to SP.
+ st_s r14,[sp,4]
+ add sp,sp,12
+ j [blink]
+
+; Store relative to some register with a unknown value, using st_s r0,[gp,s11].
+
+.align 4
+st_s_r0gp:
+ sub_s sp,sp,12
+ st_s r13,[sp,8]
+ st_s r0,[gp,0] ; GP value is not relative to SP.
+ st_s r14,[sp,4]
+ add sp,sp,12
+ j [blink]
+
+; Check prologue that uses `push_s RR` instructions. `push_s b` and `push_s
+; blink` use slightly different subopcodes.
+
+.align 4
+push_s_prologue:
+ push_s r12
+ push_s r0
+ push_s r3
+ push_s r13
+ push_s r1
+ push_s r14
+ push_s r15
+ push_s r2
+ push_s blink ; Also tested in mini_prologue ().
+ add sp,sp,(4 * 9)
+ j [blink]
+
+; Check for SUB_S c,b,u3 presence - it doesn't affect prologue.
+
+.align 4
+sub_s_cbu3:
+ push_s r13
+ sub_s r0,r1,3
+ push_s r14
+ add sp,sp,8
+ j [blink]
+
+; Check for SUB_S b,b,c presence - it doesn't affect prologue.
+
+.align 4
+sub_s_bbc:
+ push_s r13
+ sub_s r0,r0,r1
+ push_s r0
+ push_s r1
+ push_s r14
+ add sp,sp,16
+ j [blink]
+
+; Check for SUB_S b,b,u5.
+
+.align 4
+sub_s_bbu5:
+ push_s r13
+ sub_s r2,r2,14
+ push_s r2
+ push_s r14
+ add sp,sp,12
+ j [blink]
+
+; Check for SUB 0,b,c, which is effectively a noop (but it can set status
+; flags). It shouldn't stop prologue analysis.
+
+.align 4
+sub_0bc:
+ push_s r13
+ sub 0,r1,r2
+ sub.f 0,r3,r4
+ push_s r14
+ add sp,sp,8
+ j [blink]
+
+; Check for SUB a,limm,c.
+
+.align 4
+sub_alimmb:
+ push_s r13
+ sub r13,0xdeadbeef,r14
+ push_s r14
+ add sp,sp,8
+ j [blink]
+
+; Check for sub_s.ne b,b,b. Has a condition code, hence should halt prologue.
+
+.align 4
+sub_s_ne_bbb:
+ push_s r13
+ sub_s.ne r13,r13,r13
+ push_s r14
+ add sp,sp,8
+ j [blink]
+
+; Check MOV that uses LIMM values.
+
+.align 4
+mov_limm:
+ push_s r13
+ mov r13,0xdeadbeef
+ push_s r14
+ add sp,sp,4
+ pop_s r13
+ j [blink]
+
+; Check MOV 0,c.
+
+.align 4
+mov0c_limm:
+ push_s r13
+ mov 0,r13
+ push_s r14
+ add sp,sp,4
+ pop_s r13
+ j [blink]
+
+; Check that MOV_S h,s3 doesn't prevent prologue analysis.
+
+.align 4
+mov_s_hs3:
+ push_s r13
+ mov_s r5,1
+ push_s r14
+ add sp,sp,8
+ j [blink]
+
+; Check that MOV_S b,u8 doesn't prevent prologue analysis.
+
+.align 4
+mov_s_bu8:
+ push_s r13
+ mov_s r12,250
+ push_s r14
+ add sp,sp,8
+ j [blink]
+
+; Check that `mov_s.ne b,h` halts prologue analysis.
+
+.align 4
+mov_s_ne_bh:
+ push_s r13
+ mov_s.ne r13,r5
+ push_s r14
+ add sp,sp,8
+ j [blink]
+
+; Check that register R12 which original value is not stored will not pop-up in
+; the "Saved registers" list.
+
+.align 4
+unstored_reg:
+ sub_s sp,sp,12
+ st_s r13,[sp,0]
+ st_s r14,[sp,4]
+ mov r12,0x42
+ st_s r12,[sp,8]
+ add sp,sp,12
+ j [blink]
+
+; Two stores at the same adddress. GDB should report only the R14.
+
+.align 4
+double_store:
+ sub_s sp,sp,4
+ st_s r13,[sp,0]
+ st_s r14,[sp,0]
+ add sp,sp,4
+ j [blink]
+
+; Test for a case where callee has an alloca or anything else that might
+; modify stack dynamically in the function body - after the prologue.
+; This assumes that FP is set properly, so that GDB can use it - this holds
+; true for frames generated by GCC.
+
+.align 4
+alloca_outer:
+ sub sp,sp,8
+ st blink,[sp,4]
+ st fp,[sp,0]
+ mov fp,sp
+ add r0,r1,r2 ; Not a prologue anymore.
+ sub sp,sp,8
+ bl @alloca_inner
+ add sp,sp,8
+ ld fp,[sp,0]
+ ld blink,[sp,4]
+ j.d [blink]
+ add sp,sp,8
+
+.align 4
+alloca_inner:
+ push r13
+ push r14
+ add sp,sp,8
+ j [blink]
+
+
+.align 4
+main:
+ push blink
+ # Create small section for GP-relative accesses.
+ push gp
+ sub sp,sp,16
+ add gp,sp,8
+ bl @standard_prologue
+ bl @mini_prologue
+ bl @no_subsp_prologue
+ bl @leaf_prologue
+ bl @pushfp_prologue
+ bl @fp_prologue_with_store
+ bl @noncallee_saved_regs_r12_st
+ bl @noncallee_saved_regs_r12_push
+ bl @noncallee_saved_regs_r2_push
+ bl @noncallee_saved_regs_gp_push
+ bl @noncallee_saved_regs_lp_count
+ bl @noncallee_saved_regs_blink_out_of_prologue
+ bl @arg_regs_fp
+ bl @arg_regs_fp_mov_s
+ bl @arg_regs_sp
+ bl @enter_s_nop
+ bl @enter_s_blink
+ bl @enter_s_fp
+ bl @enter_s_r13
+ bl @enter_s_r15
+ bl @enter_s_all
+ bl @nested_prologue_outer
+ bl @max_length_prologue
+ bl @branch_in_prologue
+ bl @cond_branch_in_prologue
+ bl @jump_in_prologue
+ bl @cond_jump_in_prologue
+ bl @predicated_insn
+ bl @loop_in_prologue
+ bl @store_constant
+ bl @st_c_limm
+ bl @st_ab_writeback
+ bl @st_as_writeback
+ bl @sth_as_writeback
+ bl @std_as_writeback
+ bl @st_halfword
+ bl @sts_halfword
+ bl @st_byte
+ bl @sts_byte
+ bl @sts_byte_sp
+ bl @st_double
+ bl @r_relative_store
+ bl @r_relative_sub_store
+ bl @r_relative_store_st_s
+ bl @r_relative_store_unknown
+ bl @st_s_r0gp
+ bl @push_s_prologue
+ bl @sub_s_cbu3
+ bl @sub_s_bbc
+ bl @sub_s_bbu5
+ bl @sub_0bc
+ bl @sub_alimmb
+ bl @sub_s_ne_bbb
+ bl @mov_limm
+ bl @mov0c_limm
+ bl @mov_s_hs3
+ bl @mov_s_bu8
+ bl @mov_s_ne_bh
+ bl @unstored_reg
+ bl @double_store
+ bl @alloca_outer
+ add sp,sp,16
+ pop gp
+ pop blink
+ j_s [blink]
+
+.align 4
--- /dev/null
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2017 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/>.
+
+if {![istarget "arc*-*-*"]} then {
+ verbose "Skipping ARC prologue test."
+ return
+}
+
+standard_testfile .S
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } {
+ return -1
+}
+
+if ![runto_main] {
+ fail "Can't run to main"
+ return 0
+}
+
+# Convert list of saved registers and their offsets to a GDB string.
+proc saved_regs_to_str { savedregs funcname } {
+ set str ""
+ # If blink is stored, that it is present twice in saved regs - as blink and
+ # as pc.
+ set has_blink 0
+ set blink_addr 0
+ foreach r $savedregs {
+ if { [llength $r] == 1 } {
+ append str ".*$r at.*"
+ } else {
+ set name [lindex $r 0]
+ set offset [lindex $r 1]
+ set addr [get_hexadecimal_valueof "\$sp+$offset" 0 \
+ "get value of $name@sp+$offset in $funcname"]
+ append str "\\s*$name at $addr,?"
+ if { $name == "blink" } {
+ set has_blink 1
+ set blink_addr $addr
+ }
+ }
+ }
+ if { $has_blink == 1 } {
+ append str "\\s*pc at $blink_addr"
+ }
+ return $str
+}
+
+# Arguments:
+# funcname - name of function to test
+# savedregs - list of register saved in the frame. Each entry can be either
+# a string, where it is a register name, or it is a list of two
+# items - name of register and it's offset relatively to SP in
+# the memory. SP value is at the moment of prologue end.
+# fp_offset - if not an empty string, then proc will test that FP register
+# has a value that is (SP + offset).
+
+proc prologue_test {funcname {savedregs ""} {fp_offset ""} } {
+ global hex
+ gdb_breakpoint $funcname temporary
+ gdb_continue_to_breakpoint $funcname
+ gdb_test "backtrace 10" \
+ "#0\[ \t\]*$hex in $funcname .*\r\n#1\[ \t\]*$hex in main.*" \
+ "backtrace in $funcname"
+ if { $savedregs != "" } {
+ set str [saved_regs_to_str $savedregs $funcname]
+ gdb_test "info frame" \
+ ".*Saved registers:$str" \
+ "saved registers in $funcname"
+ }
+ if { $fp_offset != "" } {
+ set sp [get_integer_valueof \$sp -1 "get value of sp in $funcname"]
+ set fp_val [expr $sp + $fp_offset]
+ set fp_real_val \
+ [get_integer_valueof \$fp 0 "get value of fp in $funcname"]
+ if { $fp_real_val != $fp_val } {
+ fail "check FP value in $funcname"
+ } else {
+ pass "check FP value in $funcname"
+ }
+ }
+}
+
+
+prologue_test "standard_prologue" { {r13 0} {r14 4} {r18 8} {blink 12} }
+prologue_test "mini_prologue" { {r13 0} {r14 8} {r15 4} {blink 12} }
+prologue_test "no_subsp_prologue" { {r13 8} {r20 4} {r25 0} {blink 12} }
+prologue_test "leaf_prologue" { {r13 0} {r15 4} }
+prologue_test "pushfp_prologue" { {r13 8} {r14 4} {fp 0} } 0
+prologue_test "fp_prologue_with_store" { {r13 12} {r14 8} {r15 0} {fp 4} } 4
+prologue_test "noncallee_saved_regs_r12_st" { {r12 0} {r13 4} }
+# Register offset is specified relatively to SP at the prologue end, so
+# "push r12" hasn't been executed at this moment.
+prologue_test "noncallee_saved_regs_r12_push" { {r12 0} {r13 4} }
+prologue_test "noncallee_saved_regs_r2_push" { {r2 0} {r13 4} }
+prologue_test "noncallee_saved_regs_gp_push" { {r25 4} {gp 0} }
+prologue_test "noncallee_saved_regs_lp_count" { {r25 4} {lp_count 0} }
+prologue_test "noncallee_saved_regs_blink_out_of_prologue" { {r25 8} {gp 4} \
+ {blink 0}}
+# Argument registers are not reported as "saved" regs.
+prologue_test "arg_regs_fp" { {r0 12} {r1 8} {r7 4} {r8 0} {fp 16} } 16
+prologue_test "arg_regs_fp_mov_s" { {r0 4} {r8 0} {fp 8} } 8
+prologue_test "arg_regs_sp" { {r0 0} {r1 4} {r7 8} {r8 12} {r13 16} {r14 20} }
+prologue_test "enter_s_nop"
+prologue_test "enter_s_blink" { {blink 0} }
+prologue_test "enter_s_fp" { {fp 0} } 0
+# Layout of registers as stored by enter_s doesn't conform to ARC ABI.
+prologue_test "enter_s_r13" { {r13 4} {fp 8} {blink 0} } 0
+prologue_test "enter_s_r15" { {r13 0} {r14 4} {r15 8} } 0
+# This enter_s saves GP, however because it is not a "calle-saved register",
+# GDB will not report it as "saved register" (but maybe it should). GP is at
+# offset 56.
+prologue_test "enter_s_all" { {r13 4} {r14 8} {r15 12} {r16 16} {r17 20} \
+ {r18 24} {r19 28} {r20 32} {r21 36} {r22 40} {r23 44} {r24 48} {r25 52} \
+ {gp 56} {fp 60} {blink 0} } 0
+
+# Test more levels of backtrace.
+gdb_breakpoint nested_prologue_inner temporary
+gdb_continue_to_breakpoint nested_prologue_inner
+gdb_test "backtrace 10" \
+ "#0\[ \t\]*$hex in nested_prologue_inner .*\r\n#1\[ \t\]*$hex in nested_prologue_outer .*\r\n#2\[ \t\]*$hex in main.*" \
+ "backtrace in nested_prologue_inner"
+set regs [saved_regs_to_str {r13 r18} "nested_prologue_inner"]
+gdb_test "info frame" ".*Saved registers:$regs" \
+ "saved registers in nested_prologue_inner"
+set regs [saved_regs_to_str {r14 r15 blink} "nested_prologue_inner"]
+gdb_test "info frame 1" ".*Saved registers:$regs" \
+ "saved registers in nested_prologue_outer"
+
+# sub sp,sp for local variables is part of prologue, hence should be added to
+# all of those offsets.
+prologue_test "max_length_prologue" { {r0 72} {r1 76} {r2 80} {r3 84} {r4 88} \
+ {r5 92} {r6 96} {r7 100} {r13 20} {r14 24} {r15 28} {r16 32} \
+ {r17 36} {r18 40} {r19 44} {r20 48} {r21 52} {r22 56} {r23 60} {r24 64} \
+ {r25 68} {fp 16} {blink 104} }
+
+prologue_test "branch_in_prologue" { {r13 0} }
+prologue_test "cond_branch_in_prologue" { {r13 4} }
+prologue_test "jump_in_prologue" { {r13 0} }
+prologue_test "cond_jump_in_prologue" { {r13 4} }
+prologue_test "predicated_insn" { {r13 8} {r15 0} }
+prologue_test "loop_in_prologue" { {r25 4} {lp_count 0} }
+prologue_test "store_constant" { {r13 8} {r14 4} }
+prologue_test "st_c_limm" { {r15 0} }
+prologue_test "st_ab_writeback" { {r13 8} {r14 4} {r15 0} }
+prologue_test "st_as_writeback" { {r13 8} {r14 4} {r15 0} }
+prologue_test "sth_as_writeback" { {r13 8} {r15 0} }
+prologue_test "std_as_writeback" { {r13 12} {r14 4} {r15 8} {r16 0} }
+prologue_test "st_halfword" { {r13 8} {r15 0} }
+prologue_test "sts_halfword" { {r13 8} {r15 0} }
+prologue_test "st_byte" { {r13 8} {r15 0} }
+prologue_test "sts_byte" { {r13 8} {r15 0} }
+prologue_test "sts_byte_sp" { {r13 8} {r15 0} }
+prologue_test "st_double" { {r14 16} {r15 20} {r18 8} {r19 12}}
+prologue_test "r_relative_store" { {r13 8} {r14 4} {r15 0} }
+prologue_test "r_relative_sub_store" { {r13 8} {r14 4} {r15 0} }
+prologue_test "r_relative_store_st_s" { {r13 8} {r14 0} {r15 4} }
+prologue_test "r_relative_store_unknown" { {r13 8} }
+prologue_test "st_s_r0gp" { {r13 8} }
+prologue_test "push_s_prologue" { {r0 28} {r1 16} {r2 4} {r3 24} {r12 32} \
+ {r13 20} {r14 12} {r15 8} {blink 0}}
+prologue_test "sub_s_cbu3" { {r13 4} {r14 0} }
+prologue_test "sub_s_bbc" { {r1 4} {r13 12} {r14 0} }
+prologue_test "sub_s_bbu5" { {r13 8} {r14 0} }
+prologue_test "sub_0bc" { {r13 4} {r14 0} }
+prologue_test "sub_alimmb" { {r13 4} {r14 0} }
+prologue_test "sub_s_ne_bbb" { {r13 0} }
+prologue_test "mov_limm" { {r13 4} {r14 0} }
+prologue_test "mov0c_limm" { {r13 4} {r14 0} }
+prologue_test "mov_s_hs3" { {r13 4} {r14 0} }
+prologue_test "mov_s_bu8" { {r13 4} {r14 0} }
+prologue_test "mov_s_ne_bh" { {r13 0} }
+prologue_test "unstored_reg" { {r13 0} {r14 4} }
+prologue_test "double_store" { {r14 0} }
+
+# alloca() tests
+gdb_breakpoint alloca_inner temporary
+gdb_continue_to_breakpoint alloca_inner
+gdb_test "backtrace 3" \
+ "#0\[ \t\]*$hex in alloca_inner .*\r\n#1\[ \t\]*$hex in alloca_outer .*\r\n#2\[ \t\]*$hex in main.*" \
+ "backtrace in alloca_inner"
+set regs [saved_regs_to_str {r13 r14} alloca_inner]
+gdb_test "info frame 0" ".*Saved registers:$regs" \
+ "saved registers in alloca_inner"
+set regs [saved_regs_to_str {fp blink} alloca_inner]
+gdb_test "info frame 1" ".*Saved registers:$regs" \
+ "saved registers in alloca_outer"
+