x86: move ginsn stuff

Message ID be83d50e-c1dd-4973-8a54-4dbbce3ebd04@suse.com
State New
Headers
Series x86: move ginsn stuff |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Jan Beulich July 26, 2024, 1:31 p.m. UTC
  This had been badly inserted between md_assemble() and its helpers
anyway. Follow what was done for Arm64 and move the code to its own
file, #include-d as appropriate.
---
I would want to exclude all of this code when !BFD64, i.e. when 64-bit
code can't be emitted in the first place. However, the common files are
always built, even on architectures entirely unlikely to ever gain
ginsn / sframe support. I think that would need sorting first.
  

Comments

Indu Bhagat July 27, 2024, 7:35 a.m. UTC | #1
On 7/26/24 6:31 AM, Jan Beulich wrote:
> This had been badly inserted between md_assemble() and its helpers
> anyway. Follow what was done for Arm64 and move the code to its own
> file, #include-d as appropriate.

Hi Jan,

Thanks for moving the code to tc-i386-ginsn.c.  I agree its cleaner that 
way.

But moving the sframe related APIs to "tc-i386-ginsn.c" is confusing.  I 
think we should continue to keep the sframe related code in tc-i386.c.

> ---
> I would want to exclude all of this code when !BFD64, i.e. when 64-bit
> code can't be emitted in the first place. However, the common files are
> always built, even on architectures entirely unlikely to ever gain
> ginsn / sframe support. I think that would need sorting first.
> 

gas/gen-sframe.c is built but has checks inside, e.g., #ifdef 
support_sframe_p, so only the backends opting in for SFrame support have 
the functionality compiled in.

gas/sframe-opt.c does get compiled in every time, true.

Even for SCFI/ginsn generic files, there should be checks for 
TARGET_USE_GINSN or TARGET_USE_SCFI, so that backends not opting for 
these functionalities do not have stuff compiled in.

So, IIUC, checking for !BFD64 will be a finer grain check and will 
further limit some of the above code for only cases when host is 64-bit 
or when a 64-bit target is involved.  Right ?

Thanks
Indu
  
Jan Beulich July 29, 2024, 7:30 a.m. UTC | #2
On 27.07.2024 09:35, Indu Bhagat wrote:
> On 7/26/24 6:31 AM, Jan Beulich wrote:
>> This had been badly inserted between md_assemble() and its helpers
>> anyway. Follow what was done for Arm64 and move the code to its own
>> file, #include-d as appropriate.
> 
> Thanks for moving the code to tc-i386-ginsn.c.  I agree its cleaner that 
> way.
> 
> But moving the sframe related APIs to "tc-i386-ginsn.c" is confusing.  I 
> think we should continue to keep the sframe related code in tc-i386.c.

Well, I can certainly leave the sframe functions where they were. To me they
just look more connected to ginsn than anything else.

>> ---
>> I would want to exclude all of this code when !BFD64, i.e. when 64-bit
>> code can't be emitted in the first place. However, the common files are
>> always built, even on architectures entirely unlikely to ever gain
>> ginsn / sframe support. I think that would need sorting first.
>>
> 
> gas/gen-sframe.c is built but has checks inside, e.g., #ifdef 
> support_sframe_p, so only the backends opting in for SFrame support have 
> the functionality compiled in.
> 
> gas/sframe-opt.c does get compiled in every time, true.
> 
> Even for SCFI/ginsn generic files, there should be checks for 
> TARGET_USE_GINSN or TARGET_USE_SCFI, so that backends not opting for 
> these functionalities do not have stuff compiled in.
> 
> So, IIUC, checking for !BFD64 will be a finer grain check and will 
> further limit some of the above code for only cases when host is 64-bit 
> or when a 64-bit target is involved.  Right ?

Yes, that's more fine-grained and x86-only.

Jan
  
Indu Bhagat July 29, 2024, 5:39 p.m. UTC | #3
On 7/29/24 12:30 AM, Jan Beulich wrote:
> On 27.07.2024 09:35, Indu Bhagat wrote:
>> On 7/26/24 6:31 AM, Jan Beulich wrote:
>>> This had been badly inserted between md_assemble() and its helpers
>>> anyway. Follow what was done for Arm64 and move the code to its own
>>> file, #include-d as appropriate.
>>
>> Thanks for moving the code to tc-i386-ginsn.c.  I agree its cleaner that
>> way.
>>
>> But moving the sframe related APIs to "tc-i386-ginsn.c" is confusing.  I
>> think we should continue to keep the sframe related code in tc-i386.c.
> 
> Well, I can certainly leave the sframe functions where they were. To me they
> just look more connected to ginsn than anything else.
> 
>>> ---
>>> I would want to exclude all of this code when !BFD64, i.e. when 64-bit
>>> code can't be emitted in the first place. However, the common files are
>>> always built, even on architectures entirely unlikely to ever gain
>>> ginsn / sframe support. I think that would need sorting first.
>>>
>>
>> gas/gen-sframe.c is built but has checks inside, e.g., #ifdef
>> support_sframe_p, so only the backends opting in for SFrame support have
>> the functionality compiled in.
>>
>> gas/sframe-opt.c does get compiled in every time, true.
>>
>> Even for SCFI/ginsn generic files, there should be checks for
>> TARGET_USE_GINSN or TARGET_USE_SCFI, so that backends not opting for
>> these functionalities do not have stuff compiled in.
>>
>> So, IIUC, checking for !BFD64 will be a finer grain check and will
>> further limit some of the above code for only cases when host is 64-bit
>> or when a 64-bit target is involved.  Right ?
> 
> Yes, that's more fine-grained and x86-only.
> 

OK. Later if there are any more use-cases of ginsn other than SCFI for 
x86, it should easy to enable ginsn for a wider set of targets by 
rolling back these checks again.

Thanks
  

Patch

--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -650,6 +650,7 @@  unsigned int x86_sframe_cfa_sp_reg;
 /* The other CFA base register for SFrame stack trace info.  */
 unsigned int x86_sframe_cfa_fp_reg;
 
