From: Anton Kolesov Date: Fri, 10 Feb 2017 11:12:09 +0000 (+0300) Subject: arc: Add prologue analysis X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=fe5f7374bef8f23ffa0fe0dee0f9b05e0a218a29;p=binutils-gdb.git 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 * 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 * gdb.texinfo (Synopsys ARC): Document "set debug arc 2". gdb/testsuite/ChangeLog: yyyy-mm-dd Anton Kolesov * gdb.arch/arc-analyze-prologue.S: New file. * gdb.arch/arc-analyze-prologue.exp: Likewise. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index cb66e81b6fa..3520eec8eca 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,14 @@ +2017-03-28 Anton Kolesov + + * 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 * configure.tgt: Add arc-insn.o. diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c index 69ac641334c..657b1eb84f4 100644 --- a/gdb/arc-tdep.c +++ b/gdb/arc-tdep.c @@ -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, ; 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, ; 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; } diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 3088f13b0ce..c542c0b46ff 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,7 @@ +2017-03-28 Anton Kolesov + + * gdb.texinfo (Synopsys ARC): Document "set debug arc 2". + 2017-03-28 Anton Kolesov * gdb.texinfo (Synopsys ARC): Add "maint print arc arc-instruction". diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 7e2fc8103d0..300d78eefb8 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -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 diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index efccd014332..5241a275013 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2017-03-28 Anton Kolesov + + * gdb.arch/arc-analyze-prologue.S: New file. + * gdb.arch/arc-analyze-prologue.exp: Likewise. + 2017-03-28 Anton Kolesov * gdb.arch/arc-decode-insn.S: New file. diff --git a/gdb/testsuite/gdb.arch/arc-analyze-prologue.S b/gdb/testsuite/gdb.arch/arc-analyze-prologue.S new file mode 100644 index 00000000000..b32897ad7e1 --- /dev/null +++ b/gdb/testsuite/gdb.arch/arc-analyze-prologue.S @@ -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 . + +.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 diff --git a/gdb/testsuite/gdb.arch/arc-analyze-prologue.exp b/gdb/testsuite/gdb.arch/arc-analyze-prologue.exp new file mode 100644 index 00000000000..12a44b51a00 --- /dev/null +++ b/gdb/testsuite/gdb.arch/arc-analyze-prologue.exp @@ -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 . + +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" +