+#include "gdbcmd.h"
+#include "objfiles.h"
+#include "osabi.h"
+#include "prologue-value.h"
+#include "target-descriptions.h"
+#include "trad-frame.h"
+
+/* ARC header files. */
+#include "opcode/arc.h"
+#include "opcodes/arc-dis.h"
+#include "arc-tdep.h"
+#include "arch/arc.h"
+
+/* Standard headers. */
+#include <algorithm>
+
+/* The frame unwind cache for ARC. */
+
+struct arc_frame_cache
+{
+ /* The stack pointer at the time this frame was created; i.e. the caller's
+ stack pointer when this function was called. It is used to identify this
+ frame. */
+ CORE_ADDR prev_sp;
+
+ /* 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;
+};
+
+/* Global debug flag. */
+
+int arc_debug;
+
+/* List of "maintenance print arc" commands. */
+
+static struct cmd_list_element *maintenance_print_arc_list = NULL;
+
+/* XML target description features. */
+
+static const char core_v2_feature_name[] = "org.gnu.gdb.arc.core.v2";
+static const char
+ core_reduced_v2_feature_name[] = "org.gnu.gdb.arc.core-reduced.v2";
+static const char
+ core_arcompact_feature_name[] = "org.gnu.gdb.arc.core.arcompact";
+static const char aux_minimal_feature_name[] = "org.gnu.gdb.arc.aux-minimal";
+
+/* XML target description known registers. */
+
+static const char *const core_v2_register_names[] = {
+ "r0", "r1", "r2", "r3",
+ "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11",
+ "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19",
+ "r20", "r21", "r22", "r23",
+ "r24", "r25", "gp", "fp",
+ "sp", "ilink", "r30", "blink",
+ "r32", "r33", "r34", "r35",
+ "r36", "r37", "r38", "r39",
+ "r40", "r41", "r42", "r43",
+ "r44", "r45", "r46", "r47",
+ "r48", "r49", "r50", "r51",
+ "r52", "r53", "r54", "r55",
+ "r56", "r57", "accl", "acch",
+ "lp_count", "reserved", "limm", "pcl",
+};
+
+static const char *const aux_minimal_register_names[] = {
+ "pc", "status32",
+};
+
+static const char *const core_arcompact_register_names[] = {
+ "r0", "r1", "r2", "r3",
+ "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11",
+ "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19",
+ "r20", "r21", "r22", "r23",
+ "r24", "r25", "gp", "fp",
+ "sp", "ilink1", "ilink2", "blink",
+ "r32", "r33", "r34", "r35",
+ "r36", "r37", "r38", "r39",
+ "r40", "r41", "r42", "r43",
+ "r44", "r45", "r46", "r47",
+ "r48", "r49", "r50", "r51",
+ "r52", "r53", "r54", "r55",
+ "r56", "r57", "r58", "r59",
+ "lp_count", "reserved", "limm", "pcl",
+};
+
+static char *arc_disassembler_options = NULL;
+
+/* Possible arc target descriptors. */
+static struct target_desc *tdesc_arc_list[ARC_SYS_TYPE_NUM];
+
+/* 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. */
+
+static ULONGEST
+arc_insn_get_operand_value (const struct arc_instruction &insn,
+ unsigned int operand_num)
+{
+ switch (insn.operands[operand_num].kind)
+ {
+ case ARC_OPERAND_KIND_LIMM:
+ gdb_assert (insn.limm_p);
+ return insn.limm_value;
+ case ARC_OPERAND_KIND_SHIMM:
+ return insn.operands[operand_num].value;
+ default:
+ /* Value in instruction is a register number. */
+ struct regcache *regcache = get_current_regcache ();
+ ULONGEST value;
+ regcache_cooked_read_unsigned (regcache,
+ insn.operands[operand_num].value,
+ &value);
+ return value;
+ }
+}
+
+/* Like arc_insn_get_operand_value, but returns a signed value. */
+
+static LONGEST
+arc_insn_get_operand_value_signed (const struct arc_instruction &insn,
+ unsigned int operand_num)
+{
+ switch (insn.operands[operand_num].kind)
+ {
+ case ARC_OPERAND_KIND_LIMM:
+ gdb_assert (insn.limm_p);
+ /* Convert unsigned raw value to signed one. This assumes 2's
+ complement arithmetic, but so is the LONG_MIN value from generic
+ defs.h and that assumption is true for ARC. */
+ gdb_static_assert (sizeof (insn.limm_value) == sizeof (int));
+ return (((LONGEST) insn.limm_value) ^ INT_MIN) - INT_MIN;
+ case ARC_OPERAND_KIND_SHIMM:
+ /* Sign conversion has been done by binutils. */
+ return insn.operands[operand_num].value;
+ default:
+ /* Value in instruction is a register number. */
+ struct regcache *regcache = get_current_regcache ();
+ LONGEST value;
+ regcache_cooked_read_signed (regcache,
+ insn.operands[operand_num].value,
+ &value);
+ return value;
+ }
+}
+
+/* Get register with base address of memory operation. */
+
+static int
+arc_insn_get_memory_base_reg (const struct arc_instruction &insn)
+{
+ /* POP_S and PUSH_S have SP as an implicit argument in a disassembler. */
+ if (insn.insn_class == PUSH || insn.insn_class == POP)
+ return ARC_SP_REGNUM;
+
+ gdb_assert (insn.insn_class == LOAD || insn.insn_class == STORE);
+
+ /* Other instructions all have at least two operands: operand 0 is data,
+ operand 1 is address. Operand 2 is offset from address. However, see
+ comment to arc_instruction.operands - in some cases, third operand may be
+ missing, namely if it is 0. */
+ gdb_assert (insn.operands_count >= 2);
+ return insn.operands[1].value;
+}
+
+/* Get offset of a memory operation INSN. */
+
+static CORE_ADDR
+arc_insn_get_memory_offset (const struct arc_instruction &insn)
+{
+ /* POP_S and PUSH_S have offset as an implicit argument in a
+ disassembler. */
+ if (insn.insn_class == POP)
+ return 4;
+ else if (insn.insn_class == PUSH)
+ return -4;
+
+ gdb_assert (insn.insn_class == LOAD || insn.insn_class == STORE);
+
+ /* Other instructions all have at least two operands: operand 0 is data,
+ operand 1 is address. Operand 2 is offset from address. However, see
+ comment to arc_instruction.operands - in some cases, third operand may be
+ missing, namely if it is 0. */
+ if (insn.operands_count < 3)
+ return 0;
+
+ CORE_ADDR value = arc_insn_get_operand_value (insn, 2);
+ /* Handle scaling. */
+ if (insn.writeback_mode == ARC_WRITEBACK_AS)
+ {
+ /* Byte data size is not valid for AS. Halfword means shift by 1 bit.
+ Word and double word means shift by 2 bits. */
+ gdb_assert (insn.data_size_mode != ARC_SCALING_B);
+ if (insn.data_size_mode == ARC_SCALING_H)
+ value <<= 1;
+ else
+ value <<= 2;
+ }
+ return value;
+}
+
+CORE_ADDR
+arc_insn_get_branch_target (const struct arc_instruction &insn)
+{
+ gdb_assert (insn.is_control_flow);
+
+ /* BI [c]: PC = nextPC + (c << 2). */
+ if (insn.insn_class == BI)
+ {
+ ULONGEST reg_value = arc_insn_get_operand_value (insn, 0);
+ return arc_insn_get_linear_next_pc (insn) + (reg_value << 2);
+ }
+ /* BIH [c]: PC = nextPC + (c << 1). */
+ else if (insn.insn_class == BIH)
+ {
+ ULONGEST reg_value = arc_insn_get_operand_value (insn, 0);
+ return arc_insn_get_linear_next_pc (insn) + (reg_value << 1);
+ }
+ /* JLI and EI. */
+ /* JLI and EI depend on optional AUX registers. Not supported right now. */
+ else if (insn.insn_class == JLI)
+ {
+ fprintf_unfiltered (gdb_stderr,
+ "JLI_S instruction is not supported by the GDB.");
+ return 0;
+ }
+ else if (insn.insn_class == EI)
+ {
+ fprintf_unfiltered (gdb_stderr,
+ "EI_S instruction is not supported by the GDB.");
+ return 0;
+ }
+ /* LEAVE_S: PC = BLINK. */
+ else if (insn.insn_class == LEAVE)
+ {
+ struct regcache *regcache = get_current_regcache ();
+ ULONGEST value;
+ regcache_cooked_read_unsigned (regcache, ARC_BLINK_REGNUM, &value);
+ return value;
+ }
+ /* BBIT0/1, BRcc: PC = currentPC + operand. */
+ else if (insn.insn_class == BBIT0 || insn.insn_class == BBIT1
+ || insn.insn_class == BRCC)
+ {
+ /* Most instructions has branch target as their sole argument. However
+ conditional brcc/bbit has it as a third operand. */
+ CORE_ADDR pcrel_addr = arc_insn_get_operand_value (insn, 2);
+
+ /* Offset is relative to the 4-byte aligned address of the current
+ instruction, hence last two bits should be truncated. */
+ return pcrel_addr + align_down (insn.address, 4);
+ }
+ /* B, Bcc, BL, BLcc, LP, LPcc: PC = currentPC + operand. */
+ else if (insn.insn_class == BRANCH || insn.insn_class == LOOP)
+ {
+ CORE_ADDR pcrel_addr = arc_insn_get_operand_value (insn, 0);
+
+ /* Offset is relative to the 4-byte aligned address of the current
+ instruction, hence last two bits should be truncated. */
+ return pcrel_addr + align_down (insn.address, 4);
+ }
+ /* J, Jcc, JL, JLcc: PC = operand. */
+ else if (insn.insn_class == JUMP)
+ {
+ /* All jumps are single-operand. */
+ return arc_insn_get_operand_value (insn, 0);
+ }
+
+ /* This is some new and unknown instruction. */
+ gdb_assert_not_reached ("Unknown branch instruction.");
+}
+
+/* Dump INSN into gdb_stdlog. */
+
+static void
+arc_insn_dump (const struct arc_instruction &insn)
+{
+ struct gdbarch *gdbarch = target_gdbarch ();
+
+ arc_print ("Dumping arc_instruction at %s\n",
+ paddress (gdbarch, insn.address));
+ arc_print ("\tlength = %u\n", insn.length);
+
+ if (!insn.valid)
+ {
+ arc_print ("\tThis is not a valid ARC instruction.\n");
+ return;
+ }
+
+ arc_print ("\tlength_with_limm = %u\n", insn.length + (insn.limm_p ? 4 : 0));
+ arc_print ("\tcc = 0x%x\n", insn.condition_code);
+ arc_print ("\tinsn_class = %u\n", insn.insn_class);
+ arc_print ("\tis_control_flow = %i\n", insn.is_control_flow);
+ arc_print ("\thas_delay_slot = %i\n", insn.has_delay_slot);
+
+ CORE_ADDR next_pc = arc_insn_get_linear_next_pc (insn);
+ arc_print ("\tlinear_next_pc = %s\n", paddress (gdbarch, next_pc));
+
+ if (insn.is_control_flow)
+ {
+ CORE_ADDR t = arc_insn_get_branch_target (insn);
+ arc_print ("\tbranch_target = %s\n", paddress (gdbarch, t));
+ }
+
+ arc_print ("\tlimm_p = %i\n", insn.limm_p);
+ if (insn.limm_p)
+ arc_print ("\tlimm_value = 0x%08x\n", insn.limm_value);
+
+ if (insn.insn_class == STORE || insn.insn_class == LOAD
+ || insn.insn_class == PUSH || insn.insn_class == POP)
+ {
+ arc_print ("\twriteback_mode = %u\n", insn.writeback_mode);
+ arc_print ("\tdata_size_mode = %u\n", insn.data_size_mode);
+ arc_print ("\tmemory_base_register = %s\n",
+ gdbarch_register_name (gdbarch,
+ arc_insn_get_memory_base_reg (insn)));
+ /* get_memory_offset returns an unsigned CORE_ADDR, but treat it as a
+ LONGEST for a nicer representation. */
+ arc_print ("\taddr_offset = %s\n",
+ plongest (arc_insn_get_memory_offset (insn)));
+ }
+
+ arc_print ("\toperands_count = %u\n", insn.operands_count);
+ for (unsigned int i = 0; i < insn.operands_count; ++i)
+ {
+ int is_reg = (insn.operands[i].kind == ARC_OPERAND_KIND_REG);
+
+ arc_print ("\toperand[%u] = {\n", i);
+ arc_print ("\t\tis_reg = %i\n", is_reg);
+ if (is_reg)
+ arc_print ("\t\tregister = %s\n",
+ gdbarch_register_name (gdbarch, insn.operands[i].value));
+ /* Don't know if this value is signed or not, so print both
+ representations. This tends to look quite ugly, especially for big
+ numbers. */
+ arc_print ("\t\tunsigned value = %s\n",
+ pulongest (arc_insn_get_operand_value (insn, i)));
+ arc_print ("\t\tsigned value = %s\n",
+ plongest (arc_insn_get_operand_value_signed (insn, i)));
+ arc_print ("\t}\n");
+ }
+}
+
+CORE_ADDR
+arc_insn_get_linear_next_pc (const struct arc_instruction &insn)
+{
+ /* In ARC long immediate is always 4 bytes. */
+ return (insn.address + insn.length + (insn.limm_p ? 4 : 0));
+}
+
+/* Implement the "write_pc" gdbarch method.
+
+ In ARC PC register is a normal register so in most cases setting PC value
+ is a straightforward process: debugger just writes PC value. However it
+ gets trickier in case when current instruction is an instruction in delay
+ slot. In this case CPU will execute instruction at current PC value, then
+ will set PC to the current value of BTA register; also current instruction
+ cannot be branch/jump and some of the other instruction types. Thus if
+ debugger would try to just change PC value in this case, this instruction
+ will get executed, but then core will "jump" to the original branch target.
+
+ Whether current instruction is a delay-slot instruction or not is indicated
+ by DE bit in STATUS32 register indicates if current instruction is a delay
+ slot instruction. This bit is writable by debug host, which allows debug
+ host to prevent core from jumping after the delay slot instruction. It
+ also works in another direction: setting this bit will make core to treat
+ any current instructions as a delay slot instruction and to set PC to the
+ current value of BTA register.
+
+ To workaround issues with changing PC register while in delay slot
+ instruction, debugger should check for the STATUS32.DE bit and reset it if
+ it is set. No other change is required in this function. Most common
+ case, where this function might be required is calling inferior functions
+ from debugger. Generic GDB logic handles this pretty well: current values
+ of registers are stored, value of PC is changed (that is the job of this
+ function), and after inferior function is executed, GDB restores all
+ registers, include BTA and STATUS32, which also means that core is returned
+ to its original state of being halted on delay slot instructions.
+
+ This method is useless for ARC 600, because it doesn't have externally
+ exposed BTA register. In the case of ARC 600 it is impossible to restore
+ core to its state in all occasions thus core should never be halted (from
+ the perspective of debugger host) in the delay slot. */
+
+static void
+arc_write_pc (struct regcache *regcache, CORE_ADDR new_pc)
+{
+ struct gdbarch *gdbarch = regcache->arch ();
+
+ if (arc_debug)
+ debug_printf ("arc: Writing PC, new value=%s\n",
+ paddress (gdbarch, new_pc));
+
+ regcache_cooked_write_unsigned (regcache, gdbarch_pc_regnum (gdbarch),
+ new_pc);
+
+ ULONGEST status32;
+ regcache_cooked_read_unsigned (regcache, gdbarch_ps_regnum (gdbarch),
+ &status32);
+
+ /* Mask for DE bit is 0x40. */
+ if (status32 & 0x40)
+ {
+ if (arc_debug)
+ {
+ debug_printf ("arc: Changing PC while in delay slot. Will "
+ "reset STATUS32.DE bit to zero. Value of STATUS32 "
+ "register is 0x%s\n",
+ phex (status32, ARC_REGISTER_SIZE));
+ }
+
+ /* Reset bit and write to the cache. */
+ status32 &= ~0x40;
+ regcache_cooked_write_unsigned (regcache, gdbarch_ps_regnum (gdbarch),
+ status32);
+ }
+}
+
+/* Implement the "virtual_frame_pointer" gdbarch method.
+
+ According to ABI the FP (r27) is used to point to the middle of the current
+ stack frame, just below the saved FP and before local variables, register
+ spill area and outgoing args. However for optimization levels above O2 and
+ in any case in leaf functions, the frame pointer is usually not set at all.
+ The exception being when handling nested functions.
+
+ We use this function to return a "virtual" frame pointer, marking the start
+ of the current stack frame as a register-offset pair. If the FP is not
+ being used, then it should return SP, with an offset of the frame size.
+
+ The current implementation doesn't actually know the frame size, nor
+ whether the FP is actually being used, so for now we just return SP and an
+ offset of zero. This is no worse than other architectures, but is needed
+ to avoid assertion failures.
+
+ TODO: Can we determine the frame size to get a correct offset?
+
+ PC is a program counter where we need the virtual FP. REG_PTR is the base
+ register used for the virtual FP. OFFSET_PTR is the offset used for the
+ virtual FP. */
+
+static void
+arc_virtual_frame_pointer (struct gdbarch *gdbarch, CORE_ADDR pc,
+ int *reg_ptr, LONGEST *offset_ptr)
+{
+ *reg_ptr = gdbarch_sp_regnum (gdbarch);
+ *offset_ptr = 0;
+}
+
+/* Implement the "push_dummy_call" gdbarch method.
+
+ Stack Frame Layout
+
+ This shows the layout of the stack frame for the general case of a
+ function call; a given function might not have a variable number of
+ arguments or local variables, or might not save any registers, so it would
+ not have the corresponding frame areas. Additionally, a leaf function
+ (i.e. one which calls no other functions) does not need to save the
+ contents of the BLINK register (which holds its return address), and a
+ function might not have a frame pointer.
+
+ The stack grows downward, so SP points below FP in memory; SP always
+ points to the last used word on the stack, not the first one.
+
+ | | |
+ | arg word N | | caller's
+ | : | | frame
+ | arg word 10 | |
+ | arg word 9 | |
+ old SP ---> +-----------------------+ --+
+ | | |
+ | callee-saved | |
+ | registers | |
+ | including fp, blink | |
+ | | | callee's
+ new FP ---> +-----------------------+ | frame
+ | | |
+ | local | |
+ | variables | |
+ | | |
+ | register | |
+ | spill area | |
+ | | |
+ | outgoing args | |
+ | | |
+ new SP ---> +-----------------------+ --+
+ | |
+ | unused |
+ | |
+ |
+ |
+ V
+ downwards
+
+ The list of arguments to be passed to a function is considered to be a
+ sequence of _N_ words (as though all the parameters were stored in order in
+ memory with each parameter occupying an integral number of words). Words
+ 1..8 are passed in registers 0..7; if the function has more than 8 words of
+ arguments then words 9..@em N are passed on the stack in the caller's frame.
+
+ If the function has a variable number of arguments, e.g. it has a form such
+ as `function (p1, p2, ...);' and _P_ words are required to hold the values
+ of the named parameters (which are passed in registers 0..@em P -1), then
+ the remaining 8 - _P_ words passed in registers _P_..7 are spilled into the
+ top of the frame so that the anonymous parameter words occupy a continuous
+ region.
+
+ Any arguments are already in target byte order. We just need to store
+ them!
+
+ BP_ADDR is the return address where breakpoint must be placed. NARGS is
+ the number of arguments to the function. ARGS is the arguments values (in
+ target byte order). SP is the Current value of SP register. STRUCT_RETURN
+ is TRUE if structures are returned by the function. STRUCT_ADDR is the
+ hidden address for returning a struct. Returns SP of a new frame. */
+
+static CORE_ADDR
+arc_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
+ struct regcache *regcache, CORE_ADDR bp_addr, int nargs,
+ struct value **args, CORE_ADDR sp,
+ function_call_return_method return_method,
+ CORE_ADDR struct_addr)
+{
+ if (arc_debug)
+ debug_printf ("arc: push_dummy_call (nargs = %d)\n", nargs);
+
+ int arg_reg = ARC_FIRST_ARG_REGNUM;
+
+ /* Push the return address. */
+ regcache_cooked_write_unsigned (regcache, ARC_BLINK_REGNUM, bp_addr);
+
+ /* Are we returning a value using a structure return instead of a normal
+ value return? If so, struct_addr is the address of the reserved space for
+ the return structure to be written on the stack, and that address is
+ passed to that function as a hidden first argument. */
+ if (return_method == return_method_struct)
+ {
+ /* Pass the return address in the first argument register. */
+ regcache_cooked_write_unsigned (regcache, arg_reg, struct_addr);
+
+ if (arc_debug)
+ debug_printf ("arc: struct return address %s passed in R%d",
+ print_core_address (gdbarch, struct_addr), arg_reg);
+
+ arg_reg++;
+ }
+
+ if (nargs > 0)
+ {
+ unsigned int total_space = 0;
+
+ /* How much space do the arguments occupy in total? Must round each
+ argument's size up to an integral number of words. */
+ for (int i = 0; i < nargs; i++)
+ {
+ unsigned int len = TYPE_LENGTH (value_type (args[i]));
+ unsigned int space = align_up (len, 4);
+
+ total_space += space;
+
+ if (arc_debug)
+ debug_printf ("arc: arg %d: %u bytes -> %u\n", i, len, space);
+ }
+
+ /* Allocate a buffer to hold a memory image of the arguments. */
+ gdb_byte *memory_image = XCNEWVEC (gdb_byte, total_space);
+
+ /* Now copy all of the arguments into the buffer, correctly aligned. */
+ gdb_byte *data = memory_image;
+ for (int i = 0; i < nargs; i++)
+ {
+ unsigned int len = TYPE_LENGTH (value_type (args[i]));
+ unsigned int space = align_up (len, 4);
+
+ memcpy (data, value_contents (args[i]), (size_t) len);
+ if (arc_debug)
+ debug_printf ("arc: copying arg %d, val 0x%08x, len %d to mem\n",
+ i, *((int *) value_contents (args[i])), len);
+
+ data += space;
+ }
+
+ /* Now load as much as possible of the memory image into registers. */
+ data = memory_image;
+ while (arg_reg <= ARC_LAST_ARG_REGNUM)
+ {
+ if (arc_debug)
+ debug_printf ("arc: passing 0x%02x%02x%02x%02x in register R%d\n",
+ data[0], data[1], data[2], data[3], arg_reg);
+
+ /* Note we don't use write_unsigned here, since that would convert
+ the byte order, but we are already in the correct byte order. */
+ regcache->cooked_write (arg_reg, data);
+
+ data += ARC_REGISTER_SIZE;
+ total_space -= ARC_REGISTER_SIZE;
+
+ /* All the data is now in registers. */
+ if (total_space == 0)
+ break;
+
+ arg_reg++;
+ }
+
+ /* If there is any data left, push it onto the stack (in a single write
+ operation). */
+ if (total_space > 0)
+ {
+ if (arc_debug)
+ debug_printf ("arc: passing %d bytes on stack\n", total_space);
+
+ sp -= total_space;
+ write_memory (sp, data, (int) total_space);
+ }
+
+ xfree (memory_image);
+ }
+
+ /* Finally, update the SP register. */
+ regcache_cooked_write_unsigned (regcache, gdbarch_sp_regnum (gdbarch), sp);
+
+ return sp;
+}
+
+/* Implement the "push_dummy_code" gdbarch method.
+
+ We don't actually push any code. We just identify where a breakpoint can
+ be inserted to which we are can return and the resume address where we
+ should be called.
+
+ ARC does not necessarily have an executable stack, so we can't put the
+ return breakpoint there. Instead we put it at the entry point of the
+ function. This means the SP is unchanged.
+
+ SP is a current stack pointer FUNADDR is an address of the function to be
+ called. ARGS is arguments to pass. NARGS is a number of args to pass.
+ VALUE_TYPE is a type of value returned. REAL_PC is a resume address when
+ the function is called. BP_ADDR is an address where breakpoint should be
+ set. Returns the updated stack pointer. */
+
+static CORE_ADDR
+arc_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp, CORE_ADDR funaddr,
+ struct value **args, int nargs, struct type *value_type,
+ CORE_ADDR *real_pc, CORE_ADDR *bp_addr,
+ struct regcache *regcache)
+{
+ *real_pc = funaddr;
+ *bp_addr = entry_point_address ();
+ return sp;
+}
+
+/* Implement the "cannot_fetch_register" gdbarch method. */
+
+static int
+arc_cannot_fetch_register (struct gdbarch *gdbarch, int regnum)
+{
+ /* Assume that register is readable if it is unknown. LIMM and RESERVED are
+ not real registers, but specific register numbers. They are available as
+ regnums to align architectural register numbers with GDB internal regnums,
+ but they shouldn't appear in target descriptions generated by
+ GDB-servers. */
+ switch (regnum)
+ {
+ case ARC_RESERVED_REGNUM:
+ case ARC_LIMM_REGNUM:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Implement the "cannot_store_register" gdbarch method. */
+
+static int
+arc_cannot_store_register (struct gdbarch *gdbarch, int regnum)
+{
+ /* Assume that register is writable if it is unknown. See comment in
+ arc_cannot_fetch_register about LIMM and RESERVED. */
+ switch (regnum)
+ {
+ case ARC_RESERVED_REGNUM:
+ case ARC_LIMM_REGNUM:
+ case ARC_PCL_REGNUM:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Get the return value of a function from the registers/memory used to
+ return it, according to the convention used by the ABI - 4-bytes values are
+ in the R0, while 8-byte values are in the R0-R1.
+
+ TODO: This implementation ignores the case of "complex double", where
+ according to ABI, value is returned in the R0-R3 registers.
+
+ TYPE is a returned value's type. VALBUF is a buffer for the returned
+ value. */
+
+static void
+arc_extract_return_value (struct gdbarch *gdbarch, struct type *type,
+ struct regcache *regcache, gdb_byte *valbuf)
+{
+ unsigned int len = TYPE_LENGTH (type);
+
+ if (arc_debug)
+ debug_printf ("arc: extract_return_value\n");
+
+ if (len <= ARC_REGISTER_SIZE)
+ {
+ ULONGEST val;
+
+ /* Get the return value from one register. */
+ regcache_cooked_read_unsigned (regcache, ARC_R0_REGNUM, &val);
+ store_unsigned_integer (valbuf, (int) len,
+ gdbarch_byte_order (gdbarch), val);
+
+ if (arc_debug)
+ debug_printf ("arc: returning 0x%s\n", phex (val, ARC_REGISTER_SIZE));
+ }
+ else if (len <= ARC_REGISTER_SIZE * 2)
+ {
+ ULONGEST low, high;
+
+ /* Get the return value from two registers. */
+ regcache_cooked_read_unsigned (regcache, ARC_R0_REGNUM, &low);
+ regcache_cooked_read_unsigned (regcache, ARC_R1_REGNUM, &high);
+
+ store_unsigned_integer (valbuf, ARC_REGISTER_SIZE,
+ gdbarch_byte_order (gdbarch), low);
+ store_unsigned_integer (valbuf + ARC_REGISTER_SIZE,
+ (int) len - ARC_REGISTER_SIZE,
+ gdbarch_byte_order (gdbarch), high);
+
+ if (arc_debug)
+ debug_printf ("arc: returning 0x%s%s\n",
+ phex (high, ARC_REGISTER_SIZE),
+ phex (low, ARC_REGISTER_SIZE));
+ }
+ else
+ error (_("arc: extract_return_value: type length %u too large"), len);
+}
+
+
+/* Store the return value of a function into the registers/memory used to
+ return it, according to the convention used by the ABI.
+
+ TODO: This implementation ignores the case of "complex double", where
+ according to ABI, value is returned in the R0-R3 registers.
+
+ TYPE is a returned value's type. VALBUF is a buffer with the value to
+ return. */
+
+static void
+arc_store_return_value (struct gdbarch *gdbarch, struct type *type,
+ struct regcache *regcache, const gdb_byte *valbuf)
+{
+ unsigned int len = TYPE_LENGTH (type);
+
+ if (arc_debug)
+ debug_printf ("arc: store_return_value\n");
+
+ if (len <= ARC_REGISTER_SIZE)
+ {
+ ULONGEST val;
+
+ /* Put the return value into one register. */
+ val = extract_unsigned_integer (valbuf, (int) len,
+ gdbarch_byte_order (gdbarch));
+ regcache_cooked_write_unsigned (regcache, ARC_R0_REGNUM, val);
+
+ if (arc_debug)
+ debug_printf ("arc: storing 0x%s\n", phex (val, ARC_REGISTER_SIZE));
+ }
+ else if (len <= ARC_REGISTER_SIZE * 2)
+ {
+ ULONGEST low, high;
+
+ /* Put the return value into two registers. */
+ low = extract_unsigned_integer (valbuf, ARC_REGISTER_SIZE,
+ gdbarch_byte_order (gdbarch));
+ high = extract_unsigned_integer (valbuf + ARC_REGISTER_SIZE,
+ (int) len - ARC_REGISTER_SIZE,
+ gdbarch_byte_order (gdbarch));
+
+ regcache_cooked_write_unsigned (regcache, ARC_R0_REGNUM, low);
+ regcache_cooked_write_unsigned (regcache, ARC_R1_REGNUM, high);
+
+ if (arc_debug)
+ debug_printf ("arc: storing 0x%s%s\n",
+ phex (high, ARC_REGISTER_SIZE),
+ phex (low, ARC_REGISTER_SIZE));
+ }