diff options
Diffstat (limited to 'bfd/elfxx-ia64.c')
-rw-r--r-- | bfd/elfxx-ia64.c | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/bfd/elfxx-ia64.c b/bfd/elfxx-ia64.c index e64b1d398c4..cc8e5beebe3 100644 --- a/bfd/elfxx-ia64.c +++ b/bfd/elfxx-ia64.c @@ -681,6 +681,141 @@ bfd_elfNN_ia64_after_parse (int itanium) oor_branch_size = itanium ? sizeof (oor_ip) : sizeof (oor_brl); } +#define BTYPE_SHIFT 6 +#define Y_SHIFT 26 +#define X6_SHIFT 27 +#define X4_SHIFT 27 +#define X3_SHIFT 33 +#define X2_SHIFT 31 +#define X_SHIFT 33 +#define OPCODE_SHIFT 37 + +#define OPCODE_BITS (0xfLL << OPCODE_SHIFT) +#define X6_BITS (0x3fLL << X6_SHIFT) +#define X4_BITS (0xfLL << X4_SHIFT) +#define X3_BITS (0x7LL << X3_SHIFT) +#define X2_BITS (0x3LL << X2_SHIFT) +#define X_BITS (0x1LL << X_SHIFT) +#define Y_BITS (0x1LL << Y_SHIFT) +#define BTYPE_BITS (0x7LL << BTYPE_SHIFT) +#define PREDICATE_BITS (0x3fLL) + +#define IS_NOP_B(i) \ + (((i) & (OPCODE_BITS | X6_BITS)) == (2LL << OPCODE_SHIFT)) +#define IS_NOP_F(i) \ + (((i) & (OPCODE_BITS | X_BITS | X6_BITS | Y_BITS)) \ + == (0x1LL << X6_SHIFT)) +#define IS_NOP_I(i) \ + (((i) & (OPCODE_BITS | X3_BITS | X6_BITS | Y_BITS)) \ + == (0x1LL << X6_SHIFT)) +#define IS_NOP_M(i) \ + (((i) & (OPCODE_BITS | X3_BITS | X2_BITS | X4_BITS | Y_BITS)) \ + == (0x1LL << X4_SHIFT)) +#define IS_BR_COND(i) \ + (((i) & (OPCODE_BITS | BTYPE_BITS)) == (0x4LL << OPCODE_SHIFT)) +#define IS_BR_CALL(i) \ + (((i) & OPCODE_BITS) == (0x5LL << OPCODE_SHIFT)) + +static bfd_boolean +elfNN_ia64_relax_br (bfd_byte *contents, bfd_vma off) +{ + unsigned int template, mlx; + bfd_vma t0, t1, s0, s1, s2, br_code; + long br_slot; + bfd_byte *hit_addr; + + hit_addr = (bfd_byte *) (contents + off); + br_slot = (long) hit_addr & 0x3; + hit_addr -= br_slot; + t0 = bfd_getl64 (hit_addr + 0); + t1 = bfd_getl64 (hit_addr + 8); + + /* Check if we can turn br into brl. A label is always at the start + of the bundle. Even if there are predicates on NOPs, we still + perform this optimization. */ + template = t0 & 0x1e; + s0 = (t0 >> 5) & 0x1ffffffffffLL; + s1 = ((t0 >> 46) | (t1 << 18)) & 0x1ffffffffffLL; + s2 = (t1 >> 23) & 0x1ffffffffffLL; + switch (br_slot) + { + case 0: + /* Check if slot 1 and slot 2 are NOPs. Possible template is + BBB. We only need to check nop.b. */ + if (!(IS_NOP_B (s1) && IS_NOP_B (s2))) + return FALSE; + br_code = s0; + break; + case 1: + /* Check if slot 2 is NOP. Possible templates are MBB and BBB. + For BBB, slot 0 also has to be nop.b. */ + if (!((template == 0x12 /* MBB */ + && IS_NOP_B (s2)) + || (template == 0x16 /* BBB */ + && IS_NOP_B (s0) + && IS_NOP_B (s2)))) + return FALSE; + br_code = s1; + break; + case 2: + /* Check if slot 1 is NOP. Possible templates are MIB, MBB, BBB, + MMB and MFB. For BBB, slot 0 also has to be nop.b. */ + if (!((template == 0x10 /* MIB */ + && IS_NOP_I (s1)) + || (template == 0x12 /* MBB */ + && IS_NOP_B (s1)) + || (template == 0x16 /* BBB */ + && IS_NOP_B (s0) + && IS_NOP_B (s1)) + || (template == 0x18 /* MMB */ + && IS_NOP_M (s1)) + || (template == 0x1c /* MFB */ + && IS_NOP_F (s1)))) + return FALSE; + br_code = s2; + break; + default: + /* It should never happen. */ + abort (); + } + + /* We can turn br.cond/br.call into brl.cond/brl.call. */ + if (!(IS_BR_COND (br_code) || IS_BR_CALL (br_code))) + return FALSE; + + /* Turn br into brl by setting bit 40. */ + br_code |= 0x1LL << 40; + + /* Turn the old bundle into a MLX bundle with the same stop-bit + variety. */ + if (t0 & 0x1) + mlx = 0x5; + else + mlx = 0x4; + + if (template == 0x16) + { + /* For BBB, we need to put nop.m in slot 0 and keep the original + predicate. */ + t0 &= PREDICATE_BITS << 5; + t0 |= 0x1LL << (X4_SHIFT + 5); + } + else + { + /* Keep the original instruction in slot 0. */ + t0 &= 0x1ffffffffffLL << 5; + } + + t0 |= mlx; + + /* Put brl in slot 1. */ + t1 = br_code << 23; + + bfd_putl64 (t0, hit_addr); + bfd_putl64 (t1, hit_addr + 8); + return TRUE; +} + static void elfNN_ia64_relax_brl (bfd_byte *contents, bfd_vma off) { @@ -985,6 +1120,16 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again) } else if (r_type == R_IA64_PCREL60B) continue; + else if (elfNN_ia64_relax_br (contents, roff)) + { + irel->r_info + = ELFNN_R_INFO (ELFNN_R_SYM (irel->r_info), + R_IA64_PCREL60B); + + /* Make the relocation offset point to slot 1. */ + irel->r_offset = (irel->r_offset & ~((bfd_vma) 0x3)) + 1; + continue; + } /* We can't put a trampoline in a .init/.fini section. Issue an error. */ |