aboutsummaryrefslogtreecommitdiff
path: root/gas/config
diff options
context:
space:
mode:
authorPaul Brook <paul@codesourcery.com>2005-09-06 16:59:24 +0000
committerPaul Brook <paul@codesourcery.com>2005-09-06 16:59:24 +0000
commit0110f2b896d40a2ab1d19b85f7990d1c1a45792a (patch)
tree0b9fef94c8cfa8d6ff26146fd8578e43db60f1b6 /gas/config
parent9a64e435419869030e27d54f0433810b8b56d1ac (diff)
downloadgdb-0110f2b896d40a2ab1d19b85f7990d1c1a45792a.zip
gdb-0110f2b896d40a2ab1d19b85f7990d1c1a45792a.tar.gz
gdb-0110f2b896d40a2ab1d19b85f7990d1c1a45792a.tar.bz2
2005-09-06 Paul Brook <paul@codesourcery.com>
gas/ * config/tc-arm.c (arm_it): Add relax field. (T16_32_TAB): Add addi, addis, add_pc, add_sp, dec_sp, inc_sp, b, bcond, ldr_pc, ldr_pc2, ldr_sp, str_sp, subi, subis. (do_t_add_sub, do_t_addr, do_t_branch, do_t_ldst, do_t_mov_cmp): Allow relaxation. (output_relax_insn): New function. (put_thumb32_insn): New function. (output_inst): Use new functions. (md_assemble): Don't throw error on relaxable instructions. (insns): Change "b" entry from TCE(...) to tCE(...). (md_estimate_size_before_relax): Return 2. (md_convert_frag, relax_immediate, relax_adr, relax_addsub, relax_branch, arm_relax_frag): New functions. (arm_force_relocation): Return 0 for Thumb-2 immediate operand relocations. * config/tc-arm.h (md_convert_frag): Remove definition. (md_relax_frag): Define. (arm_relax_frag): Add prototype. gas/testsuite/ * gas/arm/thumb2_relax.d: New test. * gas/arm/thumb2_relax.s: New test. * gas/arm/thumb32.d: Adjust expected results to include relaxation. * gas/arm/thumb32.s: Tweak for better coverage of relaxable instructions. Remove load/store tests.
Diffstat (limited to 'gas/config')
-rw-r--r--gas/config/tc-arm.c613
-rw-r--r--gas/config/tc-arm.h4
2 files changed, 564 insertions, 53 deletions
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c
index 23d5bc7..30fd223 100644
--- a/gas/config/tc-arm.c
+++ b/gas/config/tc-arm.c
@@ -213,6 +213,9 @@ struct arm_it
int size;
int size_req;
int cond;
+ /* Set to the opcode if the instruction needs relaxation.
+ Zero if the instruction is not relaxed. */
+ unsigned long relax;
struct
{
bfd_reloc_code_real_type type;
@@ -5759,17 +5762,24 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
encodings (the latter only in post-V6T2 cores). The index is the
value used in the insns table below. When there is more than one
possible 16-bit encoding for the instruction, this table always
- holds variant (1). */
+ holds variant (1).
+ Also contains several pseudo-instructions used during relaxation. */
#define T16_32_TAB \
X(adc, 4140, eb400000), \
X(adcs, 4140, eb500000), \
X(add, 1c00, eb000000), \
X(adds, 1c00, eb100000), \
+ X(addi, 0000, f1000000), \
+ X(addis, 0000, f1100000), \
+ X(add_pc,000f, f20f0000), \
+ X(add_sp,000d, f10d0000), \
X(adr, 000f, f20f0000), \
X(and, 4000, ea000000), \
X(ands, 4000, ea100000), \
X(asr, 1000, fa40f000), \
X(asrs, 1000, fa50f000), \
+ X(b, e000, f000b000), \
+ X(bcond, d000, f0008000), \
X(bic, 4380, ea200000), \
X(bics, 4380, ea300000), \
X(cmn, 42c0, eb100f00), \
@@ -5777,14 +5787,19 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
X(cpsie, b660, f3af8400), \
X(cpsid, b670, f3af8600), \
X(cpy, 4600, ea4f0000), \
+ X(dec_sp,80dd, f1bd0d00), \
X(eor, 4040, ea800000), \
X(eors, 4040, ea900000), \
+ X(inc_sp,00dd, f10d0d00), \
X(ldmia, c800, e8900000), \
X(ldr, 6800, f8500000), \
X(ldrb, 7800, f8100000), \
X(ldrh, 8800, f8300000), \
X(ldrsb, 5600, f9100000), \
X(ldrsh, 5e00, f9300000), \
+ X(ldr_pc,4800, f85f0000), \
+ X(ldr_pc2,4800, f85f0000), \
+ X(ldr_sp,9800, f85d0000), \
X(lsl, 0000, fa00f000), \
X(lsls, 0000, fa10f000), \
X(lsr, 0800, fa20f000), \
@@ -5812,8 +5827,11 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
X(str, 6000, f8400000), \
X(strb, 7000, f8000000), \
X(strh, 8000, f8200000), \
+ X(str_sp,9000, f84d0000), \
X(sub, 1e00, eba00000), \
X(subs, 1e00, ebb00000), \
+ X(subi, 8000, f1a00000), \
+ X(subis, 8000, f1b00000), \
X(sxtb, b240, fa4ff080), \
X(sxth, b200, fa0ff080), \
X(tst, 4200, ea100f00), \
@@ -5875,17 +5893,61 @@ do_t_add_sub (void)
if (unified_syntax)
{
+ bfd_boolean flags;
+ bfd_boolean narrow;
+ int opcode;
+
+ flags = (inst.instruction == T_MNEM_adds
+ || inst.instruction == T_MNEM_subs);
+ if (flags)
+ narrow = (current_it_mask == 0);
+ else
+ narrow = (current_it_mask != 0);
if (!inst.operands[2].isreg)
{
- /* ??? Convert large immediates to addw/subw. */
- /* ??? 16-bit adds with small immediates. */
- /* For an immediate, we always generate a 32-bit opcode;
- section relaxation will shrink it later if possible. */
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ opcode = 0;
+ if (inst.size_req != 4)
+ {
+ int add;
+
+ add = (inst.instruction == T_MNEM_add
+ || inst.instruction == T_MNEM_adds);
+ /* Attempt to use a narrow opcode, with relaxation if
+ appropriate. */
+ if (Rd == REG_SP && Rs == REG_SP && !flags)
+ opcode = add ? T_MNEM_inc_sp : T_MNEM_dec_sp;
+ else if (Rd <= 7 && Rs == REG_SP && add && !flags)
+ opcode = T_MNEM_add_sp;
+ else if (Rd <= 7 && Rs == REG_PC && add && !flags)
+ opcode = T_MNEM_add_pc;
+ else if (Rd <= 7 && Rs <= 7 && narrow)
+ {
+ if (flags)
+ opcode = add ? T_MNEM_addis : T_MNEM_subis;
+ else
+ opcode = add ? T_MNEM_addi : T_MNEM_subi;
+ }
+ if (opcode)
+ {
+ inst.instruction = THUMB_OP16(opcode);
+ inst.instruction |= (Rd << 4) | Rs;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ if (inst.size_req != 2)
+ inst.relax = opcode;
+ }
+ else
+ constraint (inst.size_req == 2, BAD_HIREG);
+ }
+ if (inst.size_req == 4
+ || (inst.size_req != 2 && !opcode))
+ {
+ /* ??? Convert large immediates to addw/subw. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
}
else
{
@@ -5893,13 +5955,6 @@ do_t_add_sub (void)
/* See if we can do this with a 16-bit instruction. */
if (!inst.operands[2].shifted && inst.size_req != 4)
{
- bfd_boolean narrow;
-
- if (inst.instruction == T_MNEM_adds
- || inst.instruction == T_MNEM_subs)
- narrow = (current_it_mask == 0);
- else
- narrow = (current_it_mask != 0);
if (Rd > 7 || Rs > 7 || Rn > 7)
narrow = FALSE;
@@ -5992,10 +6047,16 @@ do_t_add_sub (void)
static void
do_t_adr (void)
{
- if (unified_syntax && inst.size_req != 2)
+ if (unified_syntax && inst.size_req == 0 && inst.operands[0].reg <= 7)
+ {
+ /* Defer to section relaxation. */
+ inst.relax = inst.instruction;
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 4;
+ }
+ else if (unified_syntax && inst.size_req != 2)
{
- /* Always generate a 32-bit opcode;
- section relaxation will shrink it later if possible. */
+ /* Generate a 32-bit opcode. */
inst.instruction = THUMB_OP32 (inst.instruction);
inst.instruction |= inst.operands[0].reg << 8;
inst.reloc.type = BFD_RELOC_ARM_T32_ADD_PC12;
@@ -6003,6 +6064,7 @@ do_t_adr (void)
}
else
{
+ /* Generate a 16-bit opcode. */
inst.instruction = THUMB_OP16 (inst.instruction);
inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
inst.reloc.exp.X_add_number -= 4; /* PC relative adjust. */
@@ -6266,29 +6328,37 @@ do_t_blx (void)
static void
do_t_branch (void)
{
- if (unified_syntax && inst.size_req != 2)
+ int opcode;
+ if (inst.cond != COND_ALWAYS)
+ opcode = T_MNEM_bcond;
+ else
+ opcode = inst.instruction;
+
+ if (unified_syntax && inst.size_req == 4)
{
+ inst.instruction = THUMB_OP32(opcode);
if (inst.cond == COND_ALWAYS)
- {
- inst.instruction = 0xf000b000;
- inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH25;
- }
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH25;
else
{
assert (inst.cond != 0xF);
- inst.instruction = (inst.cond << 22) | 0xf0008000;
+ inst.instruction |= inst.cond << 22;
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH20;
}
}
else
{
+ inst.instruction = THUMB_OP16(opcode);
if (inst.cond == COND_ALWAYS)
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12;
else
{
- inst.instruction = 0xd000 | (inst.cond << 8);
+ inst.instruction |= inst.cond << 8;
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9;
}
+ /* Allow section relaxation. */
+ if (unified_syntax && inst.size_req != 2)
+ inst.relax = opcode;
}
inst.reloc.pc_rel = 1;
@@ -6562,21 +6632,67 @@ do_t_ldrexd (void)
static void
do_t_ldst (void)
{
+ unsigned long opcode;
+ int Rn;
+
+ opcode = inst.instruction;
if (unified_syntax)
{
- /* Generation of 16-bit instructions for anything other than
- Rd, [Rn, Ri] is deferred to section relaxation time. */
- if (inst.operands[1].isreg && inst.operands[1].immisreg
+ if (inst.operands[1].isreg
+ && !inst.operands[1].writeback
&& !inst.operands[1].shifted && !inst.operands[1].postind
&& !inst.operands[1].negative && inst.operands[0].reg <= 7
- && inst.operands[1].reg <= 7 && inst.operands[1].imm <= 7
- && inst.instruction <= 0xffff)
+ && opcode <= 0xffff
+ && inst.size_req != 4)
{
- inst.instruction = THUMB_OP16 (inst.instruction);
- goto op16;
+ /* Insn may have a 16-bit form. */
+ Rn = inst.operands[1].reg;
+ if (inst.operands[1].immisreg)
+ {
+ inst.instruction = THUMB_OP16 (opcode);
+ /* [Rn, Ri] */
+ if (Rn <= 7 && inst.operands[1].imm <= 7)
+ goto op16;
+ }
+ else if ((Rn <= 7 && opcode != T_MNEM_ldrsh
+ && opcode != T_MNEM_ldrsb)
+ || ((Rn == REG_PC || Rn == REG_SP) && opcode == T_MNEM_ldr)
+ || (Rn == REG_SP && opcode == T_MNEM_str))
+ {
+ /* [Rn, #const] */
+ if (Rn > 7)
+ {
+ if (Rn == REG_PC)
+ {
+ if (inst.reloc.pc_rel)
+ opcode = T_MNEM_ldr_pc2;
+ else
+ opcode = T_MNEM_ldr_pc;
+ }
+ else
+ {
+ if (opcode == T_MNEM_ldr)
+ opcode = T_MNEM_ldr_sp;
+ else
+ opcode = T_MNEM_str_sp;
+ }
+ inst.instruction = inst.operands[0].reg << 8;
+ }
+ else
+ {
+ inst.instruction = inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ inst.instruction |= THUMB_OP16 (opcode);
+ if (inst.size_req == 2)
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ else
+ inst.relax = opcode;
+ return;
+ }
}
-
- inst.instruction = THUMB_OP32 (inst.instruction);
+ /* Definitely a 32-bit variant. */
+ inst.instruction = THUMB_OP32 (opcode);
inst.instruction |= inst.operands[0].reg << 12;
encode_thumb32_addr_mode (1, /*is_t=*/FALSE, /*is_d=*/FALSE);
return;
@@ -6708,26 +6824,41 @@ do_t_mov_cmp (void)
{
int r0off = (inst.instruction == T_MNEM_mov
|| inst.instruction == T_MNEM_movs) ? 8 : 16;
+ unsigned long opcode;
bfd_boolean narrow;
bfd_boolean low_regs;
low_regs = (inst.operands[0].reg <= 7 && inst.operands[1].reg <= 7);
+ opcode = inst.instruction;
if (current_it_mask)
- narrow = inst.instruction != T_MNEM_movs;
+ narrow = opcode != T_MNEM_movs;
else
- narrow = inst.instruction != T_MNEM_movs || low_regs;
+ narrow = opcode != T_MNEM_movs || low_regs;
if (inst.size_req == 4
|| inst.operands[1].shifted)
narrow = FALSE;
if (!inst.operands[1].isreg)
{
- /* For an immediate, we always generate a 32-bit opcode;
- section relaxation will shrink it later if possible. */
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
- inst.instruction |= inst.operands[0].reg << r0off;
- inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ /* Immediate operand. */
+ if (current_it_mask == 0 && opcode == T_MNEM_mov)
+ narrow = 0;
+ if (low_regs && narrow)
+ {
+ inst.instruction = THUMB_OP16 (opcode);
+ inst.instruction |= inst.operands[0].reg << 8;
+ if (inst.size_req == 2)
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
+ else
+ inst.relax = opcode;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= inst.operands[0].reg << r0off;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
}
else if (!narrow)
{
@@ -7501,6 +7632,46 @@ fix_new_arm (fragS * frag,
new_fix->tc_fix_data = thumb_mode;
}
+/* Create a frg for an instruction requiring relaxation. */
+static void
+output_relax_insn (void)
+{
+ char * to;
+ symbolS *sym;
+ int offset;
+
+ switch (inst.reloc.exp.X_op)
+ {
+ case O_symbol:
+ sym = inst.reloc.exp.X_add_symbol;
+ offset = inst.reloc.exp.X_add_number;
+ break;
+ case O_constant:
+ sym = NULL;
+ offset = inst.reloc.exp.X_add_number;
+ break;
+ default:
+ sym = make_expr_symbol (&inst.reloc.exp);
+ offset = 0;
+ break;
+ }
+ to = frag_var (rs_machine_dependent, INSN_SIZE, THUMB_SIZE,
+ inst.relax, sym, offset, NULL/*offset, opcode*/);
+ md_number_to_chars (to, inst.instruction, THUMB_SIZE);
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (INSN_SIZE);
+#endif
+}
+
+/* Write a 32-bit thumb instruction to buf. */
+static void
+put_thumb32_insn (char * buf, unsigned long insn)
+{
+ md_number_to_chars (buf, insn >> 16, THUMB_SIZE);
+ md_number_to_chars (buf + THUMB_SIZE, insn, THUMB_SIZE);
+}
+
static void
output_inst (const char * str)
{
@@ -7511,6 +7682,10 @@ output_inst (const char * str)
as_bad ("%s -- `%s'", inst.error, str);
return;
}
+ if (inst.relax) {
+ output_relax_insn();
+ return;
+ }
if (inst.size == 0)
return;
@@ -7519,8 +7694,7 @@ output_inst (const char * str)
if (thumb_mode && (inst.size > THUMB_SIZE))
{
assert (inst.size == (2 * THUMB_SIZE));
- md_number_to_chars (to, inst.instruction >> 16, THUMB_SIZE);
- md_number_to_chars (to + THUMB_SIZE, inst.instruction, THUMB_SIZE);
+ put_thumb32_insn (to, inst.instruction);
}
else if (inst.size > INSN_SIZE)
{
@@ -7819,7 +7993,7 @@ md_assemble (char *str)
if (current_it_mask == 0x10)
current_it_mask = 0;
- if (!inst.error)
+ if (!(inst.error || inst.relax))
{
assert (inst.instruction < 0xe800 || inst.instruction > 0xffff);
inst.size = (inst.instruction > 0xffff ? 4 : 2);
@@ -8364,7 +8538,7 @@ static const struct asm_opcode insns[] =
tC3(ldmfd, 8900000, ldmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
TCE(swi, f000000, df00, 1, (EXPi), swi, t_swi),
- TCE(b, a000000, e000, 1, (EXPr), branch, t_branch),
+ tCE(b, a000000, b, 1, (EXPr), branch, t_branch),
TCE(bl, b000000, f000f800, 1, (EXPr), branch, t_branch23),
/* Pseudo ops. */
@@ -9600,12 +9774,344 @@ md_chars_to_number (char * buf, int n)
/* MD interface: Sections. */
+/* Estimate the size of a frag before relaxing. Assume everything fits in
+ 2 bytes. */
+
int
-md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
+md_estimate_size_before_relax (fragS * fragp,
segT segtype ATTRIBUTE_UNUSED)
{
- as_fatal (_("md_estimate_size_before_relax\n"));
- return 1;
+ fragp->fr_var = 2;
+ return 2;
+}
+
+/* Convert a machine dependent frag. */
+
+void
+md_convert_frag (bfd *abfd, segT asec ATTRIBUTE_UNUSED, fragS *fragp)
+{
+ unsigned long insn;
+ unsigned long old_op;
+ char *buf;
+ expressionS exp;
+ fixS *fixp;
+ int reloc_type;
+ int pc_rel;
+ int opcode;
+
+ buf = fragp->fr_literal + fragp->fr_fix;
+
+ old_op = bfd_get_16(abfd, buf);
+ if (fragp->fr_symbol) {
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ } else {
+ exp.X_op = O_constant;
+ }
+ exp.X_add_number = fragp->fr_offset;
+ opcode = fragp->fr_subtype;
+ switch (opcode)
+ {
+ case T_MNEM_ldr_pc:
+ case T_MNEM_ldr_pc2:
+ case T_MNEM_ldr_sp:
+ case T_MNEM_str_sp:
+ case T_MNEM_ldr:
+ case T_MNEM_ldrb:
+ case T_MNEM_ldrh:
+ case T_MNEM_str:
+ case T_MNEM_strb:
+ case T_MNEM_strh:
+ if (fragp->fr_var == 4)
+ {
+ insn = THUMB_OP32(opcode);
+ if ((old_op >> 12) == 4 || (old_op >> 12) == 9)
+ {
+ insn |= (old_op & 0x700) << 4;
+ }
+ else
+ {
+ insn |= (old_op & 7) << 12;
+ insn |= (old_op & 0x38) << 13;
+ }
+ insn |= 0x00000c00;
+ put_thumb32_insn (buf, insn);
+ reloc_type = BFD_RELOC_ARM_T32_OFFSET_IMM;
+ }
+ else
+ {
+ reloc_type = BFD_RELOC_ARM_THUMB_OFFSET;
+ }
+ pc_rel = (opcode == T_MNEM_ldr_pc2);
+ break;
+ case T_MNEM_adr:
+ if (fragp->fr_var == 4)
+ {
+ insn = THUMB_OP32 (opcode);
+ insn |= (old_op & 0xf0) << 4;
+ put_thumb32_insn (buf, insn);
+ reloc_type = BFD_RELOC_ARM_T32_ADD_PC12;
+ }
+ else
+ {
+ reloc_type = BFD_RELOC_ARM_THUMB_ADD;
+ exp.X_add_number -= 4;
+ }
+ pc_rel = 1;
+ break;
+ case T_MNEM_mov:
+ case T_MNEM_movs:
+ case T_MNEM_cmp:
+ case T_MNEM_cmn:
+ if (fragp->fr_var == 4)
+ {
+ int r0off = (opcode == T_MNEM_mov
+ || opcode == T_MNEM_movs) ? 0 : 8;
+ insn = THUMB_OP32 (opcode);
+ insn = (insn & 0xe1ffffff) | 0x10000000;
+ insn |= (old_op & 0x700) << r0off;
+ put_thumb32_insn (buf, insn);
+ reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ {
+ reloc_type = BFD_RELOC_ARM_THUMB_IMM;
+ }
+ pc_rel = 0;
+ break;
+ case T_MNEM_b:
+ if (fragp->fr_var == 4)
+ {
+ insn = THUMB_OP32(opcode);
+ put_thumb32_insn (buf, insn);
+ reloc_type = BFD_RELOC_THUMB_PCREL_BRANCH25;
+ }
+ else
+ reloc_type = BFD_RELOC_THUMB_PCREL_BRANCH12;
+ pc_rel = 1;
+ break;
+ case T_MNEM_bcond:
+ if (fragp->fr_var == 4)
+ {
+ insn = THUMB_OP32(opcode);
+ insn |= (old_op & 0xf00) << 14;
+ put_thumb32_insn (buf, insn);
+ reloc_type = BFD_RELOC_THUMB_PCREL_BRANCH20;
+ }
+ else
+ reloc_type = BFD_RELOC_THUMB_PCREL_BRANCH9;
+ pc_rel = 1;
+ break;
+ case T_MNEM_add_sp:
+ case T_MNEM_add_pc:
+ case T_MNEM_inc_sp:
+ case T_MNEM_dec_sp:
+ if (fragp->fr_var == 4)
+ {
+ /* ??? Choose between add and addw. */
+ insn = THUMB_OP32 (opcode);
+ insn |= (old_op & 0xf0) << 4;
+ put_thumb32_insn (buf, insn);
+ reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ reloc_type = BFD_RELOC_ARM_THUMB_ADD;
+ pc_rel = 0;
+ break;
+
+ case T_MNEM_addi:
+ case T_MNEM_addis:
+ case T_MNEM_subi:
+ case T_MNEM_subis:
+ if (fragp->fr_var == 4)
+ {
+ insn = THUMB_OP32 (opcode);
+ insn |= (old_op & 0xf0) << 4;
+ insn |= (old_op & 0xf) << 16;
+ put_thumb32_insn (buf, insn);
+ reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ reloc_type = BFD_RELOC_ARM_THUMB_ADD;
+ pc_rel = 0;
+ break;
+ default:
+ abort();
+ }
+ fixp = fix_new_exp (fragp, fragp->fr_fix, fragp->fr_var, &exp, pc_rel,
+ reloc_type);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+ fragp->fr_fix += fragp->fr_var;
+}
+
+/* Return the size of a relaxable immediate operand instruction.
+ SHIFT and SIZE specify the form of the allowable immediate. */
+static int
+relax_immediate (fragS *fragp, int size, int shift)
+{
+ offsetT offset;
+ offsetT mask;
+ offsetT low;
+
+ /* ??? Should be able to do better than this. */
+ if (fragp->fr_symbol)
+ return 4;
+
+ low = (1 << shift) - 1;
+ mask = (1 << (shift + size)) - (1 << shift);
+ offset = fragp->fr_offset;
+ /* Force misaligned offsets to 32-bit variant. */
+ if (offset & low)
+ return -4;
+ if (offset & ~mask)
+ return 4;
+ return 2;
+}
+
+/* Return the size of a relaxable adr pseudo-instruction or PC-relative
+ load. */
+static int
+relax_adr (fragS *fragp, asection *sec)
+{
+ addressT addr;
+ offsetT val;
+
+ /* Assume worst case for symbols not known to be in the same section. */
+ if (!S_IS_DEFINED(fragp->fr_symbol)
+ || sec != S_GET_SEGMENT (fragp->fr_symbol))
+ return 4;
+
+ val = S_GET_VALUE(fragp->fr_symbol) + fragp->fr_offset;
+ addr = fragp->fr_address + fragp->fr_fix;
+ addr = (addr + 4) & ~3;
+ /* Fix the insn as the 4-byte version if the target address is not
+ sufficiently aligned. This is prevents an infinite loop when two
+ instructions have contradictory range/alignment requirements. */
+ if (val & 3)
+ return -4;
+ val -= addr;
+ if (val < 0 || val > 1020)
+ return 4;
+ return 2;
+}
+
+/* Return the size of a relaxable add/sub immediate instruction. */
+static int
+relax_addsub (fragS *fragp, asection *sec)
+{
+ char *buf;
+ int op;
+
+ buf = fragp->fr_literal + fragp->fr_fix;
+ op = bfd_get_16(sec->owner, buf);
+ if ((op & 0xf) == ((op >> 4) & 0xf))
+ return relax_immediate (fragp, 8, 0);
+ else
+ return relax_immediate (fragp, 3, 0);
+}
+
+
+/* Return the size of a relaxable branch instruction. BITS is the
+ size of the offset field in the narrow instruction. */
+
+static int
+relax_branch (fragS *fragp, asection *sec, int bits)
+{
+ addressT addr;
+ offsetT val;
+ offsetT limit;
+
+ /* Assume worst case for symbols not known to be in the same section. */
+ if (!S_IS_DEFINED(fragp->fr_symbol)
+ || sec != S_GET_SEGMENT (fragp->fr_symbol))
+ return 4;
+
+ val = S_GET_VALUE(fragp->fr_symbol) + fragp->fr_offset;
+ addr = fragp->fr_address + fragp->fr_fix + 4;
+ val -= addr;
+
+ /* Offset is a signed value *2 */
+ limit = 1 << bits;
+ if (val >= limit || val < -limit)
+ return 4;
+ return 2;
+}
+
+
+/* Relax a machine dependent frag. This returns the amount by which
+ the current size of the frag should change. */
+
+int
+arm_relax_frag (asection *sec, fragS *fragp, long stretch ATTRIBUTE_UNUSED)
+{
+ int oldsize;
+ int newsize;
+
+ oldsize = fragp->fr_var;
+ switch (fragp->fr_subtype)
+ {
+ case T_MNEM_ldr_pc2:
+ newsize = relax_adr(fragp, sec);
+ break;
+ case T_MNEM_ldr_pc:
+ case T_MNEM_ldr_sp:
+ case T_MNEM_str_sp:
+ newsize = relax_immediate(fragp, 8, 2);
+ break;
+ case T_MNEM_ldr:
+ case T_MNEM_str:
+ newsize = relax_immediate(fragp, 5, 2);
+ break;
+ case T_MNEM_ldrh:
+ case T_MNEM_strh:
+ newsize = relax_immediate(fragp, 5, 1);
+ break;
+ case T_MNEM_ldrb:
+ case T_MNEM_strb:
+ newsize = relax_immediate(fragp, 5, 0);
+ break;
+ case T_MNEM_adr:
+ newsize = relax_adr(fragp, sec);
+ break;
+ case T_MNEM_mov:
+ case T_MNEM_movs:
+ case T_MNEM_cmp:
+ case T_MNEM_cmn:
+ newsize = relax_immediate(fragp, 8, 0);
+ break;
+ case T_MNEM_b:
+ newsize = relax_branch(fragp, sec, 11);
+ break;
+ case T_MNEM_bcond:
+ newsize = relax_branch(fragp, sec, 8);
+ break;
+ case T_MNEM_add_sp:
+ case T_MNEM_add_pc:
+ newsize = relax_immediate (fragp, 8, 2);
+ break;
+ case T_MNEM_inc_sp:
+ case T_MNEM_dec_sp:
+ newsize = relax_immediate (fragp, 7, 2);
+ break;
+ case T_MNEM_addi:
+ case T_MNEM_addis:
+ case T_MNEM_subi:
+ case T_MNEM_subis:
+ newsize = relax_addsub (fragp, sec);
+ break;
+ default:
+ abort();
+ }
+ if (newsize < 0)
+ {
+ fragp->fr_var = -newsize;
+ md_convert_frag (sec->owner, sec, fragp);
+ frag_wane(fragp);
+ return -(newsize + oldsize);
+ }
+ fragp->fr_var = newsize;
+ return newsize - oldsize;
}
/* Round up a section size to the appropriate boundary. */
@@ -11476,7 +11982,10 @@ arm_force_relocation (struct fix * fixp)
/* Resolve these relocations even if the symbol is extern or weak. */
if (fixp->fx_r_type == BFD_RELOC_ARM_IMMEDIATE
|| fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM
- || fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
+ || fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_IMM12
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_PC12)
return 0;
return generic_force_reloc (fixp);
diff --git a/gas/config/tc-arm.h b/gas/config/tc-arm.h
index 1def078..c2ae6d6 100644
--- a/gas/config/tc-arm.h
+++ b/gas/config/tc-arm.h
@@ -79,7 +79,9 @@ struct fix;
#define TC_FORCE_RELOCATION(FIX) arm_force_relocation (FIX)
-#define md_convert_frag(b, s, f) { as_fatal (_("arm convert_frag\n")); }
+#define md_relax_frag(segment, fragp, stretch) \
+ arm_relax_frag(segment, fragp, stretch)
+extern int arm_relax_frag (asection *, struct frag *, long);
#define md_cleanup() arm_cleanup ()