diff options
author | Nick Clifton <nickc@redhat.com> | 2012-04-12 07:46:54 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2012-04-12 07:46:54 +0000 |
commit | 6530b175a1051db81806d031b8ab2937744ff57b (patch) | |
tree | 3af9a7137c724b8db9731019fd811e2269559d2e /gas/config | |
parent | 202e23565d36f4696d5e836ec06d4c685c30fb16 (diff) | |
download | gdb-6530b175a1051db81806d031b8ab2937744ff57b.zip gdb-6530b175a1051db81806d031b8ab2937744ff57b.tar.gz gdb-6530b175a1051db81806d031b8ab2937744ff57b.tar.bz2 |
* config/tc-arm.c (only_one_reg_in_list): New function.
(encode_ldmstm): Ditto.
(do_ldmstm): Use a different encoding when pushing or poping
a single register.
(A_COND_MASK): New macro.
(A_PUSH_POP_OP_MASK): Ditto.
(A1_OPCODE_PUSH): Ditto.
(A2_OPCODE_PUSH): Ditto.
(A2_OPCODE_POP): Ditto.
* gas/arm/push-pop.d: New testcase.
* gas/arm/push-pop.s: Ditto.
* gas/arm/stm-ldm.d: Ditto.
* gas/arm/stm-ldm.s: Ditto.
Diffstat (limited to 'gas/config')
-rw-r--r-- | gas/config/tc-arm.c | 39 |
1 files changed, 37 insertions, 2 deletions
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index 585f78e..545b7ec 100644 --- a/gas/config/tc-arm.c +++ b/gas/config/tc-arm.c @@ -622,6 +622,14 @@ struct asm_opcode #define T2_OPCODE_MASK 0xfe1fffff #define T2_DATA_OP_SHIFT 21 +#define A_COND_MASK 0xf0000000 +#define A_PUSH_POP_OP_MASK 0x0fff0000 + +/* Opcodes for pushing/poping registers to/from the stack. */ +#define A1_OPCODE_PUSH 0x092d0000 +#define A2_OPCODE_PUSH 0x052d0004 +#define A2_OPCODE_POP 0x049d0004 + /* Codes to distinguish the arithmetic instructions. */ #define OPCODE_AND 0 #define OPCODE_EOR 1 @@ -7795,11 +7803,21 @@ do_it (void) } } +/* If there is only one register in the register list, + then return its register number. Otherwise return -1. */ +static int +only_one_reg_in_list (int range) +{ + int i = ffs (range) - 1; + return (i > 15 || range != (1 << i)) ? -1 : i; +} + static void -do_ldmstm (void) +encode_ldmstm(int from_push_pop_mnem) { int base_reg = inst.operands[0].reg; int range = inst.operands[1].imm; + int one_reg; inst.instruction |= base_reg << 16; inst.instruction |= range; @@ -7832,6 +7850,23 @@ do_ldmstm (void) as_warn (_("if writeback register is in list, it must be the lowest reg in the list")); } } + + /* If PUSH/POP has only one register, then use the A2 encoding. */ + one_reg = only_one_reg_in_list (range); + if (from_push_pop_mnem && one_reg >= 0) + { + int is_push = (inst.instruction & A_PUSH_POP_OP_MASK) == A1_OPCODE_PUSH; + + inst.instruction &= A_COND_MASK; + inst.instruction |= is_push ? A2_OPCODE_PUSH : A2_OPCODE_POP; + inst.instruction |= one_reg << 12; + } +} + +static void +do_ldmstm (void) +{ + encode_ldmstm (/*from_push_pop_mnem=*/FALSE); } /* ARMv5TE load-consecutive (argument parse) @@ -8333,7 +8368,7 @@ do_push_pop (void) inst.operands[0].isreg = 1; inst.operands[0].writeback = 1; inst.operands[0].reg = REG_SP; - do_ldmstm (); + encode_ldmstm (/*from_push_pop_mnem=*/TRUE); } /* ARM V6 RFE (Return from Exception) loads the PC and CPSR from the |