+static ginsnS *x86_ginsn_new (const symbolS *, enum ginsn_gen_mode);
 #endif
 
 /* 1 for intel syntax,
@@ -6228,1108 +6229,6 @@  static INLINE bool may_need_pass2 (const
 	       && (t->base_opcode | 8) == 0x2c);
 }
 
-#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF)
-
-/* DWARF register number for EFLAGS.  Used for pushf/popf insns.  */
-#define GINSN_DW2_REGNUM_EFLAGS     49
-/* DWARF register number for RSI.  Used as dummy value when RegIP/RegIZ.  */
-#define GINSN_DW2_REGNUM_RSI_DUMMY  4
-
-/* Identify the callee-saved registers in System V AMD64 ABI.  */
-
-bool
-x86_scfi_callee_saved_p (unsigned int dw2reg_num)
-{
-  if (dw2reg_num == 3 /* rbx.  */
-      || dw2reg_num == REG_FP /* rbp.  */
-      || dw2reg_num == REG_SP /* rsp.  */
-      || (dw2reg_num >= 12 && dw2reg_num <= 15) /* r12 - r15.  */)
-    return true;
-
-  return false;
-}
-
-/* Check whether an instruction prefix which affects operation size
-   accompanies.  For insns in the legacy space, setting REX.W takes precedence
-   over the operand-size prefix (66H) when both are used.
-
-   The current users of this API are in the handlers for PUSH, POP or other
-   instructions which affect the stack pointer implicitly:  the operation size
-   (16, 32, or 64 bits) determines the amount by which the stack pointer is
-   incremented / decremented (2, 4 or 8).  */
-
-static bool
-ginsn_opsize_prefix_p (void)
-{
-  return (!(i.prefix[REX_PREFIX] & REX_W) && i.prefix[DATA_PREFIX]);
-}
-
-/* Get the DWARF register number for the given register entry.
-   For specific byte/word/dword register accesses like al, cl, ah, ch, r8d,
-   r20w etc., we need to identify the DWARF register number for the
-   corresponding 8-byte GPR.
-
-   This function is a hack - it relies on relative ordering of reg entries in
-   the i386_regtab.  FIXME - it will be good to allow a more direct way to get
-   this information.  */
-
-static unsigned int
-ginsn_dw2_regnum (const reg_entry *ireg)
-{
-  const reg_entry *temp = ireg;
-  unsigned int dwarf_reg = Dw2Inval, idx = 0;
-
-  /* ginsn creation is available for AMD64 abi only ATM.  Other flag_code
-     are not expected.  */
-  gas_assert (ireg && flag_code == CODE_64BIT);
-
-  /* Watch out for RegIP, RegIZ.  These are expected to appear only with
-     base/index addressing modes.  Although creating inaccurate data
-     dependencies, using a dummy value (lets say volatile register rsi) will
-     not hurt SCFI.  TBD_GINSN_GEN_NOT_SCFI.  */
-  if (ireg->reg_num == RegIP || ireg->reg_num == RegIZ)
-    return GINSN_DW2_REGNUM_RSI_DUMMY;
-
-  dwarf_reg = ireg->dw2_regnum[object_64bit];
-
-  if (dwarf_reg == Dw2Inval)
-    {
-      if (ireg <= &i386_regtab[3])
-	/* For al, cl, dl, bl, bump over to axl, cxl, dxl, bxl respectively by
-	   adding 8.  */
-	temp = ireg + 8;
-      else if (ireg <= &i386_regtab[7])
-	/* For ah, ch, dh, bh, bump over to axl, cxl, dxl, bxl respectively by
-	   adding 4.  */
-	temp = ireg + 4;
-      else
-	{
-	  /* The code relies on the relative ordering of the reg entries in
-	     i386_regtab.  There are 32 register entries between axl-r31b,
-	     ax-r31w etc.  The assertions here ensures the code does not
-	     recurse indefinitely.  */
-	  gas_assert ((temp - &i386_regtab[0]) >= 0);
-	  idx = temp - &i386_regtab[0];
-	  gas_assert (idx + 32 < i386_regtab_size - 1);
-
-	  temp = temp + 32;
-	}
-
-      dwarf_reg = ginsn_dw2_regnum (temp);
-    }
-
-  /* Sanity check - failure may indicate state corruption, bad ginsn or
-     perhaps the i386-reg table and the current function got out of sync.  */
-  gas_assert (dwarf_reg < Dw2Inval);
-
-  return dwarf_reg;
-}
-
-static ginsnS *
-x86_ginsn_addsub_reg_mem (const symbolS *insn_end_sym)
-{
-  unsigned int dw2_regnum;
-  unsigned int src1_dw2_regnum;
-  ginsnS *ginsn = NULL;
-  ginsnS * (*ginsn_func) (const symbolS *, bool,
-			  enum ginsn_src_type, unsigned int, offsetT,
-			  enum ginsn_src_type, unsigned int, offsetT,
-			  enum ginsn_dst_type, unsigned int, offsetT);
-  uint16_t opcode = i.tm.base_opcode;
-
-  gas_assert (i.tm.opcode_space == SPACE_BASE
-	      && (opcode == 0x1 || opcode == 0x29));
-  ginsn_func = (opcode == 0x1) ? ginsn_new_add : ginsn_new_sub;
-
-  /* op %reg, symbol or even other cases where destination involves indirect
-     access are unnecessary for SCFI correctness.  TBD_GINSN_GEN_NOT_SCFI.  */
-  if (i.mem_operands)
-    return ginsn;
-
-  /* Skip detection of 8/16/32-bit op size; 'add/sub reg, reg/mem' ops always
-     make the dest reg untraceable for SCFI.  */
-
-  /* op reg, reg/mem.  */
-  src1_dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
-  /* Of interest only when second opnd is not memory.  */
-  if (i.reg_operands == 2)
-    {
-      dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
-      ginsn = ginsn_func (insn_end_sym, true,
-			  GINSN_SRC_REG, src1_dw2_regnum, 0,
-			  GINSN_SRC_REG, dw2_regnum, 0,
-			  GINSN_DST_REG, dw2_regnum, 0);
-      ginsn_set_where (ginsn);
-    }
-
-  return ginsn;
-}
-
-static ginsnS *
-x86_ginsn_addsub_mem_reg (const symbolS *insn_end_sym)
-{
-  unsigned int dw2_regnum;
-  unsigned int src1_dw2_regnum;
-  const reg_entry *mem_reg;
-  int32_t gdisp = 0;
-  ginsnS *ginsn = NULL;
-  ginsnS * (*ginsn_func) (const symbolS *, bool,
-			  enum ginsn_src_type, unsigned int, offsetT,
-			  enum ginsn_src_type, unsigned int, offsetT,
-			  enum ginsn_dst_type, unsigned int, offsetT);
-  uint16_t opcode = i.tm.base_opcode;
-
-  gas_assert (i.tm.opcode_space == SPACE_BASE
-	      && (opcode == 0x3 || opcode == 0x2b));
-  ginsn_func = (opcode == 0x3) ? ginsn_new_add : ginsn_new_sub;
-
-  /* op symbol, %reg.  */
-  if (i.mem_operands && !i.base_reg && !i.index_reg)
-    return ginsn;
-
-  /* Skip detection of 8/16/32-bit op size; 'add/sub reg/mem, reg' ops always
-     make the dest reg untraceable for SCFI.  */
-
-  /* op reg/mem, %reg.  */
-  dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
-
-  if (i.reg_operands == 2)
-    {
-      src1_dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
-      ginsn = ginsn_func (insn_end_sym, true,
-			  GINSN_SRC_REG, src1_dw2_regnum, 0,
-			  GINSN_SRC_REG, dw2_regnum, 0,
-			  GINSN_DST_REG, dw2_regnum, 0);
-      ginsn_set_where (ginsn);
-    }
-  else if (i.mem_operands)
-    {
-      mem_reg = (i.base_reg) ? i.base_reg : i.index_reg;
-      src1_dw2_regnum = ginsn_dw2_regnum (mem_reg);
-      if (i.disp_operands == 1)
-	gdisp = i.op[0].disps->X_add_number;
-      ginsn = ginsn_func (insn_end_sym, true,
-			  GINSN_SRC_INDIRECT, src1_dw2_regnum, gdisp,
-			  GINSN_SRC_REG, dw2_regnum, 0,
-			  GINSN_DST_REG, dw2_regnum, 0);
-      ginsn_set_where (ginsn);
-    }
-
-  return ginsn;
-}
-
-static ginsnS *
-x86_ginsn_alu_imm (const symbolS *insn_end_sym)
-{
-  offsetT src_imm;
-  unsigned int dw2_regnum;
-  ginsnS *ginsn = NULL;
-  enum ginsn_src_type src_type = GINSN_SRC_REG;
-  enum ginsn_dst_type dst_type = GINSN_DST_REG;
-
-  ginsnS * (*ginsn_func) (const symbolS *, bool,
-			  enum ginsn_src_type, unsigned int, offsetT,
-			  enum ginsn_src_type, unsigned int, offsetT,
-			  enum ginsn_dst_type, unsigned int, offsetT);
-
-  /* FIXME - create ginsn where dest is REG_SP / REG_FP only ? */
-  /* Map for insn.tm.extension_opcode
-     000 ADD    100 AND
-     001 OR     101 SUB
-     010 ADC    110 XOR
-     011 SBB    111 CMP  */
-
-  /* add/sub/and imm, %reg only at this time for SCFI.
-     Although all three ('and', 'or' , 'xor') make the destination reg
-     untraceable, 'and' op is handled but not 'or' / 'xor' because we will look
-     into supporting the DRAP pattern at some point.  Other opcodes ('adc',
-     'sbb' and 'cmp') are not generated here either.  The ginsn representation
-     does not have support for the latter three opcodes;  GINSN_TYPE_OTHER may
-     be added for these after x86_ginsn_unhandled () invocation if the
-     destination register is REG_SP or REG_FP.  */
-  if (i.tm.extension_opcode == 5)
-    ginsn_func = ginsn_new_sub;
-  else if (i.tm.extension_opcode == 4)
-    ginsn_func = ginsn_new_and;
-  else if (i.tm.extension_opcode == 0)
-    ginsn_func = ginsn_new_add;
-  else
-    return ginsn;
-
-  /* TBD_GINSN_REPRESENTATION_LIMIT: There is no representation for when a
-     symbol is used as an operand, like so:
-	  addq    $simd_cmp_op+8, %rdx
-     Skip generating any ginsn for this.  */
-  if (i.imm_operands == 1
-      && i.op[0].imms->X_op != O_constant)
-    return ginsn;
-
-  /* addq    $1, symbol
-     addq    $1, -16(%rbp)
-     These are not of interest for SCFI.  Also, TBD_GINSN_GEN_NOT_SCFI.  */
-  if (i.mem_operands == 1)
-    return ginsn;
-
-  /* 8/16/32-bit op size makes the destination reg untraceable for SCFI.
-     Deal with this via the x86_ginsn_unhandled () code path.  */
-  if (i.suffix != QWORD_MNEM_SUFFIX)
-    return ginsn;
-
-  gas_assert (i.imm_operands == 1);
-  src_imm = i.op[0].imms->X_add_number;
-  /* The second operand may be a register or indirect access.  For SCFI, only
-     the case when the second opnd is a register is interesting.  Revisit this
-     if generating ginsns for a different gen mode TBD_GINSN_GEN_NOT_SCFI.  */
-  if (i.reg_operands == 1)
-    {
-      dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
-      /* For ginsn, keep the imm as second src operand.  */
-      ginsn = ginsn_func (insn_end_sym, true,
-			  src_type, dw2_regnum, 0,
-			  GINSN_SRC_IMM, 0, src_imm,
-			  dst_type, dw2_regnum, 0);
-
-      ginsn_set_where (ginsn);
-    }
-
-  return ginsn;
-}
-
-/* Create ginsn(s) for MOV operations.
-
-   The generated ginsns corresponding to mov with indirect access to memory
-   (src or dest) suffer with loss of information: when both index and base
-   registers are at play, only base register gets conveyed in ginsn.  Note
-   this TBD_GINSN_GEN_NOT_SCFI.  */
-
-static ginsnS *
-x86_ginsn_move (const symbolS *insn_end_sym)
-{
-  ginsnS *ginsn = NULL;
-  unsigned int dst_reg;
-  unsigned int src_reg;
-  offsetT src_disp = 0;
-  offsetT dst_disp = 0;
-  const reg_entry *dst = NULL;
-  const reg_entry *src = NULL;
-  uint16_t opcode = i.tm.base_opcode;
-  enum ginsn_src_type src_type = GINSN_SRC_REG;
-  enum ginsn_dst_type dst_type = GINSN_DST_REG;
-
-  /* mov %reg, symbol or mov symbol, %reg.
-     Not of interest for SCFI.  Also, TBD_GINSN_GEN_NOT_SCFI.  */
-  if (i.mem_operands == 1 && !i.base_reg && !i.index_reg)
-    return ginsn;
-
-  /* 8/16/32-bit op size makes the destination reg untraceable for SCFI.
-     Handle mov reg, reg only.  mov to or from a memory operand will make
-     dest reg, when present, untraceable, irrespective of the op size.  */
-  if (i.reg_operands == 2 && i.suffix != QWORD_MNEM_SUFFIX)
-    return ginsn;
-
-  gas_assert (i.tm.opcode_space == SPACE_BASE);
-  if (opcode == 0x8b || opcode == 0x8a)
-    {
-      /* mov  disp(%reg), %reg.  */
-      if (i.mem_operands)
-	{
-	  src = (i.base_reg) ? i.base_reg : i.index_reg;
-	  if (i.disp_operands == 1)
-	    src_disp = i.op[0].disps->X_add_number;
-	  src_type = GINSN_SRC_INDIRECT;
-	}
-      else
-	src = i.op[0].regs;
-
-      dst = i.op[1].regs;
-    }
-  else if (opcode == 0x89 || opcode == 0x88)
-    {
-      /* mov %reg, disp(%reg).  */
-      src = i.op[0].regs;
-      if (i.mem_operands)
-	{
-	  dst = (i.base_reg) ? i.base_reg : i.index_reg;
-	  if (i.disp_operands == 1)
-	    dst_disp = i.op[1].disps->X_add_number;
-	  dst_type = GINSN_DST_INDIRECT;
-	}
-      else
-	dst = i.op[1].regs;
-    }
-
-  src_reg = ginsn_dw2_regnum (src);
-  dst_reg = ginsn_dw2_regnum (dst);
-
-  ginsn = ginsn_new_mov (insn_end_sym, true,
-			 src_type, src_reg, src_disp,
-			 dst_type, dst_reg, dst_disp);
-  ginsn_set_where (ginsn);
-
-  return ginsn;
-}
-
-/* Generate appropriate ginsn for lea.
-
-   Unhandled sub-cases (marked with TBD_GINSN_GEN_NOT_SCFI) also suffer with
-   some loss of information in the final ginsn chosen eventually (type
-   GINSN_TYPE_OTHER).  But this is fine for now for GINSN_GEN_SCFI generation
-   mode.  */
-
-static ginsnS *
-x86_ginsn_lea (const symbolS *insn_end_sym)
-{
-  offsetT src_disp = 0;
-  ginsnS *ginsn = NULL;
-  unsigned int src1_reg;
-  const reg_entry *src1;
-  offsetT index_scale;
-  unsigned int dst_reg;
-  bool index_regiz_p;
-
-  if ((!i.base_reg) != (!i.index_reg || i.index_reg->reg_num == RegIZ))
-    {
-      /* lea disp(%base), %dst    or    lea disp(,%index,imm), %dst.
-	 Either index_reg or base_reg exists, but not both.  Further, as per
-	 above, the case when just %index exists but is equal to RegIZ is
-	 excluded.  If not excluded, a GINSN_TYPE_MOV of %rsi
-	 (GINSN_DW2_REGNUM_RSI_DUMMY) to %dst will be generated by this block.
-	 Such a mov ginsn is imprecise; so, exclude now and generate
-	 GINSN_TYPE_OTHER instead later via the x86_ginsn_unhandled ().
-	 Excluding other cases is required due to
-	 TBD_GINSN_REPRESENTATION_LIMIT.  */
-
-      index_scale = i.log2_scale_factor;
-      index_regiz_p = i.index_reg && i.index_reg->reg_num == RegIZ;
-      src1 = i.base_reg ? i.base_reg : i.index_reg;
-      src1_reg = ginsn_dw2_regnum (src1);
-      dst_reg = ginsn_dw2_regnum (i.op[1].regs);
-      /* It makes sense to represent a scale factor of 1 precisely here
-	 (i.e., not using GINSN_TYPE_OTHER, but rather similar to the
-	 base-without-index case).  A non-zero scale factor is still OK if
-	 the index reg is zero reg.
-	 However, skip from here the case when disp has a symbol instead.
-	 TBD_GINSN_REPRESENTATION_LIMIT.  */
-      if ((!index_scale || index_regiz_p)
-	  && (!i.disp_operands || i.op[0].disps->X_op == O_constant))
-	{
-	  if (i.disp_operands)
-	    src_disp = i.op[0].disps->X_add_number;
-
-	  if (src_disp)
-	    /* Generate an ADD ginsn.  */
-	    ginsn = ginsn_new_add (insn_end_sym, true,
-				   GINSN_SRC_REG, src1_reg, 0,
-				   GINSN_SRC_IMM, 0, src_disp,
-				   GINSN_DST_REG, dst_reg, 0);
-	  else
-	    /* Generate a MOV ginsn.  */
-	    ginsn = ginsn_new_mov (insn_end_sym, true,
-				   GINSN_SRC_REG, src1_reg, 0,
-				   GINSN_DST_REG, dst_reg, 0);
-
-	  ginsn_set_where (ginsn);
-	}
-    }
-  /* Skip handling other cases here,
-     - when (i.index_reg && i.base_reg) is true,
-       e.g., lea disp(%base,%index,imm), %dst
-       We do not have a ginsn representation for multiply.
-     - or, when (!i.index_reg && !i.base_reg) is true,
-       e.g., lea symbol, %dst
-       Not a frequent pattern.  If %dst is a register of interest, the user is
-       likely to use a MOV op anyway.
-     Deal with these via the x86_ginsn_unhandled () code path to generate
-     GINSN_TYPE_OTHER when necessary.  TBD_GINSN_GEN_NOT_SCFI.  */
-
-  return ginsn;
-}
-
-static ginsnS *
-x86_ginsn_jump (const symbolS *insn_end_sym, bool cond_p)
-{
-  ginsnS *ginsn = NULL;
-  const symbolS *src_symbol;
-  ginsnS * (*ginsn_func) (const symbolS *sym, bool real_p,
-			  enum ginsn_src_type src_type, unsigned int src_reg,
-			  const symbolS *src_ginsn_sym);
-
-  gas_assert (i.disp_operands == 1);
-
-  ginsn_func = cond_p ? ginsn_new_jump_cond : ginsn_new_jump;
-  if (i.op[0].disps->X_op == O_symbol && !i.op[0].disps->X_add_number)
-    {
-      src_symbol = i.op[0].disps->X_add_symbol;
-      ginsn = ginsn_func (insn_end_sym, true,
-			  GINSN_SRC_SYMBOL, 0, src_symbol);
-
-      ginsn_set_where (ginsn);
-    }
-  else
-    {
-      /* A non-zero addend in jump/JCC target makes control-flow tracking
-	 difficult.  Skip SCFI for now.  */
-      as_bad (_("SCFI: `%s' insn with non-zero addend to sym not supported"),
-	      cond_p ? "JCC" : "jmp");
-      return ginsn;
-    }
-
-  return ginsn;
-}
-
-static ginsnS *
-x86_ginsn_enter (const symbolS *insn_end_sym)
-{
-  ginsnS *ginsn = NULL;
-  ginsnS *ginsn_next = NULL;
-  ginsnS *ginsn_last = NULL;
-  /* In 64-bit mode, the default stack update size is 8 bytes.  */
-  int stack_opnd_size = 8;
-
-  gas_assert (i.imm_operands == 2);
-
-  /* For non-zero size operands, bail out as untraceable for SCFI.  */
-  if (i.op[0].imms->X_op != O_constant || i.op[0].imms->X_add_symbol != 0
-      || i.op[1].imms->X_op != O_constant || i.op[1].imms->X_add_symbol != 0)
-    {
-      as_bad ("SCFI: enter insn with non-zero operand not supported");
-      return ginsn;
-    }
-
-  /* Check if this is a 16-bit op.  */
-  if (ginsn_opsize_prefix_p ())
-    stack_opnd_size = 2;
-
-  /* If the nesting level is 0, the processor pushes the frame pointer from
-     the BP/EBP/RBP register onto the stack, copies the current stack
-     pointer from the SP/ESP/RSP register into the BP/EBP/RBP register, and
-     loads the SP/ESP/RSP register with the current stack-pointer value
-     minus the value in the size operand.  */
-  ginsn = ginsn_new_sub (insn_end_sym, false,
-			 GINSN_SRC_REG, REG_SP, 0,
-			 GINSN_SRC_IMM, 0, stack_opnd_size,
-			 GINSN_DST_REG, REG_SP, 0);
-  ginsn_set_where (ginsn);
-  ginsn_next = ginsn_new_store (insn_end_sym, false,
-				GINSN_SRC_REG, REG_FP,
-				GINSN_DST_INDIRECT, REG_SP, 0);
-  ginsn_set_where (ginsn_next);
-  gas_assert (!ginsn_link_next (ginsn, ginsn_next));
-  ginsn_last = ginsn_new_mov (insn_end_sym, false,
-			      GINSN_SRC_REG, REG_SP, 0,
-			      GINSN_DST_REG, REG_FP, 0);
-  ginsn_set_where (ginsn_last);
-  gas_assert (!ginsn_link_next (ginsn_next, ginsn_last));
-
-  return ginsn;
-}
-
-static ginsnS *
-x86_ginsn_leave (const symbolS *insn_end_sym)
-{
-  ginsnS *ginsn = NULL;
-  ginsnS *ginsn_next = NULL;
-  ginsnS *ginsn_last = NULL;
-  /* In 64-bit mode, the default stack update size is 8 bytes.  */
-  int stack_opnd_size = 8;
-
-  /* Check if this is a 16-bit op.  */
-  if (ginsn_opsize_prefix_p ())
-    stack_opnd_size = 2;
-
-  /* The 'leave' instruction copies the contents of the RBP register
-     into the RSP register to release all stack space allocated to the
-     procedure.  */
-  ginsn = ginsn_new_mov (insn_end_sym, false,
-			 GINSN_SRC_REG, REG_FP, 0,
-			 GINSN_DST_REG, REG_SP, 0);
-  ginsn_set_where (ginsn);
-  /* Then it restores the old value of the RBP register from the stack.  */
-  ginsn_next = ginsn_new_load (insn_end_sym, false,
-			       GINSN_SRC_INDIRECT, REG_SP, 0,
-			       GINSN_DST_REG, REG_FP);
-  ginsn_set_where (ginsn_next);
-  gas_assert (!ginsn_link_next (ginsn, ginsn_next));
-  ginsn_last = ginsn_new_add (insn_end_sym, false,
-			      GINSN_SRC_REG, REG_SP, 0,
-			      GINSN_SRC_IMM, 0, stack_opnd_size,
-			      GINSN_DST_REG, REG_SP, 0);
-  ginsn_set_where (ginsn_next);
-  gas_assert (!ginsn_link_next (ginsn_next, ginsn_last));
-
-  return ginsn;
-}
-
-/* Check if an instruction is whitelisted.
-
-   Some instructions may appear with REG_SP or REG_FP as destination, because
-   which they are deemed 'interesting' for SCFI.  Whitelist them here if they
-   do not affect SCFI correctness.  */
-
-static bool
-x86_ginsn_safe_to_skip_p (void)
-{
-  bool skip_p = false;
-  uint16_t opcode = i.tm.base_opcode;
-
-  switch (opcode)
-    {
-    case 0x80:
-    case 0x81:
-    case 0x83:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* cmp imm, reg/rem.  */
-      if (i.tm.extension_opcode == 7)
-	skip_p = true;
-      break;
-
-    case 0x38:
-    case 0x39:
-    case 0x3a:
-    case 0x3b:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* cmp imm/reg/mem, reg/rem.  */
-      skip_p = true;
-      break;
-
-    case 0xf6:
-    case 0xf7:
-    case 0x84:
-    case 0x85:
-      /* test imm/reg/mem, reg/mem.  */
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      skip_p = true;
-      break;
-
-    default:
-      break;
-    }
-
-  return skip_p;
-}
-
-#define X86_GINSN_UNHANDLED_NONE        0
-#define X86_GINSN_UNHANDLED_DEST_REG    1
-#define X86_GINSN_UNHANDLED_CFG         2
-#define X86_GINSN_UNHANDLED_STACKOP     3
-#define X86_GINSN_UNHANDLED_UNEXPECTED  4
-
-/* Check the input insn for its impact on the correctness of the synthesized
-   CFI.  Returns an error code to the caller.  */
-
-static int
-x86_ginsn_unhandled (void)
-{
-  int err = X86_GINSN_UNHANDLED_NONE;
-  const reg_entry *reg_op;
-  unsigned int dw2_regnum;
-
-  /* Keep an eye out for instructions affecting control flow.  */
-  if (i.tm.opcode_modifier.jump)
-    err = X86_GINSN_UNHANDLED_CFG;
-  /* Also, for any instructions involving an implicit update to the stack
-     pointer.  */
-  else if (i.tm.opcode_modifier.operandconstraint == IMPLICIT_STACK_OP)
-    err = X86_GINSN_UNHANDLED_STACKOP;
-  /* Finally, also check if the missed instructions are affecting REG_SP or
-     REG_FP.  The destination operand is the last at all stages of assembly
-     (due to following AT&T syntax layout in the internal representation).  In
-     case of Intel syntax input, this still remains true as swap_operands ()
-     is done by now.
-     PS: These checks do not involve index / base reg, as indirect memory
-     accesses via REG_SP or REG_FP do not affect SCFI correctness.
-     (Also note these instructions are candidates for other ginsn generation
-     modes in future.  TBD_GINSN_GEN_NOT_SCFI.)  */
-  else if (i.operands && i.reg_operands
-	   && !(i.flags[i.operands - 1] & Operand_Mem))
-    {
-      reg_op = i.op[i.operands - 1].regs;
-      if (reg_op)
-	{
-	  dw2_regnum = ginsn_dw2_regnum (reg_op);
-	  if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
-	    err = X86_GINSN_UNHANDLED_DEST_REG;
-	}
-      else
-	/* Something unexpected.  Indicate to caller.  */
-	err = X86_GINSN_UNHANDLED_UNEXPECTED;
-    }
-
-  return err;
-}
-
-/* Generate one or more generic GAS instructions, a.k.a, ginsns for the current
-   machine instruction.
-
-   Returns the head of linked list of ginsn(s) added, if success; Returns NULL
-   if failure.
-
-   The input ginsn_gen_mode GMODE determines the set of minimal necessary
-   ginsns necessary for correctness of any passes applicable for that mode.
-   For supporting the GINSN_GEN_SCFI generation mode, following is the list of
-   machine instructions that must be translated into the corresponding ginsns
-   to ensure correctness of SCFI:
-     - All instructions affecting the two registers that could potentially
-       be used as the base register for CFA tracking.  For SCFI, the base
-       register for CFA tracking is limited to REG_SP and REG_FP only for
-       now.
-     - All change of flow instructions: conditional and unconditional branches,
-       call and return from functions.
-     - All instructions that can potentially be a register save / restore
-       operation.
-     - All instructions that perform stack manipulation implicitly: the CALL,
-       RET, PUSH, POP, ENTER, and LEAVE instructions.
-
-   The function currently supports GINSN_GEN_SCFI ginsn generation mode only.
-   To support other generation modes will require work on this target-specific
-   process of creation of ginsns:
-     - Some of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
-       possible starting points.
-     - Also note that ginsn representation may need enhancements.  Specifically,
-       note some TBD_GINSN_INFO_LOSS and TBD_GINSN_REPRESENTATION_LIMIT markers.
-   */
-
-static ginsnS *
-x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
-{
-  int err = 0;
-  uint16_t opcode;
-  unsigned int dw2_regnum;
-  const reg_entry *mem_reg;
-  ginsnS *ginsn = NULL;
-  ginsnS *ginsn_next = NULL;
-  /* In 64-bit mode, the default stack update size is 8 bytes.  */
-  int stack_opnd_size = 8;
-
-  /* Currently supports generation of selected ginsns, sufficient for
-     the use-case of SCFI only.  */
-  if (gmode != GINSN_GEN_SCFI)
-    return ginsn;
-
-  opcode = i.tm.base_opcode;
-
-  /* Until it is clear how to handle APX NDD and other new opcodes, disallow
-     them from SCFI.  */
-  if (is_apx_rex2_encoding ()
-      || (i.tm.opcode_modifier.evex && is_apx_evex_encoding ()))
-    {
-      as_bad (_("SCFI: unsupported APX op %#x may cause incorrect CFI"),
-	      opcode);
-      return ginsn;
-    }
-
-  switch (opcode)
-    {
-
-    /* Add opcodes 0x0/0x2 and sub opcodes 0x28/0x2a (with opcode_space
-       SPACE_BASE) are 8-bit ops.  While they are relevant for SCFI
-       correctness,  skip handling them here and use the x86_ginsn_unhandled
-       code path to generate GINSN_TYPE_OTHER when necessary.  */
-
-    case 0x1:  /* add reg, reg/mem.  */
-    case 0x29: /* sub reg, reg/mem.  */
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      ginsn = x86_ginsn_addsub_reg_mem (insn_end_sym);
-      break;
-
-    case 0x3:  /* add reg/mem, reg.  */
-    case 0x2b: /* sub reg/mem, reg.  */
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      ginsn = x86_ginsn_addsub_mem_reg (insn_end_sym);
-      break;
-
-    case 0xa0: /* push fs.  */
-    case 0xa8: /* push gs.  */
-      /* push fs / push gs have opcode_space == SPACE_0F.  */
-      if (i.tm.opcode_space != SPACE_0F)
-	break;
-      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
-      /* Check if operation size is 16-bit.  */
-      if (ginsn_opsize_prefix_p ())
-	stack_opnd_size = 2;
-      ginsn = ginsn_new_sub (insn_end_sym, false,
-			     GINSN_SRC_REG, REG_SP, 0,
-			     GINSN_SRC_IMM, 0, stack_opnd_size,
-			     GINSN_DST_REG, REG_SP, 0);
-      ginsn_set_where (ginsn);
-      ginsn_next = ginsn_new_store (insn_end_sym, false,
-				    GINSN_SRC_REG, dw2_regnum,
-				    GINSN_DST_INDIRECT, REG_SP, 0);
-      ginsn_set_where (ginsn_next);
-      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
-      break;
-
-    case 0xa1: /* pop fs.  */
-    case 0xa9: /* pop gs.  */
-      /* pop fs / pop gs have opcode_space == SPACE_0F.  */
-      if (i.tm.opcode_space != SPACE_0F)
-	break;
-      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
-      /* Check if operation size is 16-bit.  */
-      if (ginsn_opsize_prefix_p ())
-	stack_opnd_size = 2;
-      ginsn = ginsn_new_load (insn_end_sym, false,
-			      GINSN_SRC_INDIRECT, REG_SP, 0,
-			      GINSN_DST_REG, dw2_regnum);
-      ginsn_set_where (ginsn);
-      ginsn_next = ginsn_new_add (insn_end_sym, false,
-				  GINSN_SRC_REG, REG_SP, 0,
-				  GINSN_SRC_IMM, 0, stack_opnd_size,
-				  GINSN_DST_REG, REG_SP, 0);
-      ginsn_set_where (ginsn_next);
-      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
-      break;
-
-    case 0x50 ... 0x57:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* push reg.  */
-      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
-      /* Check if operation size is 16-bit.  */
-      if (ginsn_opsize_prefix_p ())
-	stack_opnd_size = 2;
-      ginsn = ginsn_new_sub (insn_end_sym, false,
-			     GINSN_SRC_REG, REG_SP, 0,
-			     GINSN_SRC_IMM, 0, stack_opnd_size,
-			     GINSN_DST_REG, REG_SP, 0);
-      ginsn_set_where (ginsn);
-      ginsn_next = ginsn_new_store (insn_end_sym, false,
-				    GINSN_SRC_REG, dw2_regnum,
-				    GINSN_DST_INDIRECT, REG_SP, 0);
-      ginsn_set_where (ginsn_next);
-      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
-      break;
-
-    case 0x58 ... 0x5f:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* pop reg.  */
-      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
-      ginsn = ginsn_new_load (insn_end_sym, false,
-			      GINSN_SRC_INDIRECT, REG_SP, 0,
-			      GINSN_DST_REG, dw2_regnum);
-      ginsn_set_where (ginsn);
-      /* Check if operation size is 16-bit.  */
-      if (ginsn_opsize_prefix_p ())
-	stack_opnd_size = 2;
-      ginsn_next = ginsn_new_add (insn_end_sym, false,
-				  GINSN_SRC_REG, REG_SP, 0,
-				  GINSN_SRC_IMM, 0, stack_opnd_size,
-				  GINSN_DST_REG, REG_SP, 0);
-      ginsn_set_where (ginsn_next);
-      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
-      break;
-
-    case 0x6a: /* push imm8.  */
-    case 0x68: /* push imm16/imm32.  */
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* Check if operation size is 16-bit.  */
-      if (ginsn_opsize_prefix_p ())
-	stack_opnd_size = 2;
-      /* Skip getting the value of imm from machine instruction
-	 because this is not important for SCFI.  */
-      ginsn = ginsn_new_sub (insn_end_sym, false,
-			     GINSN_SRC_REG, REG_SP, 0,
-			     GINSN_SRC_IMM, 0, stack_opnd_size,
-			     GINSN_DST_REG, REG_SP, 0);
-      ginsn_set_where (ginsn);
-      ginsn_next = ginsn_new_store (insn_end_sym, false,
-				    GINSN_SRC_IMM, 0,
-				    GINSN_DST_INDIRECT, REG_SP, 0);
-      ginsn_set_where (ginsn_next);
-      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
-      break;
-
-    /* PS: Opcodes 0x80 ... 0x8f with opcode_space SPACE_0F are present
-       only after relaxation.  They do not need to be handled for ginsn
-       creation.  */
-    case 0x70 ... 0x7f:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      ginsn = x86_ginsn_jump (insn_end_sym, true);
-      break;
-
-    case 0x80:
-    case 0x81:
-    case 0x83:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      ginsn = x86_ginsn_alu_imm (insn_end_sym);
-      break;
-
-    case 0x8a: /* mov r/m8, r8.  */
-    case 0x8b: /* mov r/m(16/32/64), r(16/32/64).  */
-    case 0x88: /* mov r8, r/m8.  */
-    case 0x89: /* mov r(16/32/64), r/m(16/32/64).  */
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      ginsn = x86_ginsn_move (insn_end_sym);
-      break;
-
-    case 0x8d:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* lea disp(%base,%index,imm), %dst.  */
-      ginsn = x86_ginsn_lea (insn_end_sym);
-      break;
-
-    case 0x8f:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* pop to reg/mem.  */
-      if (i.mem_operands)
-	{
-	  mem_reg = (i.base_reg) ? i.base_reg : i.index_reg;
-	  /* Use dummy register if no base or index.  Unlike other opcodes,
-	     ginsns must be generated as this affect stack pointer.  */
-	  dw2_regnum = (mem_reg
-			? ginsn_dw2_regnum (mem_reg)
-			: GINSN_DW2_REGNUM_RSI_DUMMY);
-	}
-      else
-	dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
-      ginsn = ginsn_new_load (insn_end_sym, false,
-			      GINSN_SRC_INDIRECT, REG_SP, 0,
-			      GINSN_DST_INDIRECT, dw2_regnum);
-      ginsn_set_where (ginsn);
-      /* Check if operation size is 16-bit.  */
-      if (ginsn_opsize_prefix_p ())
-	stack_opnd_size = 2;
-      ginsn_next = ginsn_new_add (insn_end_sym, false,
-				  GINSN_SRC_REG, REG_SP, 0,
-				  GINSN_SRC_IMM, 0, stack_opnd_size,
-				  GINSN_DST_REG, REG_SP, 0);
-      ginsn_set_where (ginsn_next);
-      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
-      break;
-
-    case 0x9c:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* pushf / pushfq.  */
-      /* Check if operation size is 16-bit.  */
-      if (ginsn_opsize_prefix_p ())
-	stack_opnd_size = 2;
-      ginsn = ginsn_new_sub (insn_end_sym, false,
-			     GINSN_SRC_REG, REG_SP, 0,
-			     GINSN_SRC_IMM, 0, stack_opnd_size,
-			     GINSN_DST_REG, REG_SP, 0);
-      ginsn_set_where (ginsn);
-      /* FIXME - hardcode the actual DWARF reg number value.  As for SCFI
-	 correctness, although this behaves simply a placeholder value; its
-	 just clearer if the value is correct.  */
-      dw2_regnum = GINSN_DW2_REGNUM_EFLAGS;
-      ginsn_next = ginsn_new_store (insn_end_sym, false,
-				    GINSN_SRC_REG, dw2_regnum,
-				    GINSN_DST_INDIRECT, REG_SP, 0);
-      ginsn_set_where (ginsn_next);
-      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
-      break;
-
-    case 0x9d:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* popf / popfq.  */
-      /* Check if operation size is 16-bit.  */
-      if (ginsn_opsize_prefix_p ())
-	stack_opnd_size = 2;
-      /* FIXME - hardcode the actual DWARF reg number value.  As for SCFI
-	 correctness, although this behaves simply a placeholder value; its
-	 just clearer if the value is correct.  */
-      dw2_regnum = GINSN_DW2_REGNUM_EFLAGS;
-      ginsn = ginsn_new_load (insn_end_sym, false,
-			      GINSN_SRC_INDIRECT, REG_SP, 0,
-			      GINSN_DST_REG, dw2_regnum);
-      ginsn_set_where (ginsn);
-      ginsn_next = ginsn_new_add (insn_end_sym, false,
-				  GINSN_SRC_REG, REG_SP, 0,
-				  GINSN_SRC_IMM, 0, stack_opnd_size,
-				  GINSN_DST_REG, REG_SP, 0);
-      ginsn_set_where (ginsn_next);
-      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
-      break;
-
-    case 0xff:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* push from reg/mem.  */
-      if (i.tm.extension_opcode == 6)
-	{
-	  /* Check if operation size is 16-bit.  */
-	  if (ginsn_opsize_prefix_p ())
-	    stack_opnd_size = 2;
-	  ginsn = ginsn_new_sub (insn_end_sym, false,
-				 GINSN_SRC_REG, REG_SP, 0,
-				 GINSN_SRC_IMM, 0, stack_opnd_size,
-				 GINSN_DST_REG, REG_SP, 0);
-	  ginsn_set_where (ginsn);
-	  if (i.mem_operands)
-	    {
-	      mem_reg = (i.base_reg) ? i.base_reg : i.index_reg;
-	      /* Use dummy register if no base or index.  Unlike other opcodes,
-		 ginsns must be generated as this affect stack pointer.  */
-	      dw2_regnum = (mem_reg
-			    ? ginsn_dw2_regnum (mem_reg)
-			    : GINSN_DW2_REGNUM_RSI_DUMMY);
-	    }
-	  else
-	    dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
-	  ginsn_next = ginsn_new_store (insn_end_sym, false,
-					GINSN_SRC_INDIRECT, dw2_regnum,
-					GINSN_DST_INDIRECT, REG_SP, 0);
-	  ginsn_set_where (ginsn_next);
-	  gas_assert (!ginsn_link_next (ginsn, ginsn_next));
-	}
-      else if (i.tm.extension_opcode == 4)
-	{
-	  /* jmp r/m.  E.g., notrack jmp *%rax.  */
-	  if (i.reg_operands)
-	    {
-	      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
-	      ginsn = ginsn_new_jump (insn_end_sym, true,
-				      GINSN_SRC_REG, dw2_regnum, NULL);
-	      ginsn_set_where (ginsn);
-	    }
-	  else if (i.mem_operands && i.index_reg)
-	    {
-	      /* jmp    *0x0(,%rax,8).  */
-	      dw2_regnum = ginsn_dw2_regnum (i.index_reg);
-	      ginsn = ginsn_new_jump (insn_end_sym, true,
-				      GINSN_SRC_REG, dw2_regnum, NULL);
-	      ginsn_set_where (ginsn);
-	    }
-	  else if (i.mem_operands && i.base_reg)
-	    {
-	      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
-	      ginsn = ginsn_new_jump (insn_end_sym, true,
-				      GINSN_SRC_REG, dw2_regnum, NULL);
-	      ginsn_set_where (ginsn);
-	    }
-	}
-      else if (i.tm.extension_opcode == 2)
-	{
-	  /* 0xFF /2 (call).  */
-	  if (i.reg_operands)
-	    {
-	      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
-	      ginsn = ginsn_new_call (insn_end_sym, true,
-				      GINSN_SRC_REG, dw2_regnum, NULL);
-	      ginsn_set_where (ginsn);
-	    }
-	  else if (i.mem_operands && i.base_reg)
-	    {
-	      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
-	      ginsn = ginsn_new_call (insn_end_sym, true,
-				      GINSN_SRC_REG, dw2_regnum, NULL);
-	      ginsn_set_where (ginsn);
-	    }
-	}
-      break;
-
-    case 0xc2: /* ret imm16.  */
-    case 0xc3: /* ret.  */
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* Near ret.  */
-      ginsn = ginsn_new_return (insn_end_sym, true);
-      ginsn_set_where (ginsn);
-      break;
-
-    case 0xc8:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* enter.  */
-      ginsn = x86_ginsn_enter (insn_end_sym);
-      break;
-
-    case 0xc9:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* leave.  */
-      ginsn = x86_ginsn_leave (insn_end_sym);
-      break;
-
-    case 0xe0 ... 0xe2: /* loop / loope / loopne.  */
-    case 0xe3:          /* jecxz / jrcxz.  */
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      ginsn = x86_ginsn_jump (insn_end_sym, true);
-      ginsn_set_where (ginsn);
-      break;
-
-    case 0xe8:
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* PS: SCFI machinery does not care about which func is being
-	 called.  OK to skip that info.  */
-      ginsn = ginsn_new_call (insn_end_sym, true,
-			      GINSN_SRC_SYMBOL, 0, NULL);
-      ginsn_set_where (ginsn);
-      break;
-
-    /* PS: opcode 0xe9 appears only after relaxation.  Skip here.  */
-    case 0xeb:
-      /* If opcode_space != SPACE_BASE, this is not a jmp insn.  Skip it
-	 for GINSN_GEN_SCFI.  */
-      if (i.tm.opcode_space != SPACE_BASE)
-	break;
-      /* Unconditional jmp.  */
-      ginsn = x86_ginsn_jump (insn_end_sym, false);
-      ginsn_set_where (ginsn);
-      break;
-
-    default:
-      /* TBD_GINSN_GEN_NOT_SCFI: Skip all other opcodes uninteresting for
-	 GINSN_GEN_SCFI mode.  */
-      break;
-    }
-
-  if (!ginsn && !x86_ginsn_safe_to_skip_p ())
-    {
-      /* For all unhandled insns that are not whitelisted, check that they do
-	 not impact SCFI correctness.  */
-      err = x86_ginsn_unhandled ();
-      switch (err)
-	{
-	case X86_GINSN_UNHANDLED_NONE:
-	  break;
-	case X86_GINSN_UNHANDLED_DEST_REG:
-	  /* Not all writes to REG_FP are harmful in context of SCFI.  Simply
-	     generate a GINSN_TYPE_OTHER with destination set to the
-	     appropriate register.  The SCFI machinery will bail out if this
-	     ginsn affects SCFI correctness.  */
-	  dw2_regnum = ginsn_dw2_regnum (i.op[i.operands - 1].regs);
-	  ginsn = ginsn_new_other (insn_end_sym, true,
-				   GINSN_SRC_IMM, 0,
-				   GINSN_SRC_IMM, 0,
-				   GINSN_DST_REG, dw2_regnum);
-	  ginsn_set_where (ginsn);
-	  break;
-	case X86_GINSN_UNHANDLED_CFG:
-	case X86_GINSN_UNHANDLED_STACKOP:
-	  as_bad (_("SCFI: unhandled op %#x may cause incorrect CFI"), opcode);
-	  break;
-	case X86_GINSN_UNHANDLED_UNEXPECTED:
-	  as_bad (_("SCFI: unexpected op %#x may cause incorrect CFI"),
-		  opcode);
-	  break;
-	default:
-	  abort ();
-	  break;
-	}
-    }
-
-  return ginsn;
-}
-
-#endif
-
 /* This is the guts of the machine-dependent assembler.  LINE points to a
    machine dependent instruction.  This function is supposed to emit
    the frags/bytes it assembles to.  */
