+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+void
+x86_cleanup (void)
+{
+ char *p;
+ asection *seg = now_seg;
+ subsegT subseg = now_subseg;
+ asection *sec;
+ unsigned int alignment, align_size_1;
+ unsigned int isa_1_descsz, feature_2_descsz, descsz;
+ unsigned int isa_1_descsz_raw, feature_2_descsz_raw;
+ unsigned int padding;
+
+ if (!IS_ELF || !x86_used_note)
+ return;
+
+ x86_feature_2_used |= GNU_PROPERTY_X86_FEATURE_2_X86;
+
+ /* The .note.gnu.property section layout:
+
+ Field Length Contents
+ ---- ---- ----
+ n_namsz 4 4
+ n_descsz 4 The note descriptor size
+ n_type 4 NT_GNU_PROPERTY_TYPE_0
+ n_name 4 "GNU"
+ n_desc n_descsz The program property array
+ .... .... ....
+ */
+
+ /* Create the .note.gnu.property section. */
+ sec = subseg_new (NOTE_GNU_PROPERTY_SECTION_NAME, 0);
+ bfd_set_section_flags (sec,
+ (SEC_ALLOC
+ | SEC_LOAD
+ | SEC_DATA
+ | SEC_HAS_CONTENTS
+ | SEC_READONLY));
+
+ if (get_elf_backend_data (stdoutput)->s->elfclass == ELFCLASS64)
+ {
+ align_size_1 = 7;
+ alignment = 3;
+ }
+ else
+ {
+ align_size_1 = 3;
+ alignment = 2;
+ }
+
+ bfd_set_section_alignment (sec, alignment);
+ elf_section_type (sec) = SHT_NOTE;
+
+ /* GNU_PROPERTY_X86_ISA_1_USED: 4-byte type + 4-byte data size
+ + 4-byte data */
+ isa_1_descsz_raw = 4 + 4 + 4;
+ /* Align GNU_PROPERTY_X86_ISA_1_USED. */
+ isa_1_descsz = (isa_1_descsz_raw + align_size_1) & ~align_size_1;
+
+ feature_2_descsz_raw = isa_1_descsz;
+ /* GNU_PROPERTY_X86_FEATURE_2_USED: 4-byte type + 4-byte data size
+ + 4-byte data */
+ feature_2_descsz_raw += 4 + 4 + 4;
+ /* Align GNU_PROPERTY_X86_FEATURE_2_USED. */
+ feature_2_descsz = ((feature_2_descsz_raw + align_size_1)
+ & ~align_size_1);
+
+ descsz = feature_2_descsz;
+ /* Section size: n_namsz + n_descsz + n_type + n_name + n_descsz. */
+ p = frag_more (4 + 4 + 4 + 4 + descsz);
+
+ /* Write n_namsz. */
+ md_number_to_chars (p, (valueT) 4, 4);
+
+ /* Write n_descsz. */
+ md_number_to_chars (p + 4, (valueT) descsz, 4);
+
+ /* Write n_type. */
+ md_number_to_chars (p + 4 * 2, (valueT) NT_GNU_PROPERTY_TYPE_0, 4);
+
+ /* Write n_name. */
+ memcpy (p + 4 * 3, "GNU", 4);
+
+ /* Write 4-byte type. */
+ md_number_to_chars (p + 4 * 4,
+ (valueT) GNU_PROPERTY_X86_ISA_1_USED, 4);
+
+ /* Write 4-byte data size. */
+ md_number_to_chars (p + 4 * 5, (valueT) 4, 4);
+
+ /* Write 4-byte data. */
+ md_number_to_chars (p + 4 * 6, (valueT) x86_isa_1_used, 4);
+
+ /* Zero out paddings. */
+ padding = isa_1_descsz - isa_1_descsz_raw;
+ if (padding)
+ memset (p + 4 * 7, 0, padding);
+
+ /* Write 4-byte type. */
+ md_number_to_chars (p + isa_1_descsz + 4 * 4,
+ (valueT) GNU_PROPERTY_X86_FEATURE_2_USED, 4);
+
+ /* Write 4-byte data size. */
+ md_number_to_chars (p + isa_1_descsz + 4 * 5, (valueT) 4, 4);
+
+ /* Write 4-byte data. */
+ md_number_to_chars (p + isa_1_descsz + 4 * 6,
+ (valueT) x86_feature_2_used, 4);
+
+ /* Zero out paddings. */
+ padding = feature_2_descsz - feature_2_descsz_raw;
+ if (padding)
+ memset (p + isa_1_descsz + 4 * 7, 0, padding);
+
+ /* We probably can't restore the current segment, for there likely
+ isn't one yet... */
+ if (seg && subseg)
+ subseg_set (seg, subseg);
+}
+#endif
+
+static unsigned int
+encoding_length (const fragS *start_frag, offsetT start_off,
+ const char *frag_now_ptr)
+{
+ unsigned int len = 0;
+
+ if (start_frag != frag_now)
+ {
+ const fragS *fr = start_frag;
+
+ do {
+ len += fr->fr_fix;
+ fr = fr->fr_next;
+ } while (fr && fr != frag_now);
+ }
+
+ return len - start_off + (frag_now_ptr - frag_now->fr_literal);
+}
+
+/* Return 1 for test, and, cmp, add, sub, inc and dec which may
+ be macro-fused with conditional jumps. */
+
+static int
+maybe_fused_with_jcc_p (void)
+{
+ /* No RIP address. */
+ if (i.base_reg && i.base_reg->reg_num == RegIP)
+ return 0;
+
+ /* No VEX/EVEX encoding. */
+ if (is_any_vex_encoding (&i.tm))
+ return 0;
+
+ /* and, add, sub with destination register. */
+ if ((i.tm.base_opcode >= 0x20 && i.tm.base_opcode <= 0x25)
+ || i.tm.base_opcode <= 5
+ || (i.tm.base_opcode >= 0x28 && i.tm.base_opcode <= 0x2d)
+ || ((i.tm.base_opcode | 3) == 0x83
+ && ((i.tm.extension_opcode | 1) == 0x5
+ || i.tm.extension_opcode == 0x0)))
+ return (i.types[1].bitfield.class == Reg
+ || i.types[1].bitfield.instance == Accum);
+
+ /* test, cmp with any register. */
+ if ((i.tm.base_opcode | 1) == 0x85
+ || (i.tm.base_opcode | 1) == 0xa9
+ || ((i.tm.base_opcode | 1) == 0xf7
+ && i.tm.extension_opcode == 0)
+ || (i.tm.base_opcode >= 0x38 && i.tm.base_opcode <= 0x3d)
+ || ((i.tm.base_opcode | 3) == 0x83
+ && (i.tm.extension_opcode == 0x7)))
+ return (i.types[0].bitfield.class == Reg
+ || i.types[0].bitfield.instance == Accum
+ || i.types[1].bitfield.class == Reg
+ || i.types[1].bitfield.instance == Accum);
+
+ /* inc, dec with any register. */
+ if ((i.tm.cpu_flags.bitfield.cpuno64
+ && (i.tm.base_opcode | 0xf) == 0x4f)
+ || ((i.tm.base_opcode | 1) == 0xff
+ && i.tm.extension_opcode <= 0x1))
+ return (i.types[0].bitfield.class == Reg
+ || i.types[0].bitfield.instance == Accum);
+
+ return 0;
+}
+
+/* Return 1 if a FUSED_JCC_PADDING frag should be generated. */
+
+static int
+add_fused_jcc_padding_frag_p (void)
+{
+ /* NB: Don't work with COND_JUMP86 without i386. */
+ if (!align_branch_power
+ || now_seg == absolute_section
+ || !cpu_arch_flags.bitfield.cpui386
+ || !(align_branch & align_branch_fused_bit))
+ return 0;
+
+ if (maybe_fused_with_jcc_p ())
+ {
+ if (last_insn.kind == last_insn_other
+ || last_insn.seg != now_seg)
+ return 1;
+ if (flag_debug)
+ as_warn_where (last_insn.file, last_insn.line,
+ _("`%s` skips -malign-branch-boundary on `%s`"),
+ last_insn.name, i.tm.name);
+ }
+
+ return 0;
+}
+
+/* Return 1 if a BRANCH_PREFIX frag should be generated. */
+
+static int
+add_branch_prefix_frag_p (void)
+{
+ /* NB: Don't work with COND_JUMP86 without i386. Don't add prefix
+ to PadLock instructions since they include prefixes in opcode. */
+ if (!align_branch_power
+ || !align_branch_prefix_size
+ || now_seg == absolute_section
+ || i.tm.cpu_flags.bitfield.cpupadlock
+ || !cpu_arch_flags.bitfield.cpui386)
+ return 0;
+
+ /* Don't add prefix if it is a prefix or there is no operand in case
+ that segment prefix is special. */
+ if (!i.operands || i.tm.opcode_modifier.isprefix)
+ return 0;
+
+ if (last_insn.kind == last_insn_other
+ || last_insn.seg != now_seg)
+ return 1;
+
+ if (flag_debug)
+ as_warn_where (last_insn.file, last_insn.line,
+ _("`%s` skips -malign-branch-boundary on `%s`"),
+ last_insn.name, i.tm.name);
+
+ return 0;
+}
+
+/* Return 1 if a BRANCH_PADDING frag should be generated. */
+
+static int
+add_branch_padding_frag_p (enum align_branch_kind *branch_p)
+{
+ int add_padding;
+
+ /* NB: Don't work with COND_JUMP86 without i386. */
+ if (!align_branch_power
+ || now_seg == absolute_section
+ || !cpu_arch_flags.bitfield.cpui386)
+ return 0;
+
+ add_padding = 0;
+
+ /* Check for jcc and direct jmp. */
+ if (i.tm.opcode_modifier.jump == JUMP)
+ {
+ if (i.tm.base_opcode == JUMP_PC_RELATIVE)
+ {
+ *branch_p = align_branch_jmp;
+ add_padding = align_branch & align_branch_jmp_bit;
+ }
+ else
+ {
+ *branch_p = align_branch_jcc;
+ if ((align_branch & align_branch_jcc_bit))
+ add_padding = 1;
+ }
+ }
+ else if (is_any_vex_encoding (&i.tm))
+ return 0;
+ else if ((i.tm.base_opcode | 1) == 0xc3)
+ {
+ /* Near ret. */
+ *branch_p = align_branch_ret;
+ if ((align_branch & align_branch_ret_bit))
+ add_padding = 1;
+ }
+ else
+ {
+ /* Check for indirect jmp, direct and indirect calls. */
+ if (i.tm.base_opcode == 0xe8)
+ {
+ /* Direct call. */
+ *branch_p = align_branch_call;
+ if ((align_branch & align_branch_call_bit))
+ add_padding = 1;
+ }
+ else if (i.tm.base_opcode == 0xff
+ && (i.tm.extension_opcode == 2
+ || i.tm.extension_opcode == 4))
+ {
+ /* Indirect call and jmp. */
+ *branch_p = align_branch_indirect;
+ if ((align_branch & align_branch_indirect_bit))
+ add_padding = 1;
+ }
+
+ if (add_padding
+ && i.disp_operands
+ && tls_get_addr
+ && (i.op[0].disps->X_op == O_symbol
+ || (i.op[0].disps->X_op == O_subtract
+ && i.op[0].disps->X_op_symbol == GOT_symbol)))
+ {
+ symbolS *s = i.op[0].disps->X_add_symbol;
+ /* No padding to call to global or undefined tls_get_addr. */
+ if ((S_IS_EXTERNAL (s) || !S_IS_DEFINED (s))
+ && strcmp (S_GET_NAME (s), tls_get_addr) == 0)
+ return 0;
+ }
+ }
+
+ if (add_padding
+ && last_insn.kind != last_insn_other
+ && last_insn.seg == now_seg)
+ {
+ if (flag_debug)
+ as_warn_where (last_insn.file, last_insn.line,
+ _("`%s` skips -malign-branch-boundary on `%s`"),
+ last_insn.name, i.tm.name);
+ return 0;
+ }
+
+ return add_padding;
+}
+