X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gold%2Faarch64.cc;h=a72e2c31b2afec6ec833143f9a3beeab7af1c3a3;hb=73b9be8b5301c4ac056e10c38a47414867ee892a;hp=27457760a154e2e5d4319e3a688e0d3f2e3092e9;hpb=7a2a1c793578a8468604e661dda025ecb8d0bd20;p=deliverable%2Fbinutils-gdb.git diff --git a/gold/aarch64.cc b/gold/aarch64.cc index 27457760a1..a72e2c31b2 100644 --- a/gold/aarch64.cc +++ b/gold/aarch64.cc @@ -1,6 +1,6 @@ // aarch64.cc -- aarch64 target support for gold. -// Copyright (C) 2014-2015 Free Software Foundation, Inc. +// Copyright (C) 2014-2017 Free Software Foundation, Inc. // Written by Jing Yu and Han Shen . // This file is part of gold. @@ -24,6 +24,7 @@ #include #include +#include #include "elfcpp.h" #include "dwarf.h" @@ -76,7 +77,10 @@ class AArch64_insn_utilities public: typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype; - static const int BYTES_PER_INSN = 4; + static const int BYTES_PER_INSN; + + // Zero register encoding - 31. + static const unsigned int AARCH64_ZR; static unsigned int aarch64_bit(Insntype insn, int pos) @@ -86,10 +90,30 @@ public: aarch64_bits(Insntype insn, int pos, int l) { return (insn >> pos) & ((1 << l) - 1); } + // Get the encoding field "op31" of 3-source data processing insns. "op31" is + // the name defined in armv8 insn manual C3.5.9. + static unsigned int + aarch64_op31(Insntype insn) + { return aarch64_bits(insn, 21, 3); } + + // Get the encoding field "ra" of 3-source data processing insns. "ra" is the + // third source register. See armv8 insn manual C3.5.9. + static unsigned int + aarch64_ra(Insntype insn) + { return aarch64_bits(insn, 10, 5); } + + static bool + is_adr(const Insntype insn) + { return (insn & 0x9F000000) == 0x10000000; } + static bool is_adrp(const Insntype insn) { return (insn & 0x9F000000) == 0x90000000; } + static bool + is_mrs_tpidr_el0(const Insntype insn) + { return (insn & 0xFFFFFFE0) == 0xd53bd040; } + static unsigned int aarch64_rm(const Insntype insn) { return aarch64_bits(insn, 16, 5); } @@ -110,6 +134,39 @@ public: aarch64_rt2(const Insntype insn) { return aarch64_bits(insn, 10, 5); } + // Encode imm21 into adr. Signed imm21 is in the range of [-1M, 1M). + static Insntype + aarch64_adr_encode_imm(Insntype adr, int imm21) + { + gold_assert(is_adr(adr)); + gold_assert(-(1 << 20) <= imm21 && imm21 < (1 << 20)); + const int mask19 = (1 << 19) - 1; + const int mask2 = 3; + adr &= ~((mask19 << 5) | (mask2 << 29)); + adr |= ((imm21 & mask2) << 29) | (((imm21 >> 2) & mask19) << 5); + return adr; + } + + // Retrieve encoded adrp 33-bit signed imm value. This value is obtained by + // 21-bit signed imm encoded in the insn multiplied by 4k (page size) and + // 64-bit sign-extended, resulting in [-4G, 4G) with 12-lsb being 0. + static int64_t + aarch64_adrp_decode_imm(const Insntype adrp) + { + const int mask19 = (1 << 19) - 1; + const int mask2 = 3; + gold_assert(is_adrp(adrp)); + // 21-bit imm encoded in adrp. + uint64_t imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2); + // Retrieve msb of 21-bit-signed imm for sign extension. + uint64_t msbt = (imm >> 20) & 1; + // Real value is imm multiplied by 4k. Value now has 33-bit information. + int64_t value = imm << 12; + // Sign extend to 64-bit by repeating msbt 31 (64-33) times and merge it + // with value. + return ((((uint64_t)(1) << 32) - msbt) << 33) | value; + } + static bool aarch64_b(const Insntype insn) { return (insn & 0xFC000000) == 0x14000000; } @@ -213,7 +270,7 @@ public: uint32_t v = 0; uint32_t opc_v = 0; - /* Bail out quickly if INSN doesn't fall into the the load-store + /* Bail out quickly if INSN doesn't fall into the load-store encoding space. */ if (!aarch64_ldst (insn)) return false; @@ -329,8 +386,42 @@ public: return true; } return false; + } // End of "aarch64_mem_op_p". + + // Return true if INSN is mac insn. + static bool + aarch64_mac(Insntype insn) + { return (insn & 0xff000000) == 0x9b000000; } + + // Return true if INSN is multiply-accumulate. + // (This is similar to implementaton in elfnn-aarch64.c.) + static bool + aarch64_mlxl(Insntype insn) + { + uint32_t op31 = aarch64_op31(insn); + if (aarch64_mac(insn) + && (op31 == 0 || op31 == 1 || op31 == 5) + /* Exclude MUL instructions which are encoded as a multiple-accumulate + with RA = XZR. */ + && aarch64_ra(insn) != AARCH64_ZR) + { + return true; + } + return false; } -}; +}; // End of "AArch64_insn_utilities". + + +// Insn length in byte. + +template +const int AArch64_insn_utilities::BYTES_PER_INSN = 4; + + +// Zero register encoding - 31. + +template +const unsigned int AArch64_insn_utilities::AARCH64_ZR = 0x1f; // Output_data_got_aarch64 class. @@ -576,91 +667,190 @@ template class AArch64_output_section; -// Reloc stub class. - template -class Reloc_stub +class AArch64_relobj; + + +// Stub type enum constants. + +enum { - public: - typedef Reloc_stub This; - typedef typename elfcpp::Elf_types::Elf_Addr AArch64_address; + ST_NONE = 0, - // Do not change the value of the enums, they are used to index into - // stub_insns array. - typedef enum - { - ST_NONE = 0, + // Using adrp/add pair, 4 insns (including alignment) without mem access, + // the fastest stub. This has a limited jump distance, which is tested by + // aarch64_valid_for_adrp_p. + ST_ADRP_BRANCH = 1, - // Using adrp/add pair, 4 insns (including alignment) without mem access, - // the fastest stub. This has a limited jump distance, which is tested by - // aarch64_valid_for_adrp_p. - ST_ADRP_BRANCH = 1, + // Using ldr-absolute-address/br-register, 4 insns with 1 mem access, + // unlimited in jump distance. + ST_LONG_BRANCH_ABS = 2, - // Using ldr-absolute-address/br-register, 4 insns with 1 mem access, - // unlimited in jump distance. - ST_LONG_BRANCH_ABS = 2, + // Using ldr/calculate-pcrel/jump, 8 insns (including alignment) with 1 + // mem access, slowest one. Only used in position independent executables. + ST_LONG_BRANCH_PCREL = 3, - // Using ldr/calculate-pcrel/jump, 8 insns (including alignment) with 1 mem - // access, slowest one. Only used in position independent executables. - ST_LONG_BRANCH_PCREL = 3, + // Stub for erratum 843419 handling. + ST_E_843419 = 4, - } Stub_type; + // Stub for erratum 835769 handling. + ST_E_835769 = 5, - // Branch range. This is used to calculate the section group size, as well as - // determine whether a stub is needed. - static const int MAX_BRANCH_OFFSET = ((1 << 25) - 1) << 2; - static const int MIN_BRANCH_OFFSET = -((1 << 25) << 2); + // Number of total stub types. + ST_NUMBER = 6 +}; - // Constant used to determine if an offset fits in the adrp instruction - // encoding. - static const int MAX_ADRP_IMM = (1 << 20) - 1; - static const int MIN_ADRP_IMM = -(1 << 20); - static const int BYTES_PER_INSN = 4; - static const int STUB_ADDR_ALIGN = 4; +// Struct that wraps insns for a particular stub. All stub templates are +// created/initialized as constants by Stub_template_repertoire. - // Determine whether the offset fits in the jump/branch instruction. - static bool - aarch64_valid_branch_offset_p(int64_t offset) - { return offset >= MIN_BRANCH_OFFSET && offset <= MAX_BRANCH_OFFSET; } +template +struct Stub_template +{ + const typename AArch64_insn_utilities::Insntype* insns; + const int insn_num; +}; - // Determine whether the offset fits in the adrp immediate field. - static bool - aarch64_valid_for_adrp_p(AArch64_address location, AArch64_address dest) + +// Simple singleton class that creates/initializes/stores all types of stub +// templates. + +template +class Stub_template_repertoire +{ +public: + typedef typename AArch64_insn_utilities::Insntype Insntype; + + // Single static method to get stub template for a given stub type. + static const Stub_template* + get_stub_template(int type) { - typedef AArch64_relocate_functions Reloc; - int64_t adrp_imm = (Reloc::Page(dest) - Reloc::Page(location)) >> 12; - return adrp_imm >= MIN_ADRP_IMM && adrp_imm <= MAX_ADRP_IMM; + static Stub_template_repertoire singleton; + return singleton.stub_templates_[type]; } - // Determine the stub type for a certain relocation or ST_NONE, if no stub is - // needed. - static Stub_type - stub_type_for_reloc(unsigned int r_type, AArch64_address address, - AArch64_address target); - - Reloc_stub(Stub_type stub_type) - : stub_type_(stub_type), offset_(invalid_offset), - destination_address_(invalid_address) +private: + // Constructor - creates/initializes all stub templates. + Stub_template_repertoire(); + ~Stub_template_repertoire() { } - ~Reloc_stub() - { } + // Disallowing copy ctor and copy assignment operator. + Stub_template_repertoire(Stub_template_repertoire&); + Stub_template_repertoire& operator=(Stub_template_repertoire&); - // Return offset of code stub from beginning of its containing stub table. - section_offset_type - offset() const + // Data that stores all insn templates. + const Stub_template* stub_templates_[ST_NUMBER]; +}; // End of "class Stub_template_repertoire". + + +// Constructor - creates/initilizes all stub templates. + +template +Stub_template_repertoire::Stub_template_repertoire() +{ + // Insn array definitions. + const static Insntype ST_NONE_INSNS[] = {}; + + const static Insntype ST_ADRP_BRANCH_INSNS[] = + { + 0x90000010, /* adrp ip0, X */ + /* ADR_PREL_PG_HI21(X) */ + 0x91000210, /* add ip0, ip0, :lo12:X */ + /* ADD_ABS_LO12_NC(X) */ + 0xd61f0200, /* br ip0 */ + 0x00000000, /* alignment padding */ + }; + + const static Insntype ST_LONG_BRANCH_ABS_INSNS[] = + { + 0x58000050, /* ldr ip0, 0x8 */ + 0xd61f0200, /* br ip0 */ + 0x00000000, /* address field */ + 0x00000000, /* address fields */ + }; + + const static Insntype ST_LONG_BRANCH_PCREL_INSNS[] = + { + 0x58000090, /* ldr ip0, 0x10 */ + 0x10000011, /* adr ip1, #0 */ + 0x8b110210, /* add ip0, ip0, ip1 */ + 0xd61f0200, /* br ip0 */ + 0x00000000, /* address field */ + 0x00000000, /* address field */ + 0x00000000, /* alignment padding */ + 0x00000000, /* alignment padding */ + }; + + const static Insntype ST_E_843419_INSNS[] = + { + 0x00000000, /* Placeholder for erratum insn. */ + 0x14000000, /* b