From patchwork Tue Jul 5 13:40:27 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antoine Tremblay X-Patchwork-Id: 13651 Received: (qmail 66671 invoked by alias); 5 Jul 2016 13:41:52 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 66582 invoked by uid 89); 5 Jul 2016 13:41:51 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.1 required=5.0 tests=BAYES_00, KAM_ASCII_DIVIDERS, SPF_PASS autolearn=no version=3.3.2 spammy=longest, patched, LONGEST, Low X-HELO: usplmg21.ericsson.net Received: from usplmg21.ericsson.net (HELO usplmg21.ericsson.net) (198.24.6.65) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-SHA encrypted) ESMTPS; Tue, 05 Jul 2016 13:41:41 +0000 Received: from EUSAAHC008.ericsson.se (Unknown_Domain [147.117.188.96]) by usplmg21.ericsson.net (Symantec Mail Security) with SMTP id FA.66.03614.2B8BB775; Tue, 5 Jul 2016 15:40:02 +0200 (CEST) Received: from elxa4wqvvz1.dyn.mo.ca.am.ericsson.se (147.117.188.8) by smtps-am.internal.ericsson.com (147.117.188.96) with Microsoft SMTP Server (TLS) id 14.3.294.0; Tue, 5 Jul 2016 09:40:55 -0400 From: Antoine Tremblay To: CC: Antoine Tremblay Subject: [PATCH v3 15/18] JIT conditions support for ARM tracepoints. Date: Tue, 5 Jul 2016 09:40:27 -0400 Message-ID: <1467726030-13020-16-git-send-email-antoine.tremblay@ericsson.com> In-Reply-To: <1467726030-13020-1-git-send-email-antoine.tremblay@ericsson.com> References: <1467726030-13020-1-git-send-email-antoine.tremblay@ericsson.com> MIME-Version: 1.0 X-IsSubscribed: yes This patch adds JIT conditions support for ARM tracepoints. gdb/ChangeLog: * arch/arm.h (submask_64): New macro. (bits_64): New macro. gdb/gdbserver/ChangeLog: * linux-arm-ipa.c (get_raw_reg): Fetch registers for JIT. * linux-arm-low.c (append_insns): New function. (add_insns_32): Likewise. (arm_ax_emit_arm_prologue): Likewise. (arm_ax_emit_arm_epilogue): Likewise. (arm_ax_emit_arm_add): Likewise. (arm_ax_emit_arm_sub): Likewise. (arm_ax_emit_arm_mul): Likewise. (arm_ax_emit_arm_lsh): Likewise. (arm_ax_emit_arm_rsh_signed): Likwise. (arm_ax_emit_arm_rsh_unsigned): Likewise. (arm_ax_emit_arm_ext): Likewise. (arm_ax_emit_arm_log_not): Likewise. (arm_ax_emit_arm_bit_and): Likewise. (arm_ax_emit_arm_bit_or): Likewise. (arm_ax_emit_arm_bit_xor): Likewise. (arm_ax_emit_arm_bit_not): Likewise. (arm_ax_emit_arm_equal): Likewise. (arm_ax_emit_arm_less_signed): Likewise. (arm_ax_emit_arm_less_unsigned): Likewise. (arm_ax_emit_arm_ref): Likewise. (arm_ax_emit_arm_if_goto): Likewise. (arm_ax_emit_arm_goto): Likewise. (arm_ax_arm_write_goto_address): Likewise. (arm_ax_emit_arm_const): Likewise. (arm_ax_emit_arm_call): Likewise. (arm_ax_emit_arm_reg): Likewise. (arm_ax_emit_arm_pop): Likewise. (arm_ax_emit_arm_stack_flush): Likewise. (arm_ax_emit_arm_zero_ext): Likewise. (arm_ax_emit_arm_swap): Likewise. (arm_ax_emit_arm_stack_adjust): Likewise. (arm_ax_emit_arm_int_call_1): Likewise. (arm_ax_emit_arm_void_call_2): Likewise. (arm_ax_emit_arm_cmp_eq_goto): Likewise. (arm_ax_emit_arm_cmp_lgte_goto): Likewise. (arm_ax_emit_arm_eq_goto): Likewise. (arm_ax_emit_arm_ne_goto): Likewise. (arm_ax_emit_arm_lt_goto): Likewise. (arm_ax_emit_arm_le_goto): Likewise. (arm_ax_emit_arm_gt_goto): Likewise. (arm_ax_emit_arm_ge_goto): Likewise. (struct emit_ops arm_ax_emit_arm_ops_impl): New struct. (arm_ax_emit_arm_ops): New function. (struct linux_target_ops) : Initialize. --- gdb/arch/arm.h | 2 + gdb/gdbserver/linux-arm-ipa.c | 13 +- gdb/gdbserver/linux-arm-low.c | 788 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 801 insertions(+), 2 deletions(-) diff --git a/gdb/arch/arm.h b/gdb/arch/arm.h index fcde3d0..e7bba24 100644 --- a/gdb/arch/arm.h +++ b/gdb/arch/arm.h @@ -96,7 +96,9 @@ enum gdb_regnum { /* Support routines for instruction parsing. */ #define submask(x) ((1L << ((x) + 1)) - 1) +#define submask_64(x) ((1LL << ((x) + 1)) - 1) #define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st))) +#define bits_64(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st))) #define bit(obj,st) (((obj) >> (st)) & 1) #define sbits(obj,st,fn) \ ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st)))) diff --git a/gdb/gdbserver/linux-arm-ipa.c b/gdb/gdbserver/linux-arm-ipa.c index d684b78..ba2488e 100644 --- a/gdb/gdbserver/linux-arm-ipa.c +++ b/gdb/gdbserver/linux-arm-ipa.c @@ -159,7 +159,18 @@ supply_fast_tracepoint_registers (struct regcache *regcache, ULONGEST get_raw_reg (const unsigned char *raw_regs, int regnum) { - /* Used for JIT conditions. */ + if (regnum < ARM_CORE_NUM_FT_COLLECT_REGS) + return *(int *) (raw_regs + arm_core_ft_collect_regmap[regnum]); + + if ((get_ipa_tdesc (get_ipa_tdesc_idx ()) == tdesc_arm_with_neon + || get_ipa_tdesc (get_ipa_tdesc_idx ()) == tdesc_arm_with_vfpv3) + && regnum < ARM_VFPV3_NUM_FT_COLLECT_REGS) + return *(ULONGEST *) (raw_regs + arm_vfpv3_ft_collect_regmap[regnum]); + + if (get_ipa_tdesc (get_ipa_tdesc_idx ()) == tdesc_arm_with_vfpv2 + && regnum < ARM_VFPV2_NUM_FT_COLLECT_REGS) + return *(ULONGEST *) (raw_regs + arm_vfpv2_ft_collect_regmap[regnum]); + return 0; } diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c index 5294f00..53c4151 100644 --- a/gdb/gdbserver/linux-arm-low.c +++ b/gdb/gdbserver/linux-arm-low.c @@ -1393,8 +1393,10 @@ static const int r2 = 2; static const int r3 = 3; static const int r4 = 4; static const int r5 = 5; +static const int r12 = 12; static const int sp = 13; static const int lr = 14; +static const int pc = 15; static int arm_install_fast_tracepoint_jump_pad_arm (struct tracepoint *tp, @@ -1950,6 +1952,790 @@ arm_get_ipa_tdesc_idx (void) return 0; } +/* Append variable length instructions to the inferior memory. */ + +static int +append_insns (CORE_ADDR *to, size_t len, const unsigned char *buf) +{ + if (write_inferior_memory (*to, buf, len) != 0) + return 1; + + *to += len; + + return 0; +} + +/* Append 32 bit instructions to the inferior memory. */ + +static void +add_insns_32 (uint32_t *start, int len) +{ + CORE_ADDR buildaddr = current_insn_ptr; + + if (debug_threads) + debug_printf ("Adding %d bytes of insn at %s\n", + len * 4, paddress (buildaddr)); + + append_insns (&buildaddr, len * 4, (gdb_byte *) start); + current_insn_ptr = buildaddr; +} + +/* The "emit_prologue" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_prologue (void) +{ + /* This function emit a prologue for the following function prototype: + + enum eval_result_type f (unsigned char *regs, + ULONGEST *value); + + The first argument is a buffer of raw registers. The second argument + is the result of evaluating the expression, which will be set to + whatever is on top of the stack at the end. + + The stack set up by the prologue is as such: + + High *------------------------------------------------------* + | lr | + | r4 | + | r0 (ULONGEST *value) | + | r1 (unsigned char *regs) | <-r4 + Low *------------------------------------------------------* + + As we are implementing a stack machine, each opcode can expand the + stack so we never know how far we are from the data saved by this + prologue. In order to be able refer to value and regs later, we save + the current base of the stack in the r4 register. This way, it is not + clobbered when calling C functions. + + Finally, throughtout every operation, we are using register r0 and r1 + as the top of the stack, and r2, r3, r12 as a scratch register. */ + + uint32_t buf[16]; + uint32_t *p = buf; + + /* push {r0, r1, r4, lr} */ + p += arm_emit_arm_push_list (p, INST_AL, + encode_register_list (0, 2, ENCODE (1, 1, 4) + | ENCODE (1, 1, 14))); + p += arm_emit_arm_mov (p, INST_AL, r4, register_operand (sp)); + add_insns_32 (buf, p - buf); +} + +/* The "emit_epilogue" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_epilogue (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_add (p, INST_AL, 0, sp, r4, immediate_operand (4)); + p += arm_emit_arm_pop_list (p, INST_AL, ENCODE (1, 1, 1) + | ENCODE (1, 1, 4) + | ENCODE (1, 1, 14)); + p += arm_emit_arm_str (p, INST_AL, r0, r1, + memory_operand (offset_memory_operand (0))); + p += arm_emit_arm_mov (p, INST_AL, r0, immediate_operand (0)); + p += arm_emit_arm_mov (p, INST_AL, pc, register_operand (lr)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_add" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_add (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + /* pop {r2,r3} */ + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_add (p, INST_AL, 1, r0, r2, register_operand (r0)); + p += arm_emit_arm_adc (p, INST_AL, r1, r3, register_operand (r1)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_sub" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_sub (void) +{ + const struct target_desc *tdesc = current_process ()->tdesc; + const int r0 = find_regno (tdesc, "r0"); + const int r1 = find_regno (tdesc, "r1"); + uint32_t buf[16]; + uint32_t *p = buf; + + /* pop {r2,r3} */ + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_sub (p, INST_AL, 1, r0, r2, register_operand (r0)); + p += arm_emit_arm_sbc (p, INST_AL, 0, r1, r3, register_operand (r1)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_mul" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_mul (void) +{ + const struct target_desc *tdesc = current_process ()->tdesc; + const int r0 = find_regno (tdesc, "r0"); + const int r1 = find_regno (tdesc, "r1"); + uint32_t buf[16]; + uint32_t *p = buf; + + /* pop {r2,r3} */ + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + + /* Multiply 64-64 int as : ((ah * bl) + (bh * al)) + (al * bl). + ah = operand a, high bits. + al = operand a, low bits. + same for operand b. */ + + p += arm_emit_arm_mul (p, INST_AL, r1, r1, register_operand (r2)); + p += arm_emit_arm_mul (p, INST_AL, r3, r0, register_operand (r3)); + p += arm_emit_arm_add (p, INST_AL, 0, r1, r1, register_operand (r3)); + p += arm_emit_arm_umull (p, INST_AL, r2, r3, r0, r2); + p += arm_emit_arm_add (p, INST_AL, 0, r1, r1, register_operand (r3)); + p += arm_emit_arm_mov (p, INST_AL, r0, register_operand (r2)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_lsh" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_lsh (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_sub (p, INST_AL, 0, r12, r0, immediate_operand (32)); + p += arm_emit_arm_rsb (p, INST_AL, r1, r0, immediate_operand (32)); + p += arm_emit_arm_lsl (p, INST_AL, r3, r3, register_operand (r0)); + p += arm_emit_arm_orr_reg_shifted (p, INST_AL, r3, r3, r2, LSL, r12); + p += arm_emit_arm_orr_reg_shifted (p, INST_AL, r3, r3, r2, LSR, r1); + p += arm_emit_arm_lsl (p, INST_AL, r0, r2, register_operand (r0)); + p += arm_emit_arm_mov (p, INST_AL, r1, register_operand (r3)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_rsh_signed" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_rsh_signed (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_rsb (p, INST_AL, r1, r0, immediate_operand (32)); + p += arm_emit_arm_sub (p, INST_AL, 1, r12, r0, immediate_operand (32)); + p += arm_emit_arm_lsr (p, INST_AL, r2, r2, register_operand (r0)); + p += arm_emit_arm_orr_reg_shifted (p, INST_AL, r2, r2, r3, LSL, r1); + p += arm_emit_arm_orr_reg_shifted (p, INST_PL, r2, r2, r3, ASR, r12); + p += arm_emit_arm_asr (p, INST_AL, r1, r3, register_operand (r0)); + p += arm_emit_arm_mov (p, INST_AL, r0, register_operand (r2)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_rsh_unsigned" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_rsh_unsigned (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_rsb (p, INST_AL, r1, r0, immediate_operand (32)); + p += arm_emit_arm_sub (p, INST_AL, 1, r12, r0, immediate_operand (32)); + p += arm_emit_arm_lsr (p, INST_AL, r2, r2, register_operand (r0)); + p += arm_emit_arm_orr_reg_shifted (p, INST_AL, r2, r2, r3, LSL, r1); + p += arm_emit_arm_orr_reg_shifted (p, INST_PL, r2, r2, r3, LSR, r12); + p += arm_emit_arm_lsr (p, INST_AL, r1, r3, register_operand (r0)); + p += arm_emit_arm_mov (p, INST_AL, r0, register_operand (r2)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_ext" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_ext (int arg) +{ + const struct target_desc *tdesc = current_process ()->tdesc; + const int r0 = find_regno (tdesc, "r0"); + uint32_t buf[16]; + uint32_t *p = buf; + + if (arg <= 32) + { + p += arm_emit_arm_sbfx (p, INST_AL, r0, r0, 0, arg); + p += arm_emit_arm_mov (p, INST_AL, r1, register_operand (r0)); + p += arm_emit_arm_asr (p, INST_AL, r1, r1, immediate_operand (31)); + } + else + p += arm_emit_arm_sbfx (p, INST_AL, r1, r1, 0, 32 - arg); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_log_not" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_log_not (void) +{ + const struct target_desc *tdesc = current_process ()->tdesc; + const int r0 = find_regno (tdesc, "r0"); + uint32_t buf[16]; + uint32_t *p = buf; + + /* Check if the top of the stack is 0 */ + p += arm_emit_arm_cmp (p, INST_AL, r1, immediate_operand (0)); + p += arm_emit_arm_cmp (p, INST_EQ, r0, immediate_operand (0)); + + /* Move 1 to the top of stack if it's 0 */ + p += arm_emit_arm_mov (p, INST_EQ, r0, immediate_operand (1)); + p += arm_emit_arm_mov (p, INST_EQ, r1, immediate_operand (0)); + + /* Move 0 to the top of stack if it's not 0 */ + p += arm_emit_arm_mov (p, INST_NE, r0, immediate_operand (0)); + p += arm_emit_arm_mov (p, INST_NE, r1, immediate_operand (0)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_bit_and" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_bit_and (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_and (p, INST_AL, r0, r0, register_operand (r2)); + p += arm_emit_arm_and (p, INST_AL, r1, r1, register_operand (r3)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_bit_or" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_bit_or (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_orr (p, INST_AL, r0, r0, register_operand (r2)); + p += arm_emit_arm_orr (p, INST_AL, r1, r1, register_operand (r3)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_bit_xor" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_bit_xor (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_eor (p, INST_AL, r0, r0, register_operand (r2)); + p += arm_emit_arm_eor (p, INST_AL, r1, r1, register_operand (r3)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_bit_not" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_bit_not (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_mvn (p, INST_AL, r0, register_operand (r0)); + p += arm_emit_arm_mvn (p, INST_AL, r1, register_operand (r1)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_equal" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_equal (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + /* pop {r2,r3} */ + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_cmp (p, INST_AL, r0, register_operand (r2)); + p += arm_emit_arm_cmp (p, INST_EQ, r1, register_operand (r3)); + p += arm_emit_arm_mov (p, INST_EQ, r0, immediate_operand (1)); + p += arm_emit_arm_mov (p, INST_NE, r0, immediate_operand (0)); + p += arm_emit_arm_mov (p, INST_AL, r1, immediate_operand (0)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_less_signed" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_less_signed (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_cmp (p, INST_AL, r2, register_operand (r0)); + p += arm_emit_arm_sbc (p, INST_AL, 1, r1, r3, register_operand (r1)); + p += arm_emit_arm_mov (p, INST_LT, r0, immediate_operand (1)); + p += arm_emit_arm_mov (p, INST_GE, r0, immediate_operand (0)); + p += arm_emit_arm_mov (p, INST_AL, r1, immediate_operand (0)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_less_unsigned" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_less_unsigned (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_cmp (p, INST_AL, r3, register_operand (r1)); + p += arm_emit_arm_cmp (p, INST_EQ, r2, register_operand (r0)); + p += arm_emit_arm_mov (p, INST_CC, r0, immediate_operand (1)); + p += arm_emit_arm_mov (p, INST_CS, r0, immediate_operand (0)); + p += arm_emit_arm_mov (p, INST_AL, r1, immediate_operand (0)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_ref" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_ref (int size) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + switch (size) + { + case 1: + p += arm_emit_arm_ldrb (p, INST_AL, r0, r0, offset_memory_operand (0)); + p += arm_emit_arm_mov (p, INST_AL, r1, immediate_operand (0)); + break; + case 2: + p += arm_emit_arm_ldrh (p, INST_AL, r0, r0, offset_memory_operand (0)); + p += arm_emit_arm_mov (p, INST_AL, r1, immediate_operand (0)); + break; + case 4: + p += arm_emit_arm_ldr (p, INST_AL, r0, r0, offset_memory_operand (0)); + p += arm_emit_arm_mov (p, INST_AL, r1, immediate_operand (0)); + break; + case 8: + p += arm_emit_arm_ldrd (p, INST_AL, r0, r0, offset_memory_operand (0)); + break; + default: + emit_error = 1; + } + + add_insns_32 (buf, p - buf); +} + +/* The "emit_if_goto" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_if_goto (int *offset_p, int *size_p) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_cmp (p, INST_AL, r1, immediate_operand (0)); + p += arm_emit_arm_cmp (p, INST_EQ, r0, immediate_operand (0)); + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_bl (p, INST_EQ, arm_arm_branch_adjusted_offset (8)); + + /* The NOP instruction will be patched with an unconditional branch. */ + if (offset_p) + *offset_p = (p - buf) * 4; + if (size_p) + *size_p = 4; + + p += arm_emit_arm_nop (p, INST_AL); + add_insns_32 (buf, p - buf); +} + +/* The "emit_goto" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_goto (int *offset_p, int *size_p) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + /* The NOP instruction will be patched with an unconditional branch. */ + if (offset_p) + *offset_p = 0; + if (size_p) + *size_p = 4; + + p += arm_emit_arm_nop (p, INST_AL); + add_insns_32 (buf, p - buf); +} + +/* The "emit_write_goto_address" emit_ops method for ARM. */ + +static void +arm_ax_arm_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size) +{ + uint32_t insn; + arm_emit_arm_b (&insn, INST_AL, arm_arm_branch_relative_distance (from, to)); + append_insns (&from, 4, (const unsigned char *)&insn); +} + +/* The "emit_const" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_const (LONGEST num) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_movw (p, INST_AL, r0, + immediate_operand (bits_64 (num, 0, 15))); + p += arm_emit_arm_movt (p, INST_AL, r0, + immediate_operand (bits_64 (num, 16, 31))); + p += arm_emit_arm_movw (p, INST_AL, r1, + immediate_operand (bits_64 (num, 32, 47))); + p += arm_emit_arm_movt (p, INST_AL, r1, + immediate_operand (bits_64 (num, 48, 63))); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_call" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_call (CORE_ADDR fn) +{ + const struct target_desc *tdesc = current_process ()->tdesc; + const int r12 = find_regno (tdesc, "r12"); + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_mov_32 (p, r12, fn); + p += arm_emit_arm_blx (p, INST_AL, register_operand (r12)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_reg" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_reg (int reg) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + /* Set r0 to unsigned char *regs. */ + p += arm_emit_arm_mov (p, INST_AL, r0, register_operand (r4)); + p += arm_emit_arm_ldr (p, INST_AL, r0, r0, offset_memory_operand (0)); + p += arm_emit_arm_mov (p, INST_AL, r1, immediate_operand (reg)); + + add_insns_32 (buf, p - buf); + + arm_ax_emit_arm_call (get_raw_reg_func_addr ()); +} + +/* The "emit_pop" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_pop (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (0, 2, 0)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_stack_flush" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_stack_flush (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + /* push {r0,r1} */ + p += arm_emit_arm_push_list (p, INST_AL, encode_register_list (0, 2, 0)); + add_insns_32 (buf, p - buf); +} + +/* The "emit_zero_ext" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_zero_ext (int arg) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + if (arg > 32) + { + p += arm_emit_arm_ubfx (p, INST_AL, r1, r1, 0, arg - 32); + } + else + { + p += arm_emit_arm_ubfx (p, INST_AL, r0, r0, 0, arg); + p += arm_emit_arm_mov (p, INST_AL, r1, immediate_operand (0)); + } + add_insns_32 (buf, p - buf); +} + +/* The "emit_swap" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_swap (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_push_list (p, INST_AL, encode_register_list (0, 2, 0)); + p += arm_emit_arm_mov (p, INST_AL, r0, register_operand (r2)); + p += arm_emit_arm_mov (p, INST_AL, r1, register_operand (r3)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_stack_adjust" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_stack_adjust (int n) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_add (p, INST_AL, 0, sp, sp, immediate_operand (n * 8)); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_int_call_1" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_int_call_1 (CORE_ADDR fn, int arg1) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_mov_32 (p, r0, arg1); + + add_insns_32 (buf, p - buf); + arm_ax_emit_arm_call (fn); +} + +/* The "emit_void_call_2" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_void_call_2 (CORE_ADDR fn, int arg1) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + arm_ax_emit_arm_stack_flush (); + + /* Setup arguments for the function call: + r0: arg1 + r2-r3 : arg2 + */ + + p += arm_emit_arm_mov (p, INST_AL, r2, register_operand (r0)); + p += arm_emit_arm_mov (p, INST_AL, r3, register_operand (r1)); + p += arm_emit_arm_mov_32 (p, r0, arg1); + + add_insns_32 (buf, p - buf); + arm_ax_emit_arm_call (fn); + + /* Restore r0,r1. */ + arm_ax_emit_arm_pop (); +} + +/* Helper function for arm_{EQ|NE}_goto. */ + +static void +arm_ax_emit_arm_cmp_eq_goto (uint8_t cond, int *offset_p, int *size_p) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_cmp (p, INST_AL, r0, register_operand (r2)); + p += arm_emit_arm_cmp (p, INST_EQ, r1, register_operand (r3)); + + /* Branch over the next instruction. */ + p += arm_emit_arm_bl (p, cond, arm_arm_branch_adjusted_offset (8)); + /* The NOP instruction will be patched with an unconditional branch. */ + if (offset_p) + *offset_p = (p - buf) * 4; + if (size_p) + *size_p = 4; + p += arm_emit_arm_nop (p, INST_AL); + + add_insns_32 (buf, p - buf); +} + +/* Helper function for arm_{LE|LT|GE|GT}_goto. */ + +static void +arm_ax_emit_arm_cmp_lgte_goto (uint8_t cond, int *offset_p, int *size_p, + int swap) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + if (swap) + { + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_push_list (p, INST_AL,encode_register_list (0, 2, 0)); + p += arm_emit_arm_mov (p, INST_AL, r0, register_operand (r2)); + p += arm_emit_arm_mov (p, INST_AL, r1, register_operand (r3)); + } + + p += arm_emit_arm_pop_list (p, INST_AL, encode_register_list (2, 2, 0)); + p += arm_emit_arm_cmp (p, INST_AL, r2, register_operand (r0)); + p += arm_emit_arm_sbc (p, INST_AL, 1, r2, r3, register_operand (r1)); + + /* Branch over the next instruction. */ + p += arm_emit_arm_bl (p, cond, arm_arm_branch_adjusted_offset (8)); + /* The NOP instruction will be patched with an unconditional branch. */ + if (offset_p) + *offset_p = (p - buf) * 4; + if (size_p) + *size_p = 4; + p += arm_emit_arm_nop (p, INST_AL); + + add_insns_32 (buf, p - buf); +} + +/* The "emit_eq_goto" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_eq_goto (int *offset_p, int *size_p) +{ + arm_ax_emit_arm_cmp_eq_goto (INST_NE, offset_p, size_p); +} + +/* The "emit_ne_goto" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_ne_goto (int *offset_p, int *size_p) +{ + arm_ax_emit_arm_cmp_eq_goto (INST_EQ, offset_p, size_p); +} + +/* The "emit_lt_goto" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_lt_goto (int *offset_p, int *size_p) +{ + arm_ax_emit_arm_cmp_lgte_goto (INST_GE, offset_p, size_p, 0); +} + +/* The "emit_le_goto" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_le_goto (int *offset_p, int *size_p) +{ + arm_ax_emit_arm_cmp_lgte_goto (INST_LT, offset_p, size_p, 1); +} + +/* The "emit_gt_goto" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_gt_goto (int *offset_p, int *size_p) +{ + arm_ax_emit_arm_cmp_lgte_goto (INST_LE, offset_p, size_p, 1); +} + +/* The "emit_ge_goto" emit_ops method for ARM. */ + +static void +arm_ax_emit_arm_ge_goto (int *offset_p, int *size_p) +{ + arm_ax_emit_arm_cmp_lgte_goto (INST_LT, offset_p, size_p, 0); +} + +/* The "emit_ops" structure for ARM. */ + +static struct emit_ops arm_ax_emit_arm_ops_impl = +{ + arm_ax_emit_arm_prologue, + arm_ax_emit_arm_epilogue, + arm_ax_emit_arm_add, + arm_ax_emit_arm_sub, + arm_ax_emit_arm_mul, + arm_ax_emit_arm_lsh, + arm_ax_emit_arm_rsh_signed, + arm_ax_emit_arm_rsh_unsigned, + arm_ax_emit_arm_ext, + arm_ax_emit_arm_log_not, + arm_ax_emit_arm_bit_and, + arm_ax_emit_arm_bit_or, + arm_ax_emit_arm_bit_xor, + arm_ax_emit_arm_bit_not, + arm_ax_emit_arm_equal, + arm_ax_emit_arm_less_signed, + arm_ax_emit_arm_less_unsigned, + arm_ax_emit_arm_ref, + arm_ax_emit_arm_if_goto, + arm_ax_emit_arm_goto, + arm_ax_arm_write_goto_address, + arm_ax_emit_arm_const, + arm_ax_emit_arm_call, + arm_ax_emit_arm_reg, + arm_ax_emit_arm_pop, + arm_ax_emit_arm_stack_flush, + arm_ax_emit_arm_zero_ext, + arm_ax_emit_arm_swap, + arm_ax_emit_arm_stack_adjust, + arm_ax_emit_arm_int_call_1, + arm_ax_emit_arm_void_call_2, + arm_ax_emit_arm_eq_goto, + arm_ax_emit_arm_ne_goto, + arm_ax_emit_arm_lt_goto, + arm_ax_emit_arm_le_goto, + arm_ax_emit_arm_gt_goto, + arm_ax_emit_arm_ge_goto, +}; + +/* Implementation of linux_target_ops method "emit_ops". */ + +static struct emit_ops * +arm_ax_emit_arm_ops (void) +{ + return &arm_ax_emit_arm_ops_impl; +} + struct linux_target_ops the_low_target = { arm_arch_setup, arm_regs_info, @@ -1979,7 +2765,7 @@ struct linux_target_ops the_low_target = { arm_supports_tracepoints, arm_get_thread_area, /* get_thread_area */ arm_install_fast_tracepoint_jump_pad, /* install_fast_tracepoint_jump_pad */ - NULL, /*emit_ops */ + arm_ax_emit_arm_ops, arm_get_min_fast_tracepoint_insn_len, /* get_min_fast_tracepoint_insn_len */ NULL, /* supports_range_stepping */ arm_breakpoint_kind_from_current_state,