@@ -12213,48 +11112,7 @@  x86_cleanup (void)
     subseg_set (seg, subseg);
 }
 
-/* Whether SFrame stack trace info is supported.  */
-bool
-x86_support_sframe_p (void)
-{
-  /* At this time, SFrame stack trace is supported for AMD64 ABI only.  */
-  return (x86_elf_abi == X86_64_ABI);
-}
-
-/* Whether SFrame return address tracking is needed.  */
-bool
-x86_sframe_ra_tracking_p (void)
-{
-  /* In AMD64, return address is always stored on the stack at a fixed offset
-     from the CFA (provided via x86_sframe_cfa_ra_offset ()).
-     Do not track explicitly via an SFrame Frame Row Entry.  */
-  return false;
-}
-
-/* The fixed offset from CFA for SFrame to recover the return address.
-   (useful only when SFrame RA tracking is not needed).  */
-offsetT
-x86_sframe_cfa_ra_offset (void)
-{
-  gas_assert (x86_elf_abi == X86_64_ABI);
-  return (offsetT) -8;
-}
-
-/* The abi/arch indentifier for SFrame.  */
-unsigned char
-x86_sframe_get_abi_arch (void)
-{
-  unsigned char sframe_abi_arch = 0;
-
-  if (x86_support_sframe_p ())
-    {
-      gas_assert (!target_big_endian);
-      sframe_abi_arch = SFRAME_ABI_AMD64_ENDIAN_LITTLE;
-    }
-
-  return sframe_abi_arch;
-}
-
+#include "tc-i386-ginsn.c"
 #endif
 
 static unsigned int
