diff options
author | Paul Brook <paul@codesourcery.com> | 2005-09-06 16:59:24 +0000 |
---|---|---|
committer | Paul Brook <paul@codesourcery.com> | 2005-09-06 16:59:24 +0000 |
commit | 0110f2b896d40a2ab1d19b85f7990d1c1a45792a (patch) | |
tree | 0b9fef94c8cfa8d6ff26146fd8578e43db60f1b6 /gas/config | |
parent | 9a64e435419869030e27d54f0433810b8b56d1ac (diff) | |
download | gdb-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.c | 613 | ||||
-rw-r--r-- | gas/config/tc-arm.h | 4 |
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 () |