arc: Add prologue analysis

Add a prologue analysis that recognizes all instructions that may happen in
compiler-generated prologue, including various stores, core register moves,
subtraction and ENTER_S instruction that does a lot of prologue actions through
microcode.

Testcases cover various prologue scenarios, including instructions that are
spread across multiple 16-bit encodings (for example there are 7 encodings of
store instruction).

gdb/ChangeLog:

yyyy-mm-dd  Anton Kolesov  <anton.kolesov@synopsys.com>

	* arc-tdep.c (arc_frame_cache): Add support for prologue analysis.
	(arc_skip_prologue): Likewise.
	(arc_make_frame_cache): Likewise.
	(arc_pv_get_operand): New function.
	(arc_is_in_prologue): Likewise.
	(arc_analyze_prologue): Likewise.
	(arc_print_frame_cache): Likewise.
	(MAX_PROLOGUE_LENGTH): New constant.

gdb/doc/ChangeLog:

yyyy-mm-dd  Anton Kolesov  <anton.kolesov@synopsys.com>

	* gdb.texinfo (Synopsys ARC): Document "set debug arc 2".

gdb/testsuite/ChangeLog:

yyyy-mm-dd  Anton Kolesov  <anton.kolesov@synopsys.com>

	* gdb.arch/arc-analyze-prologue.S: New file.
	* gdb.arch/arc-analyze-prologue.exp: Likewise.
This commit is contained in:
Anton Kolesov 2017-02-10 14:12:09 +03:00
parent eea787570f
commit fe5f7374be
7 changed files with 1603 additions and 22 deletions

View File

@ -1,3 +1,14 @@
2017-03-28 Anton Kolesov <anton.kolesov@synopsys.com>
* arc-tdep.c (arc_frame_cache): Add support for prologue analysis.
(arc_skip_prologue): Likewise.
(arc_make_frame_cache): Likewise.
(arc_pv_get_operand): New function.
(arc_is_in_prologue): Likewise.
(arc_analyze_prologue): Likewise.
(arc_print_frame_cache): Likewise.
(MAX_PROLOGUE_LENGTH): New constant.
2017-03-28 Anton Kolesov <anton.kolesov@synopsys.com>
* configure.tgt: Add arc-insn.o.

View File

@ -28,6 +28,7 @@
#include "gdbcore.h"
#include "gdbcmd.h"
#include "objfiles.h"
#include "prologue-value.h"
#include "trad-frame.h"
/* ARC header files. */
@ -42,8 +43,7 @@
#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
{
@ -52,7 +52,35 @@ 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;
};
@ -117,6 +145,10 @@ static const char *const core_arcompact_register_names[] = {
"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. */
@ -227,10 +259,6 @@ arc_insn_get_memory_offset (const struct arc_instruction &insn)
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)
{
@ -913,6 +941,247 @@ arc_frame_base_address (struct frame_info *this_frame, void **prologue_cache)
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)
@ -937,6 +1206,146 @@ arc_disassemble_info (struct gdbarch *gdbarch)
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
@ -966,15 +1375,19 @@ arc_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
/* 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.
@ -1114,6 +1527,28 @@ arc_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
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 *
@ -1125,12 +1560,11 @@ arc_make_frame_cache (struct frame_info *this_frame)
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;
@ -1142,18 +1576,42 @@ arc_make_frame_cache (struct frame_info *this_frame)
}
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;
}

View File

@ -1,3 +1,7 @@
2017-03-28 Anton Kolesov <anton.kolesov@synopsys.com>
* gdb.texinfo (Synopsys ARC): Document "set debug arc 2".
2017-03-28 Anton Kolesov <anton.kolesov@synopsys.com>
* gdb.texinfo (Synopsys ARC): Add "maint print arc arc-instruction".

View File

@ -22098,8 +22098,7 @@ acceptable commands.
@item set debug arc
@kindex set debug arc
Control the level of ARC specific debug messages. Use 0 for no messages (the
default) and 1 for debug messages. At present higher values offer no further
messages.
default), 1 for debug messages, and 2 for even more debug messages.
@item show debug arc
@kindex show debug arc

View File

@ -1,3 +1,8 @@
2017-03-28 Anton Kolesov <anton.kolesov@synopsys.com>
* gdb.arch/arc-analyze-prologue.S: New file.
* gdb.arch/arc-analyze-prologue.exp: Likewise.
2017-03-28 Anton Kolesov <anton.kolesov@synopsys.com>
* gdb.arch/arc-decode-insn.S: New file.

View File

@ -0,0 +1,903 @@
; 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

View File

@ -0,0 +1,201 @@
# 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"