--- /dev/null
+++ b/gas/config/tc-i386-ginsn.c
@@ -0,0 +1,1162 @@ 
+/* tc-i386-ginsn.c -- Ginsn generation for the x86-64 ISA
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GAS.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the license, or
+   (at your option) any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; see the file COPYING3. If not,
+   see <http://www.gnu.org/licenses/>.  */
+
+/* This file contains the implementation of the ginsn creation for x86-64
+   instructions.  */
+
+/* DWARF register number for EFLAGS.  Used for pushf/popf insns.  */
+#define GINSN_DW2_REGNUM_EFLAGS     49
+/* DWARF register number for RSI.  Used as dummy value when RegIP/RegIZ.  */
+#define GINSN_DW2_REGNUM_RSI_DUMMY  4
+
+/* Identify the callee-saved registers in System V AMD64 ABI.  */
+
+bool
+x86_scfi_callee_saved_p (unsigned int dw2reg_num)
+{
+  if (dw2reg_num == 3 /* rbx.  */
+      || dw2reg_num == REG_FP /* rbp.  */
+      || dw2reg_num == REG_SP /* rsp.  */
+      || (dw2reg_num >= 12 && dw2reg_num <= 15) /* r12 - r15.  */)
+    return true;
+
+  return false;
+}
+
+/* Check whether an instruction prefix which affects operation size
+   accompanies.  For insns in the legacy space, setting REX.W takes precedence
+   over the operand-size prefix (66H) when both are used.
+
+   The current users of this API are in the handlers for PUSH, POP or other
+   instructions which affect the stack pointer implicitly:  the operation size
+   (16, 32, or 64 bits) determines the amount by which the stack pointer is
+   incremented / decremented (2, 4 or 8).  */
+
+static bool
+ginsn_opsize_prefix_p (void)
+{
+  return (!(i.prefix[REX_PREFIX] & REX_W) && i.prefix[DATA_PREFIX]);
+}
+
+/* Get the DWARF register number for the given register entry.
+   For specific byte/word/dword register accesses like al, cl, ah, ch, r8d,
+   r20w etc., we need to identify the DWARF register number for the
+   corresponding 8-byte GPR.
+
+   This function is a hack - it relies on relative ordering of reg entries in
+   the i386_regtab.  FIXME - it will be good to allow a more direct way to get
+   this information.  */
+
+static unsigned int
+ginsn_dw2_regnum (const reg_entry *ireg)
+{
+  const reg_entry *temp = ireg;
+  unsigned int dwarf_reg = Dw2Inval, idx = 0;
+
+  /* ginsn creation is available for AMD64 abi only ATM.  Other flag_code
+     are not expected.  */
+  gas_assert (ireg && flag_code == CODE_64BIT);
+
+  /* Watch out for RegIP, RegIZ.  These are expected to appear only with
+     base/index addressing modes.  Although creating inaccurate data
+     dependencies, using a dummy value (lets say volatile register rsi) will
+     not hurt SCFI.  TBD_GINSN_GEN_NOT_SCFI.  */
+  if (ireg->reg_num == RegIP || ireg->reg_num == RegIZ)
+    return GINSN_DW2_REGNUM_RSI_DUMMY;
+
+  dwarf_reg = ireg->dw2_regnum[object_64bit];
+
+  if (dwarf_reg == Dw2Inval)
+    {
+      if (ireg <= &i386_regtab[3])
+	/* For al, cl, dl, bl, bump over to axl, cxl, dxl, bxl respectively by
+	   adding 8.  */
+	temp = ireg + 8;
+      else if (ireg <= &i386_regtab[7])
+	/* For ah, ch, dh, bh, bump over to axl, cxl, dxl, bxl respectively by
+	   adding 4.  */
+	temp = ireg + 4;
+      else
+	{
+	  /* The code relies on the relative ordering of the reg entries in
+	     i386_regtab.  There are 32 register entries between axl-r31b,
+	     ax-r31w etc.  The assertions here ensures the code does not
+	     recurse indefinitely.  */
+	  gas_assert ((temp - &i386_regtab[0]) >= 0);
+	  idx = temp - &i386_regtab[0];
+	  gas_assert (idx + 32 < i386_regtab_size - 1);
+
+	  temp = temp + 32;
+	}
+
+      dwarf_reg = ginsn_dw2_regnum (temp);
+    }
+
+  /* Sanity check - failure may indicate state corruption, bad ginsn or
+     perhaps the i386-reg table and the current function got out of sync.  */
+  gas_assert (dwarf_reg < Dw2Inval);
+
+  return dwarf_reg;
+}
+
+static ginsnS *
+x86_ginsn_addsub_reg_mem (const symbolS *insn_end_sym)
+{
+  unsigned int dw2_regnum;
+  unsigned int src1_dw2_regnum;
+  ginsnS *ginsn = NULL;
+  ginsnS * (*ginsn_func) (const symbolS *, bool,
+			  enum ginsn_src_type, unsigned int, offsetT,
+			  enum ginsn_src_type, unsigned int, offsetT,
+			  enum ginsn_dst_type, unsigned int, offsetT);
+  uint16_t opcode = i.tm.base_opcode;
+
+  gas_assert (i.tm.opcode_space == SPACE_BASE
+	      && (opcode == 0x1 || opcode == 0x29));
+  ginsn_func = (opcode == 0x1) ? ginsn_new_add : ginsn_new_sub;
+
+  /* op %reg, symbol or even other cases where destination involves indirect
+     access are unnecessary for SCFI correctness.  TBD_GINSN_GEN_NOT_SCFI.  */
+  if (i.mem_operands)
+    return ginsn;
+
+  /* Skip detection of 8/16/32-bit op size; 'add/sub reg, reg/mem' ops always
+     make the dest reg untraceable for SCFI.  */
+
+  /* op reg, reg/mem.  */
+  src1_dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+  /* Of interest only when second opnd is not memory.  */
+  if (i.reg_operands == 2)
+    {
+      dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
+      ginsn = ginsn_func (insn_end_sym, true,
+			  GINSN_SRC_REG, src1_dw2_regnum, 0,
+			  GINSN_SRC_REG, dw2_regnum, 0,
+			  GINSN_DST_REG, dw2_regnum, 0);
+      ginsn_set_where (ginsn);
+    }
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_addsub_mem_reg (const symbolS *insn_end_sym)
+{
+  unsigned int dw2_regnum;
+  unsigned int src1_dw2_regnum;
+  const reg_entry *mem_reg;
+  int32_t gdisp = 0;
+  ginsnS *ginsn = NULL;
+  ginsnS * (*ginsn_func) (const symbolS *, bool,
+			  enum ginsn_src_type, unsigned int, offsetT,
+			  enum ginsn_src_type, unsigned int, offsetT,
+			  enum ginsn_dst_type, unsigned int, offsetT);
+  uint16_t opcode = i.tm.base_opcode;
+
+  gas_assert (i.tm.opcode_space == SPACE_BASE
+	      && (opcode == 0x3 || opcode == 0x2b));
+  ginsn_func = (opcode == 0x3) ? ginsn_new_add : ginsn_new_sub;
+
+  /* op symbol, %reg.  */
+  if (i.mem_operands && !i.base_reg && !i.index_reg)
+    return ginsn;
+
+  /* Skip detection of 8/16/32-bit op size; 'add/sub reg/mem, reg' ops always
+     make the dest reg untraceable for SCFI.  */
+
+  /* op reg/mem, %reg.  */
+  dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
+
+  if (i.reg_operands == 2)
+    {
+      src1_dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      ginsn = ginsn_func (insn_end_sym, true,
+			  GINSN_SRC_REG, src1_dw2_regnum, 0,
+			  GINSN_SRC_REG, dw2_regnum, 0,
+			  GINSN_DST_REG, dw2_regnum, 0);
+      ginsn_set_where (ginsn);
+    }
+  else if (i.mem_operands)
+    {
+      mem_reg = (i.base_reg) ? i.base_reg : i.index_reg;
+      src1_dw2_regnum = ginsn_dw2_regnum (mem_reg);
+      if (i.disp_operands == 1)
+	gdisp = i.op[0].disps->X_add_number;
+      ginsn = ginsn_func (insn_end_sym, true,
+			  GINSN_SRC_INDIRECT, src1_dw2_regnum, gdisp,
+			  GINSN_SRC_REG, dw2_regnum, 0,
+			  GINSN_DST_REG, dw2_regnum, 0);
+      ginsn_set_where (ginsn);
+    }
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_alu_imm (const symbolS *insn_end_sym)
+{
+  offsetT src_imm;
+  unsigned int dw2_regnum;
+  ginsnS *ginsn = NULL;
+  enum ginsn_src_type src_type = GINSN_SRC_REG;
+  enum ginsn_dst_type dst_type = GINSN_DST_REG;
+
+  ginsnS * (*ginsn_func) (const symbolS *, bool,
+			  enum ginsn_src_type, unsigned int, offsetT,
+			  enum ginsn_src_type, unsigned int, offsetT,
+			  enum ginsn_dst_type, unsigned int, offsetT);
+
+  /* FIXME - create ginsn where dest is REG_SP / REG_FP only ? */
+  /* Map for insn.tm.extension_opcode
+     000 ADD    100 AND
+     001 OR     101 SUB
+     010 ADC    110 XOR
+     011 SBB    111 CMP  */
+
+  /* add/sub/and imm, %reg only at this time for SCFI.
+     Although all three ('and', 'or' , 'xor') make the destination reg
+     untraceable, 'and' op is handled but not 'or' / 'xor' because we will look
+     into supporting the DRAP pattern at some point.  Other opcodes ('adc',
+     'sbb' and 'cmp') are not generated here either.  The ginsn representation
+     does not have support for the latter three opcodes;  GINSN_TYPE_OTHER may
+     be added for these after x86_ginsn_unhandled () invocation if the
+     destination register is REG_SP or REG_FP.  */
+  if (i.tm.extension_opcode == 5)
+    ginsn_func = ginsn_new_sub;
+  else if (i.tm.extension_opcode == 4)
+    ginsn_func = ginsn_new_and;
+  else if (i.tm.extension_opcode == 0)
+    ginsn_func = ginsn_new_add;
+  else
+    return ginsn;
+
+  /* TBD_GINSN_REPRESENTATION_LIMIT: There is no representation for when a
+     symbol is used as an operand, like so:
+	  addq    $simd_cmp_op+8, %rdx
+     Skip generating any ginsn for this.  */
+  if (i.imm_operands == 1
+      && i.op[0].imms->X_op != O_constant)
+    return ginsn;
+
+  /* addq    $1, symbol
+     addq    $1, -16(%rbp)
+     These are not of interest for SCFI.  Also, TBD_GINSN_GEN_NOT_SCFI.  */
+  if (i.mem_operands == 1)
+    return ginsn;
+
+  /* 8/16/32-bit op size makes the destination reg untraceable for SCFI.
+     Deal with this via the x86_ginsn_unhandled () code path.  */
+  if (i.suffix != QWORD_MNEM_SUFFIX)
+    return ginsn;
+
+  gas_assert (i.imm_operands == 1);
+  src_imm = i.op[0].imms->X_add_number;
+  /* The second operand may be a register or indirect access.  For SCFI, only
+     the case when the second opnd is a register is interesting.  Revisit this
+     if generating ginsns for a different gen mode TBD_GINSN_GEN_NOT_SCFI.  */
+  if (i.reg_operands == 1)
+    {
+      dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
+      /* For ginsn, keep the imm as second src operand.  */
+      ginsn = ginsn_func (insn_end_sym, true,
+			  src_type, dw2_regnum, 0,
+			  GINSN_SRC_IMM, 0, src_imm,
+			  dst_type, dw2_regnum, 0);
+
+      ginsn_set_where (ginsn);
+    }
+
+  return ginsn;
+}
+
+/* Create ginsn(s) for MOV operations.
+
+   The generated ginsns corresponding to mov with indirect access to memory
+   (src or dest) suffer with loss of information: when both index and base
+   registers are at play, only base register gets conveyed in ginsn.  Note
+   this TBD_GINSN_GEN_NOT_SCFI.  */
+
+static ginsnS *
+x86_ginsn_move (const symbolS *insn_end_sym)
+{
+  ginsnS *ginsn = NULL;
+  unsigned int dst_reg;
+  unsigned int src_reg;
+  offsetT src_disp = 0;
+  offsetT dst_disp = 0;
+  const reg_entry *dst = NULL;
+  const reg_entry *src = NULL;
+  uint16_t opcode = i.tm.base_opcode;
+  enum ginsn_src_type src_type = GINSN_SRC_REG;
+  enum ginsn_dst_type dst_type = GINSN_DST_REG;
+
+  /* mov %reg, symbol or mov symbol, %reg.
+     Not of interest for SCFI.  Also, TBD_GINSN_GEN_NOT_SCFI.  */
+  if (i.mem_operands == 1 && !i.base_reg && !i.index_reg)
+    return ginsn;
+
+  /* 8/16/32-bit op size makes the destination reg untraceable for SCFI.
+     Handle mov reg, reg only.  mov to or from a memory operand will make
+     dest reg, when present, untraceable, irrespective of the op size.  */
+  if (i.reg_operands == 2 && i.suffix != QWORD_MNEM_SUFFIX)
+    return ginsn;
+
+  gas_assert (i.tm.opcode_space == SPACE_BASE);
+  if (opcode == 0x8b || opcode == 0x8a)
+    {
+      /* mov  disp(%reg), %reg.  */
+      if (i.mem_operands)
+	{
+	  src = (i.base_reg) ? i.base_reg : i.index_reg;
+	  if (i.disp_operands == 1)
+	    src_disp = i.op[0].disps->X_add_number;
+	  src_type = GINSN_SRC_INDIRECT;
+	}
+      else
+	src = i.op[0].regs;
+
+      dst = i.op[1].regs;
+    }
+  else if (opcode == 0x89 || opcode == 0x88)
+    {
+      /* mov %reg, disp(%reg).  */
+      src = i.op[0].regs;
+      if (i.mem_operands)
+	{
+	  dst = (i.base_reg) ? i.base_reg : i.index_reg;
+	  if (i.disp_operands == 1)
+	    dst_disp = i.op[1].disps->X_add_number;
+	  dst_type = GINSN_DST_INDIRECT;
+	}
+      else
+	dst = i.op[1].regs;
+    }
+
+  src_reg = ginsn_dw2_regnum (src);
+  dst_reg = ginsn_dw2_regnum (dst);
+
+  ginsn = ginsn_new_mov (insn_end_sym, true,
+			 src_type, src_reg, src_disp,
+			 dst_type, dst_reg, dst_disp);
+  ginsn_set_where (ginsn);
+
+  return ginsn;
+}
+
+/* Generate appropriate ginsn for lea.
+
+   Unhandled sub-cases (marked with TBD_GINSN_GEN_NOT_SCFI) also suffer with
+   some loss of information in the final ginsn chosen eventually (type
+   GINSN_TYPE_OTHER).  But this is fine for now for GINSN_GEN_SCFI generation
+   mode.  */
+
+static ginsnS *
+x86_ginsn_lea (const symbolS *insn_end_sym)
+{
+  offsetT src_disp = 0;
+  ginsnS *ginsn = NULL;
+  unsigned int src1_reg;
+  const reg_entry *src1;
+  offsetT index_scale;
+  unsigned int dst_reg;
+  bool index_regiz_p;
+
+  if ((!i.base_reg) != (!i.index_reg || i.index_reg->reg_num == RegIZ))
+    {
+      /* lea disp(%base), %dst    or    lea disp(,%index,imm), %dst.
+	 Either index_reg or base_reg exists, but not both.  Further, as per
+	 above, the case when just %index exists but is equal to RegIZ is
+	 excluded.  If not excluded, a GINSN_TYPE_MOV of %rsi
+	 (GINSN_DW2_REGNUM_RSI_DUMMY) to %dst will be generated by this block.
+	 Such a mov ginsn is imprecise; so, exclude now and generate
+	 GINSN_TYPE_OTHER instead later via the x86_ginsn_unhandled ().
+	 Excluding other cases is required due to
+	 TBD_GINSN_REPRESENTATION_LIMIT.  */
+
+      index_scale = i.log2_scale_factor;
+      index_regiz_p = i.index_reg && i.index_reg->reg_num == RegIZ;
+      src1 = i.base_reg ? i.base_reg : i.index_reg;
+      src1_reg = ginsn_dw2_regnum (src1);
+      dst_reg = ginsn_dw2_regnum (i.op[1].regs);
+      /* It makes sense to represent a scale factor of 1 precisely here
+	 (i.e., not using GINSN_TYPE_OTHER, but rather similar to the
+	 base-without-index case).  A non-zero scale factor is still OK if
+	 the index reg is zero reg.
+	 However, skip from here the case when disp has a symbol instead.
+	 TBD_GINSN_REPRESENTATION_LIMIT.  */
+      if ((!index_scale || index_regiz_p)
+	  && (!i.disp_operands || i.op[0].disps->X_op == O_constant))
+	{
+	  if (i.disp_operands)
+	    src_disp = i.op[0].disps->X_add_number;
+
+	  if (src_disp)
+	    /* Generate an ADD ginsn.  */
+	    ginsn = ginsn_new_add (insn_end_sym, true,
+				   GINSN_SRC_REG, src1_reg, 0,
+				   GINSN_SRC_IMM, 0, src_disp,
+				   GINSN_DST_REG, dst_reg, 0);
+	  else
+	    /* Generate a MOV ginsn.  */
+	    ginsn = ginsn_new_mov (insn_end_sym, true,
+				   GINSN_SRC_REG, src1_reg, 0,
+				   GINSN_DST_REG, dst_reg, 0);
+
+	  ginsn_set_where (ginsn);
+	}
+    }
+  /* Skip handling other cases here,
+     - when (i.index_reg && i.base_reg) is true,
+       e.g., lea disp(%base,%index,imm), %dst
+       We do not have a ginsn representation for multiply.
+     - or, when (!i.index_reg && !i.base_reg) is true,
+       e.g., lea symbol, %dst
+       Not a frequent pattern.  If %dst is a register of interest, the user is
+       likely to use a MOV op anyway.
+     Deal with these via the x86_ginsn_unhandled () code path to generate
+     GINSN_TYPE_OTHER when necessary.  TBD_GINSN_GEN_NOT_SCFI.  */
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_jump (const symbolS *insn_end_sym, bool cond_p)
+{
+  ginsnS *ginsn = NULL;
+  const symbolS *src_symbol;
+  ginsnS * (*ginsn_func) (const symbolS *sym, bool real_p,
+			  enum ginsn_src_type src_type, unsigned int src_reg,
+			  const symbolS *src_ginsn_sym);
+
+  gas_assert (i.disp_operands == 1);
+
+  ginsn_func = cond_p ? ginsn_new_jump_cond : ginsn_new_jump;
+  if (i.op[0].disps->X_op == O_symbol && !i.op[0].disps->X_add_number)
+    {
+      src_symbol = i.op[0].disps->X_add_symbol;
+      ginsn = ginsn_func (insn_end_sym, true,
+			  GINSN_SRC_SYMBOL, 0, src_symbol);
+
+      ginsn_set_where (ginsn);
+    }
+  else
+    {
+      /* A non-zero addend in jump/JCC target makes control-flow tracking
+	 difficult.  Skip SCFI for now.  */
+      as_bad (_("SCFI: `%s' insn with non-zero addend to sym not supported"),
+	      cond_p ? "JCC" : "jmp");
+      return ginsn;
+    }
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_enter (const symbolS *insn_end_sym)
+{
+  ginsnS *ginsn = NULL;
+  ginsnS *ginsn_next = NULL;
+  ginsnS *ginsn_last = NULL;
+  /* In 64-bit mode, the default stack update size is 8 bytes.  */
+  int stack_opnd_size = 8;
+
+  gas_assert (i.imm_operands == 2);
+
+  /* For non-zero size operands, bail out as untraceable for SCFI.  */
+  if (i.op[0].imms->X_op != O_constant || i.op[0].imms->X_add_symbol != 0
+      || i.op[1].imms->X_op != O_constant || i.op[1].imms->X_add_symbol != 0)
+    {
+      as_bad ("SCFI: enter insn with non-zero operand not supported");
+      return ginsn;
+    }
+
+  /* Check if this is a 16-bit op.  */
+  if (ginsn_opsize_prefix_p ())
+    stack_opnd_size = 2;
+
+  /* If the nesting level is 0, the processor pushes the frame pointer from
+     the BP/EBP/RBP register onto the stack, copies the current stack
+     pointer from the SP/ESP/RSP register into the BP/EBP/RBP register, and
+     loads the SP/ESP/RSP register with the current stack-pointer value
+     minus the value in the size operand.  */
+  ginsn = ginsn_new_sub (insn_end_sym, false,
+			 GINSN_SRC_REG, REG_SP, 0,
+			 GINSN_SRC_IMM, 0, stack_opnd_size,
+			 GINSN_DST_REG, REG_SP, 0);
+  ginsn_set_where (ginsn);
+  ginsn_next = ginsn_new_store (insn_end_sym, false,
+				GINSN_SRC_REG, REG_FP,
+				GINSN_DST_INDIRECT, REG_SP, 0);
+  ginsn_set_where (ginsn_next);
+  gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+  ginsn_last = ginsn_new_mov (insn_end_sym, false,
+			      GINSN_SRC_REG, REG_SP, 0,
+			      GINSN_DST_REG, REG_FP, 0);
+  ginsn_set_where (ginsn_last);
+  gas_assert (!ginsn_link_next (ginsn_next, ginsn_last));
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_leave (const symbolS *insn_end_sym)
+{
+  ginsnS *ginsn = NULL;
+  ginsnS *ginsn_next = NULL;
+  ginsnS *ginsn_last = NULL;
+  /* In 64-bit mode, the default stack update size is 8 bytes.  */
+  int stack_opnd_size = 8;
+
+  /* Check if this is a 16-bit op.  */
+  if (ginsn_opsize_prefix_p ())
+    stack_opnd_size = 2;
+
+  /* The 'leave' instruction copies the contents of the RBP register
+     into the RSP register to release all stack space allocated to the
+     procedure.  */
+  ginsn = ginsn_new_mov (insn_end_sym, false,
+			 GINSN_SRC_REG, REG_FP, 0,
+			 GINSN_DST_REG, REG_SP, 0);
+  ginsn_set_where (ginsn);
+  /* Then it restores the old value of the RBP register from the stack.  */
+  ginsn_next = ginsn_new_load (insn_end_sym, false,
+			       GINSN_SRC_INDIRECT, REG_SP, 0,
+			       GINSN_DST_REG, REG_FP);
+  ginsn_set_where (ginsn_next);
+  gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+  ginsn_last = ginsn_new_add (insn_end_sym, false,
+			      GINSN_SRC_REG, REG_SP, 0,
+			      GINSN_SRC_IMM, 0, stack_opnd_size,
+			      GINSN_DST_REG, REG_SP, 0);
+  ginsn_set_where (ginsn_next);
+  gas_assert (!ginsn_link_next (ginsn_next, ginsn_last));
+
+  return ginsn;
+}
+
+/* Check if an instruction is whitelisted.
+
+   Some instructions may appear with REG_SP or REG_FP as destination, because
+   which they are deemed 'interesting' for SCFI.  Whitelist them here if they
+   do not affect SCFI correctness.  */
+
+static bool
+x86_ginsn_safe_to_skip_p (void)
+{
+  bool skip_p = false;
+  uint16_t opcode = i.tm.base_opcode;
+
+  switch (opcode)
+    {
+    case 0x80:
+    case 0x81:
+    case 0x83:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* cmp imm, reg/rem.  */
+      if (i.tm.extension_opcode == 7)
+	skip_p = true;
+      break;
+
+    case 0x38:
+    case 0x39:
+    case 0x3a:
+    case 0x3b:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* cmp imm/reg/mem, reg/rem.  */
+      skip_p = true;
+      break;
+
+    case 0xf6:
+    case 0xf7:
+    case 0x84:
+    case 0x85:
+      /* test imm/reg/mem, reg/mem.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      skip_p = true;
+      break;
+
+    default:
+      break;
+    }
+
+  return skip_p;
+}
+
+#define X86_GINSN_UNHANDLED_NONE        0
+#define X86_GINSN_UNHANDLED_DEST_REG    1
+#define X86_GINSN_UNHANDLED_CFG         2
+#define X86_GINSN_UNHANDLED_STACKOP     3
+#define X86_GINSN_UNHANDLED_UNEXPECTED  4
+
+/* Check the input insn for its impact on the correctness of the synthesized
+   CFI.  Returns an error code to the caller.  */
+
+static int
+x86_ginsn_unhandled (void)
+{
+  int err = X86_GINSN_UNHANDLED_NONE;
+  const reg_entry *reg_op;
+  unsigned int dw2_regnum;
+
+  /* Keep an eye out for instructions affecting control flow.  */
+  if (i.tm.opcode_modifier.jump)
+    err = X86_GINSN_UNHANDLED_CFG;
+  /* Also, for any instructions involving an implicit update to the stack
+     pointer.  */
+  else if (i.tm.opcode_modifier.operandconstraint == IMPLICIT_STACK_OP)
+    err = X86_GINSN_UNHANDLED_STACKOP;
+  /* Finally, also check if the missed instructions are affecting REG_SP or
+     REG_FP.  The destination operand is the last at all stages of assembly
+     (due to following AT&T syntax layout in the internal representation).  In
+     case of Intel syntax input, this still remains true as swap_operands ()
+     is done by now.
+     PS: These checks do not involve index / base reg, as indirect memory
+     accesses via REG_SP or REG_FP do not affect SCFI correctness.
+     (Also note these instructions are candidates for other ginsn generation
+     modes in future.  TBD_GINSN_GEN_NOT_SCFI.)  */
+  else if (i.operands && i.reg_operands
+	   && !(i.flags[i.operands - 1] & Operand_Mem))
+    {
+      reg_op = i.op[i.operands - 1].regs;
+      if (reg_op)
+	{
+	  dw2_regnum = ginsn_dw2_regnum (reg_op);
+	  if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
+	    err = X86_GINSN_UNHANDLED_DEST_REG;
+	}
+      else
+	/* Something unexpected.  Indicate to caller.  */
+	err = X86_GINSN_UNHANDLED_UNEXPECTED;
+    }
+
+  return err;
+}
+
+/* Generate one or more generic GAS instructions, a.k.a, ginsns for the current
+   machine instruction.
+
+   Returns the head of linked list of ginsn(s) added, if success; Returns NULL
+   if failure.
+
+   The input ginsn_gen_mode GMODE determines the set of minimal necessary
+   ginsns necessary for correctness of any passes applicable for that mode.
+   For supporting the GINSN_GEN_SCFI generation mode, following is the list of
+   machine instructions that must be translated into the corresponding ginsns
+   to ensure correctness of SCFI:
+     - All instructions affecting the two registers that could potentially
+       be used as the base register for CFA tracking.  For SCFI, the base
+       register for CFA tracking is limited to REG_SP and REG_FP only for
+       now.
+     - All change of flow instructions: conditional and unconditional branches,
+       call and return from functions.
+     - All instructions that can potentially be a register save / restore
+       operation.
+     - All instructions that perform stack manipulation implicitly: the CALL,
+       RET, PUSH, POP, ENTER, and LEAVE instructions.
+
+   The function currently supports GINSN_GEN_SCFI ginsn generation mode only.
+   To support other generation modes will require work on this target-specific
+   process of creation of ginsns:
+     - Some of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
+       possible starting points.
+     - Also note that ginsn representation may need enhancements.  Specifically,
+       note some TBD_GINSN_INFO_LOSS and TBD_GINSN_REPRESENTATION_LIMIT markers.
+   */
+
+static ginsnS *
+x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
+{
+  int err = 0;
+  uint16_t opcode;
+  unsigned int dw2_regnum;
+  const reg_entry *mem_reg;
+  ginsnS *ginsn = NULL;
+  ginsnS *ginsn_next = NULL;
+  /* In 64-bit mode, the default stack update size is 8 bytes.  */
+  int stack_opnd_size = 8;
+
+  /* Currently supports generation of selected ginsns, sufficient for
+     the use-case of SCFI only.  */
+  if (gmode != GINSN_GEN_SCFI)
+    return ginsn;
+
+  opcode = i.tm.base_opcode;
+
+  /* Until it is clear how to handle APX NDD and other new opcodes, disallow
+     them from SCFI.  */
+  if (is_apx_rex2_encoding ()
+      || (i.tm.opcode_modifier.evex && is_apx_evex_encoding ()))
+    {
+      as_bad (_("SCFI: unsupported APX op %#x may cause incorrect CFI"),
+	      opcode);
+      return ginsn;
+    }
+
+  switch (opcode)
+    {
+
+    /* Add opcodes 0x0/0x2 and sub opcodes 0x28/0x2a (with opcode_space
+       SPACE_BASE) are 8-bit ops.  While they are relevant for SCFI
+       correctness,  skip handling them here and use the x86_ginsn_unhandled
+       code path to generate GINSN_TYPE_OTHER when necessary.  */
+
+    case 0x1:  /* add reg, reg/mem.  */
+    case 0x29: /* sub reg, reg/mem.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      ginsn = x86_ginsn_addsub_reg_mem (insn_end_sym);
+      break;
+
+    case 0x3:  /* add reg/mem, reg.  */
+    case 0x2b: /* sub reg/mem, reg.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      ginsn = x86_ginsn_addsub_mem_reg (insn_end_sym);
+      break;
+
+    case 0xa0: /* push fs.  */
+    case 0xa8: /* push gs.  */
+      /* push fs / push gs have opcode_space == SPACE_0F.  */
+      if (i.tm.opcode_space != SPACE_0F)
+	break;
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, stack_opnd_size,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+      ginsn_next = ginsn_new_store (insn_end_sym, false,
+				    GINSN_SRC_REG, dw2_regnum,
+				    GINSN_DST_INDIRECT, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0xa1: /* pop fs.  */
+    case 0xa9: /* pop gs.  */
+      /* pop fs / pop gs have opcode_space == SPACE_0F.  */
+      if (i.tm.opcode_space != SPACE_0F)
+	break;
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_INDIRECT, REG_SP, 0,
+			      GINSN_DST_REG, dw2_regnum);
+      ginsn_set_where (ginsn);
+      ginsn_next = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, stack_opnd_size,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0x50 ... 0x57:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* push reg.  */
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, stack_opnd_size,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+      ginsn_next = ginsn_new_store (insn_end_sym, false,
+				    GINSN_SRC_REG, dw2_regnum,
+				    GINSN_DST_INDIRECT, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0x58 ... 0x5f:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* pop reg.  */
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_INDIRECT, REG_SP, 0,
+			      GINSN_DST_REG, dw2_regnum);
+      ginsn_set_where (ginsn);
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      ginsn_next = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, stack_opnd_size,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0x6a: /* push imm8.  */
+    case 0x68: /* push imm16/imm32.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      /* Skip getting the value of imm from machine instruction
+	 because this is not important for SCFI.  */
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, stack_opnd_size,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+      ginsn_next = ginsn_new_store (insn_end_sym, false,
+				    GINSN_SRC_IMM, 0,
+				    GINSN_DST_INDIRECT, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    /* PS: Opcodes 0x80 ... 0x8f with opcode_space SPACE_0F are present
+       only after relaxation.  They do not need to be handled for ginsn
+       creation.  */
+    case 0x70 ... 0x7f:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      ginsn = x86_ginsn_jump (insn_end_sym, true);
+      break;
+
+    case 0x80:
+    case 0x81:
+    case 0x83:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      ginsn = x86_ginsn_alu_imm (insn_end_sym);
+      break;
+
+    case 0x8a: /* mov r/m8, r8.  */
+    case 0x8b: /* mov r/m(16/32/64), r(16/32/64).  */
+    case 0x88: /* mov r8, r/m8.  */
+    case 0x89: /* mov r(16/32/64), r/m(16/32/64).  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      ginsn = x86_ginsn_move (insn_end_sym);
+      break;
+
+    case 0x8d:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* lea disp(%base,%index,imm), %dst.  */
+      ginsn = x86_ginsn_lea (insn_end_sym);
+      break;
+
+    case 0x8f:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* pop to reg/mem.  */
+      if (i.mem_operands)
+	{
+	  mem_reg = (i.base_reg) ? i.base_reg : i.index_reg;
+	  /* Use dummy register if no base or index.  Unlike other opcodes,
+	     ginsns must be generated as this affect stack pointer.  */
+	  dw2_regnum = (mem_reg
+			? ginsn_dw2_regnum (mem_reg)
+			: GINSN_DW2_REGNUM_RSI_DUMMY);
+	}
+      else
+	dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_INDIRECT, REG_SP, 0,
+			      GINSN_DST_INDIRECT, dw2_regnum);
+      ginsn_set_where (ginsn);
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      ginsn_next = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, stack_opnd_size,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0x9c:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* pushf / pushfq.  */
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, stack_opnd_size,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+      /* FIXME - hardcode the actual DWARF reg number value.  As for SCFI
+	 correctness, although this behaves simply a placeholder value; its
+	 just clearer if the value is correct.  */
+      dw2_regnum = GINSN_DW2_REGNUM_EFLAGS;
+      ginsn_next = ginsn_new_store (insn_end_sym, false,
+				    GINSN_SRC_REG, dw2_regnum,
+				    GINSN_DST_INDIRECT, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0x9d:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* popf / popfq.  */
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      /* FIXME - hardcode the actual DWARF reg number value.  As for SCFI
+	 correctness, although this behaves simply a placeholder value; its
+	 just clearer if the value is correct.  */
+      dw2_regnum = GINSN_DW2_REGNUM_EFLAGS;
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_INDIRECT, REG_SP, 0,
+			      GINSN_DST_REG, dw2_regnum);
+      ginsn_set_where (ginsn);
+      ginsn_next = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, stack_opnd_size,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0xff:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* push from reg/mem.  */
+      if (i.tm.extension_opcode == 6)
+	{
+	  /* Check if operation size is 16-bit.  */
+	  if (ginsn_opsize_prefix_p ())
+	    stack_opnd_size = 2;
+	  ginsn = ginsn_new_sub (insn_end_sym, false,
+				 GINSN_SRC_REG, REG_SP, 0,
+				 GINSN_SRC_IMM, 0, stack_opnd_size,
+				 GINSN_DST_REG, REG_SP, 0);
+	  ginsn_set_where (ginsn);
+	  if (i.mem_operands)
+	    {
+	      mem_reg = (i.base_reg) ? i.base_reg : i.index_reg;
+	      /* Use dummy register if no base or index.  Unlike other opcodes,
+		 ginsns must be generated as this affect stack pointer.  */
+	      dw2_regnum = (mem_reg
+			    ? ginsn_dw2_regnum (mem_reg)
+			    : GINSN_DW2_REGNUM_RSI_DUMMY);
+	    }
+	  else
+	    dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+	  ginsn_next = ginsn_new_store (insn_end_sym, false,
+					GINSN_SRC_INDIRECT, dw2_regnum,
+					GINSN_DST_INDIRECT, REG_SP, 0);
+	  ginsn_set_where (ginsn_next);
+	  gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+	}
+      else if (i.tm.extension_opcode == 4)
+	{
+	  /* jmp r/m.  E.g., notrack jmp *%rax.  */
+	  if (i.reg_operands)
+	    {
+	      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+	      ginsn = ginsn_new_jump (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	  else if (i.mem_operands && i.index_reg)
+	    {
+	      /* jmp    *0x0(,%rax,8).  */
+	      dw2_regnum = ginsn_dw2_regnum (i.index_reg);
+	      ginsn = ginsn_new_jump (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	  else if (i.mem_operands && i.base_reg)
+	    {
+	      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
+	      ginsn = ginsn_new_jump (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	}
+      else if (i.tm.extension_opcode == 2)
+	{
+	  /* 0xFF /2 (call).  */
+	  if (i.reg_operands)
+	    {
+	      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+	      ginsn = ginsn_new_call (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	  else if (i.mem_operands && i.base_reg)
+	    {
+	      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
+	      ginsn = ginsn_new_call (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	}
+      break;
+
+    case 0xc2: /* ret imm16.  */
+    case 0xc3: /* ret.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* Near ret.  */
+      ginsn = ginsn_new_return (insn_end_sym, true);
+      ginsn_set_where (ginsn);
+      break;
+
+    case 0xc8:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* enter.  */
+      ginsn = x86_ginsn_enter (insn_end_sym);
+      break;
+
+    case 0xc9:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* leave.  */
+      ginsn = x86_ginsn_leave (insn_end_sym);
+      break;
+
+    case 0xe0 ... 0xe2: /* loop / loope / loopne.  */
+    case 0xe3:          /* jecxz / jrcxz.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      ginsn = x86_ginsn_jump (insn_end_sym, true);
+      ginsn_set_where (ginsn);
+      break;
+
+    case 0xe8:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* PS: SCFI machinery does not care about which func is being
+	 called.  OK to skip that info.  */
+      ginsn = ginsn_new_call (insn_end_sym, true,
+			      GINSN_SRC_SYMBOL, 0, NULL);
+      ginsn_set_where (ginsn);
+      break;
+
+    /* PS: opcode 0xe9 appears only after relaxation.  Skip here.  */
+    case 0xeb:
+      /* If opcode_space != SPACE_BASE, this is not a jmp insn.  Skip it
+	 for GINSN_GEN_SCFI.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* Unconditional jmp.  */
+      ginsn = x86_ginsn_jump (insn_end_sym, false);
+      ginsn_set_where (ginsn);
+      break;
+
+    default:
+      /* TBD_GINSN_GEN_NOT_SCFI: Skip all other opcodes uninteresting for
+	 GINSN_GEN_SCFI mode.  */
+      break;
+    }
+
+  if (!ginsn && !x86_ginsn_safe_to_skip_p ())
+    {
+      /* For all unhandled insns that are not whitelisted, check that they do
+	 not impact SCFI correctness.  */
+      err = x86_ginsn_unhandled ();
+      switch (err)
+	{
+	case X86_GINSN_UNHANDLED_NONE:
+	  break;
+	case X86_GINSN_UNHANDLED_DEST_REG:
+	  /* Not all writes to REG_FP are harmful in context of SCFI.  Simply
+	     generate a GINSN_TYPE_OTHER with destination set to the
+	     appropriate register.  The SCFI machinery will bail out if this
+	     ginsn affects SCFI correctness.  */
+	  dw2_regnum = ginsn_dw2_regnum (i.op[i.operands - 1].regs);
+	  ginsn = ginsn_new_other (insn_end_sym, true,
+				   GINSN_SRC_IMM, 0,
+				   GINSN_SRC_IMM, 0,
+				   GINSN_DST_REG, dw2_regnum);
+	  ginsn_set_where (ginsn);
+	  break;
+	case X86_GINSN_UNHANDLED_CFG:
+	case X86_GINSN_UNHANDLED_STACKOP:
+	  as_bad (_("SCFI: unhandled op %#x may cause incorrect CFI"), opcode);
+	  break;
+	case X86_GINSN_UNHANDLED_UNEXPECTED:
+	  as_bad (_("SCFI: unexpected op %#x may cause incorrect CFI"),
+		  opcode);
+	  break;
+	default:
+	  abort ();
+	  break;
+	}
+    }
+
+  return ginsn;
+}
+
+/* Whether SFrame stack trace info is supported.  */
+bool
+x86_support_sframe_p (void)
+{
+  /* At this time, SFrame stack trace is supported for AMD64 ABI only.  */
+  return (x86_elf_abi == X86_64_ABI);
+}
+
+/* Whether SFrame return address tracking is needed.  */
+bool
+x86_sframe_ra_tracking_p (void)
+{
+  /* In AMD64, return address is always stored on the stack at a fixed offset
+     from the CFA (provided via x86_sframe_cfa_ra_offset ()).
+     Do not track explicitly via an SFrame Frame Row Entry.  */
+  return false;
+}
+
+/* The fixed offset from CFA for SFrame to recover the return address.
+   (useful only when SFrame RA tracking is not needed).  */
+offsetT
+x86_sframe_cfa_ra_offset (void)
+{
+  gas_assert (x86_elf_abi == X86_64_ABI);
+  return (offsetT) -8;
+}
+
+/* The abi/arch indentifier for SFrame.  */
+unsigned char
+x86_sframe_get_abi_arch (void)
+{
+  unsigned char sframe_abi_arch = 0;
+
+  if (x86_support_sframe_p ())
+    {
+      gas_assert (!target_big_endian);
+      sframe_abi_arch = SFRAME_ABI_AMD64_ENDIAN_LITTLE;
+    }
+
+  return sframe_abi_arch;
+}