diff options
Diffstat (limited to 'gas/config/tc-arm.c')
-rw-r--r-- | gas/config/tc-arm.c | 85 |
1 files changed, 60 insertions, 25 deletions
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index 12b4add..754bc4c 100644 --- a/gas/config/tc-arm.c +++ b/gas/config/tc-arm.c @@ -5748,6 +5748,7 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d) X(adcs, 4140, eb500000), \ X(add, 1c00, eb000000), \ X(adds, 1c00, eb100000), \ + X(adr, 000f, f20f0000), \ X(and, 4000, ea000000), \ X(ands, 4000, ea100000), \ X(asr, 1000, fa40f000), \ @@ -5781,8 +5782,8 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d) X(negs, 4240, f1d00000), /* rsbs #0 */ \ X(orr, 4300, ea400000), \ X(orrs, 4300, ea500000), \ - X(pop, bc00, e8ad0000), /* ldmia sp!,... */ \ - X(push, b400, e8bd0000), /* stmia sp!,... */ \ + X(pop, bc00, e8bd0000), /* ldmia sp!,... */ \ + X(push, b400, e92d0000), /* stmdb sp!,... */ \ X(rev, ba00, fa90f080), \ X(rev16, ba40, fa90f090), \ X(revsh, bac0, fa90f0b0), \ @@ -5963,11 +5964,24 @@ do_t_add_sub (void) static void do_t_adr (void) { - inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD; - inst.reloc.exp.X_add_number -= 4; /* PC relative adjust. */ - inst.reloc.pc_rel = 1; + if (unified_syntax && inst.size_req != 2) + { + /* Always generate a 32-bit opcode; + section relaxation will shrink it later if possible. */ + inst.instruction = THUMB_OP32 (inst.instruction); + inst.instruction |= inst.operands[0].reg << 8; + inst.reloc.type = BFD_RELOC_ARM_T32_ADD_PC12; + inst.reloc.pc_rel = 1; + } + else + { + inst.instruction = THUMB_OP16 (inst.instruction); + inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD; + inst.reloc.exp.X_add_number -= 4; /* PC relative adjust. */ + inst.reloc.pc_rel = 1; - inst.instruction |= inst.operands[0].reg << 4; + inst.instruction |= inst.operands[0].reg << 4; + } } /* Arithmetic instructions for which there is just one 16-bit @@ -6948,39 +6962,52 @@ do_t_pld (void) static void do_t_push_pop (void) { + unsigned mask; + constraint (inst.operands[0].writeback, _("push/pop do not support {reglist}^")); constraint (inst.reloc.type != BFD_RELOC_UNUSED, _("expression too complex")); - if ((inst.operands[0].imm & ~0xff) == 0) + mask = inst.operands[0].imm; + if ((mask & ~0xff) == 0) inst.instruction = THUMB_OP16 (inst.instruction); else if ((inst.instruction == T_MNEM_push - && (inst.operands[0].imm & ~0xff) == 1 << REG_LR) + && (mask & ~0xff) == 1 << REG_LR) || (inst.instruction == T_MNEM_pop - && (inst.operands[0].imm & ~0xff) == 1 << REG_PC)) + && (mask & ~0xff) == 1 << REG_PC)) { inst.instruction = THUMB_OP16 (inst.instruction); inst.instruction |= THUMB_PP_PC_LR; - inst.operands[0].imm &= 0xff; + mask &= 0xff; } else if (unified_syntax) { - if (inst.operands[1].imm & (1 << 13)) - as_warn (_("SP should not be in register list")); + if (mask & (1 << 13)) + inst.error = _("SP not allowed in register list"); if (inst.instruction == T_MNEM_push) { - if (inst.operands[1].imm & (1 << 15)) - as_warn (_("PC should not be in register list")); + if (mask & (1 << 15)) + inst.error = _("PC not allowed in register list"); } else { - if (inst.operands[1].imm & (1 << 14) - && inst.operands[1].imm & (1 << 15)) - as_warn (_("LR and PC should not both be in register list")); + if (mask & (1 << 14) + && mask & (1 << 15)) + inst.error = _("LR and PC should not both be in register list"); } - - inst.instruction = THUMB_OP32 (inst.instruction); + if ((mask & (mask - 1)) == 0) + { + /* Single register push/pop implemented as str/ldr. */ + if (inst.instruction == T_MNEM_push) + inst.instruction = 0xf84d0d04; /* str reg, [sp, #-4]! */ + else + inst.instruction = 0xf85d0b04; /* ldr reg, [sp], #4 */ + mask = ffs(mask) - 1; + mask <<= 12; + } + else + inst.instruction = THUMB_OP32 (inst.instruction); } else { @@ -6988,7 +7015,7 @@ do_t_push_pop (void) return; } - inst.instruction |= inst.operands[0].imm; + inst.instruction |= mask; } static void @@ -8205,7 +8232,7 @@ static const struct asm_opcode insns[] = TCE(bl, b000000, f000f800, 1, (EXPr), branch, t_branch23), /* Pseudo ops. */ - TCE(adr, 28f0000, 000f, 2, (RR, EXP), adr, t_adr), + tCE(adr, 28f0000, adr, 2, (RR, EXP), adr, t_adr), C3(adrl, 28f0000, 2, (RR, EXP), adrl), tCE(nop, 1a00000, nop, 1, (oI255c), nop, t_nop), @@ -10010,6 +10037,7 @@ md_pcrel_from_section (fixS * fixP, segT seg) case BFD_RELOC_ARM_THUMB_OFFSET: case BFD_RELOC_ARM_T32_OFFSET_IMM: + case BFD_RELOC_ARM_T32_ADD_PC12: return (base + 4) & ~3; /* Thumb branches are simply offset by +4. */ @@ -10532,6 +10560,7 @@ md_apply_fix (fixS * fixP, case BFD_RELOC_ARM_T32_IMMEDIATE: case BFD_RELOC_ARM_T32_IMM12: + case BFD_RELOC_ARM_T32_ADD_PC12: /* We claim that this fixup has been processed here, even if in fact we generate an error because we do not have a reloc for it, so tc_gen_reloc will reject it. */ @@ -10550,17 +10579,23 @@ md_apply_fix (fixS * fixP, newval <<= 16; newval |= md_chars_to_number (buf+2, THUMB_SIZE); - if (fixP->fx_r_type == BFD_RELOC_ARM_T32_IMM12) + /* FUTURE: Implement analogue of negate_data_op for T32. */ + if (fixP->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE) + newimm = encode_thumb32_immediate (value); + else { + /* 12 bit immediate for addw/subw. */ + if (value < 0) + { + value = -value; + newval ^= 0x00a00000; + } if (value > 0xfff) newimm = (unsigned int) FAIL; else newimm = value; } - else - newimm = encode_thumb32_immediate (value); - /* FUTURE: Implement analogue of negate_data_op for T32. */ if (newimm == (unsigned int)FAIL) { as_bad_where (fixP->fx_file, fixP->fx_line, |