+/* 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 definitely 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 (stack->store_would_trash (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;
+
+ stack->store (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];
+ stack->store (addr, ARC_REGISTER_SIZE, store_value1);
+ stack->store (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));
+ stack->store (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 (stack->store_would_trash (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);
+ stack->store (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);
+ stack->store (addr, ARC_REGISTER_SIZE, regs[i]);
+ }
+
+ if (is_blink_saved)
+ {
+ addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
+ stack->store (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;
+}
+