/* tc-i386-ginsn.c -- Ginsn generation for the x86-64 ISA

   Copyright (C) 2024-2026 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_indirect_branch (const symbolS *insn_end_sym)
{
  ginsnS *ginsn = NULL;
  const reg_entry *mem_reg;
  unsigned int dw2_regnum;

  ginsnS * (*ginsn_func) (const symbolS *sym, bool real_p,
			  enum ginsn_src_type src_type, unsigned int src_reg,
			  const symbolS *src_ginsn_sym);

  /* Other cases are not expected.  */
  gas_assert (i.tm.extension_opcode == 4 || i.tm.extension_opcode == 2);

  if (i.tm.extension_opcode == 4)
    /* 0xFF /4 (jmp r/m).  */
    ginsn_func = ginsn_new_jump;
  else if (i.tm.extension_opcode == 2)
    /* 0xFF /2 (call r/m).  */
    ginsn_func = ginsn_new_call;
  else
    return ginsn;

  if (i.reg_operands)
    {
      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
      ginsn = ginsn_func (insn_end_sym, true,
			  GINSN_SRC_REG, dw2_regnum, NULL);
      ginsn_set_where (ginsn);
    }
  else if (i.mem_operands)
    {
      /* Handle jump/call near, absolute indirect, address.
	 E.g., jmp/call *imm(%rN),  jmp/call *sym(,%rN,imm)
	 or  jmp/call *sym(%rN) etc.  */
      mem_reg = i.base_reg ? i.base_reg : i.index_reg;
      /* Generate a ginsn, even if it is with TBD_GINSN_INFO_LOSS.  Otherwise,
	 the user gets the impression of missing functionality due to this
	 being a COFI and alerted for via the x86_ginsn_unhandled () workflow
	 as unhandled operation (which can be misleading for users).

	 Indirect branches make the code block ineligible for SCFI; Hence, an
	 approximate ginsn will not affect SCFI correctness:
	   - Use dummy register if no base or index
	   - Skip symbol information, if any.
	 Note this case of TBD_GINSN_GEN_NOT_SCFI.  */
      dw2_regnum = (mem_reg
		    ? ginsn_dw2_regnum (mem_reg)
		    : GINSN_DW2_REGNUM_RSI_DUMMY);
      ginsn = ginsn_func (insn_end_sym, true,
			  GINSN_SRC_REG, dw2_regnum, NULL);
      ginsn_set_where (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 || i.tm.extension_opcode == 2)
	ginsn = x86_ginsn_indirect_branch (insn_end_sym);
      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;
}
