+ 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;
+}
+
+static void
+output_insn (void)
+{
+ fragS *insn_start_frag;
+ offsetT insn_start_off;
+ fragS *fragP = NULL;
+ enum align_branch_kind branch = align_branch_none;
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (IS_ELF && x86_used_note)
+ {
+ if (i.tm.cpu_flags.bitfield.cpucmov)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_CMOV;
+ if (i.tm.cpu_flags.bitfield.cpusse)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_SSE;
+ if (i.tm.cpu_flags.bitfield.cpusse2)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_SSE2;
+ if (i.tm.cpu_flags.bitfield.cpusse3)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_SSE3;
+ if (i.tm.cpu_flags.bitfield.cpussse3)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_SSSE3;
+ if (i.tm.cpu_flags.bitfield.cpusse4_1)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_SSE4_1;
+ if (i.tm.cpu_flags.bitfield.cpusse4_2)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_SSE4_2;
+ if (i.tm.cpu_flags.bitfield.cpuavx)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX;
+ if (i.tm.cpu_flags.bitfield.cpuavx2)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX2;
+ if (i.tm.cpu_flags.bitfield.cpufma)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_FMA;
+ if (i.tm.cpu_flags.bitfield.cpuavx512f)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512F;
+ if (i.tm.cpu_flags.bitfield.cpuavx512cd)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512CD;
+ if (i.tm.cpu_flags.bitfield.cpuavx512er)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512ER;
+ if (i.tm.cpu_flags.bitfield.cpuavx512pf)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512PF;
+ if (i.tm.cpu_flags.bitfield.cpuavx512vl)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512VL;
+ if (i.tm.cpu_flags.bitfield.cpuavx512dq)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512DQ;
+ if (i.tm.cpu_flags.bitfield.cpuavx512bw)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512BW;
+ if (i.tm.cpu_flags.bitfield.cpuavx512_4fmaps)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512_4FMAPS;
+ if (i.tm.cpu_flags.bitfield.cpuavx512_4vnniw)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512_4VNNIW;
+ if (i.tm.cpu_flags.bitfield.cpuavx512_bitalg)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512_BITALG;
+ if (i.tm.cpu_flags.bitfield.cpuavx512ifma)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512_IFMA;
+ if (i.tm.cpu_flags.bitfield.cpuavx512vbmi)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512_VBMI;
+ if (i.tm.cpu_flags.bitfield.cpuavx512_vbmi2)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512_VBMI2;
+ if (i.tm.cpu_flags.bitfield.cpuavx512_vnni)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512_VNNI;
+ if (i.tm.cpu_flags.bitfield.cpuavx512_bf16)
+ x86_isa_1_used |= GNU_PROPERTY_X86_ISA_1_AVX512_BF16;
+
+ if (i.tm.cpu_flags.bitfield.cpu8087
+ || i.tm.cpu_flags.bitfield.cpu287
+ || i.tm.cpu_flags.bitfield.cpu387
+ || i.tm.cpu_flags.bitfield.cpu687
+ || i.tm.cpu_flags.bitfield.cpufisttp)
+ x86_feature_2_used |= GNU_PROPERTY_X86_FEATURE_2_X87;
+ if (i.has_regmmx
+ || i.tm.base_opcode == 0xf77 /* emms */
+ || i.tm.base_opcode == 0xf0e /* femms */)
+ x86_feature_2_used |= GNU_PROPERTY_X86_FEATURE_2_MMX;
+ if (i.has_regxmm)
+ x86_feature_2_used |= GNU_PROPERTY_X86_FEATURE_2_XMM;
+ if (i.has_regymm)
+ x86_feature_2_used |= GNU_PROPERTY_X86_FEATURE_2_YMM;
+ if (i.has_regzmm)
+ x86_feature_2_used |= GNU_PROPERTY_X86_FEATURE_2_ZMM;
+ if (i.tm.cpu_flags.bitfield.cpufxsr)
+ x86_feature_2_used |= GNU_PROPERTY_X86_FEATURE_2_FXSR;
+ if (i.tm.cpu_flags.bitfield.cpuxsave)
+ x86_feature_2_used |= GNU_PROPERTY_X86_FEATURE_2_XSAVE;
+ if (i.tm.cpu_flags.bitfield.cpuxsaveopt)
+ x86_feature_2_used |= GNU_PROPERTY_X86_FEATURE_2_XSAVEOPT;
+ if (i.tm.cpu_flags.bitfield.cpuxsavec)