Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Yao Qi <yao@codesourcery.com>
To: Wei-cheng Wang <cole945@gmail.com>
Cc: <gdb-patches@sourceware.org>
Subject: Re: [PATCH 1/5] Code for nds32 target
Date: Wed, 10 Jul 2013 05:38:00 -0000	[thread overview]
Message-ID: <51DCF32E.9020303@codesourcery.com> (raw)
In-Reply-To: <CAPmZyH4TFFOvyis+so2=sd0oNg3-m0Kj1H-vske=WyK57TLqVQ@mail.gmail.com>

On 07/08/2013 05:27 PM, Wei-cheng Wang wrote:

Here is the 2nd half.

> +/* Implement the gdbarch_register_reggroup_p method.  */
> +
> +static int
> +nds32_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
> +			   struct reggroup *group)
> +{
> +  int i;
> +  struct reggroup *groups[] =
> +  {
> +      nds32_cr_reggroup, nds32_ir_reggroup, nds32_mr_reggroup,
> +      nds32_dr_reggroup, nds32_pfr_reggroup, nds32_dmar_reggroup,
> +      nds32_racr_reggroup, nds32_idr_reggroup
> +  };
> +  static const char *prefix[] =
> +  {
> +      "cr", "ir", "mr", "dr", "pfr", "dmar", "racr", "idr"
> +  };
> +
> +  gdb_assert (sizeof (groups) == sizeof (prefix));

gdb_assert (ARRAY_SIZE (groups) == ARRAY_SIZE (prefix)); ?

> +
> +  /* GPRs. */
> +  if (group == general_reggroup)
> +    return regnum <= NDS32_PC_REGNUM;
> +
> +  /* System Registers are grouped by prefix.  */
> +  else if (group == system_reggroup)
> +    return (regnum > NDS32_PC_REGNUM)
> +	   && TYPE_CODE (register_type (gdbarch, regnum)) != TYPE_CODE_FLT;
> +
> +  for (i = 0; i < ARRAY_SIZE (groups); i++)
> +    {
> +      if (group == groups[i])
> +	{
> +	  const char *regname = tdesc_register_name (gdbarch, regnum);
> +
> +	  if (!regname)
> +	    return 0;
> +	  return strstr (regname, prefix[i]) == regname;
> +	}
> +    }
> +
> +  return default_register_reggroup_p (gdbarch, regnum, group);
> +}
> +
> +/* This function is called when
> +   1. Target-description is used, and the register is pseudo.
> +   2. Target-description is NOT used,
> +       i. and the target is simulator.
> +      ii. or the target is legacy target.  */
> +
> +/* Implement the tdesc_pseudo_register_name method.  */
> +
> +static const char *
> +nds32_register_name (struct gdbarch *gdbarch, int regnum)
> +{
> +  static char *fpu_pseudo_names[] =
> +  {
> +    "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7",
> +    "fs8", "fs9", "fs10", "fs11", "fs12", "fs13", "fs14", "fs15",
> +    "fs16", "fs17", "fs18", "fs19", "fs20", "fs21", "fs22", "fs23",
> +    "fs24", "fs25", "fs26", "fs27", "fs28", "fs29", "fs30", "fs31"
> +  };
> +  static char *sim_names[] =
> +  {
> +    "fd0", "fd1", "fd2", "fd3", "fd4", "fd5", "fd6", "fd7",
> +    "fd8", "fd9", "fd10", "fd11", "fd12", "fd13", "fd14", "fd15",
> +    "fd16", "fd17", "fd18", "fd19", "fd20", "fd21", "fd22", "fd23",
> +    "fd24", "fd25", "fd26", "fd27", "fd28", "fd29", "fd30", "fd31",
> +    "ifclp", "itb", "ir0"
> +  };
> +  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
> +  int num_regs = gdbarch_num_regs (gdbarch);
> +
> +  /* Currently, only pseudo FSR are supported.  */
> +  if (regnum >= num_regs && regnum < num_regs + 32)
> +    return fpu_pseudo_names[regnum - num_regs];
> +
> +  /* GPRs.  */
> +  if (regnum < ARRAY_SIZE (nds32_regnames))
> +    return nds32_regnames[regnum];
> +
> +  /* Registers beteen NUM_REGS and SMI_NUM_REGS are
> +     simulator registers.  */
> +  if (regnum >= NDS32_NUM_REGS && regnum < NDS32_SIM_NUM_REGS)
> +    return sim_names[regnum - NDS32_NUM_REGS];
> +
> +  warning (_("Unknown nds32 pseudo register %d."), regnum);
> +  return NULL;
> +}
> +
> +/* Implement the gdbarch_pseudo_register_read method.
> +
> +   For legacy target, target-description and FPRs are not support.
> +   Use Rcmd to access FPU registers.  */
> +
> +static enum register_status
> +nds32_pseudo_register_read (struct gdbarch *gdbarch,
> +			    struct regcache *regcache, int regnum,
> +			    gdb_byte *buf)
> +{
> +  char name_buf[8];
> +  gdb_byte reg_buf[8];
> +  int offset;
> +  enum register_status status = REG_UNKNOWN;
> +
> +  /* Sanity check.  */
> +  regnum -= gdbarch_num_regs (gdbarch);
> +  if (regnum > gdbarch_num_pseudo_regs (gdbarch))
> +    return status;
> +
> +  /* Currently, only FSR are supported.  */
> +  if (regnum < 32)
> +    {
> +      int fd_regnum;
> +
> +      /* fs0 is always the high-part of fd0.  */
> +      if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
> +	offset = (regnum & 1) ? 4 : 0;
> +      else
> +	offset = (regnum & 1) ? 0 : 4;
> +
> +      sprintf (name_buf, "fd%d", regnum >> 1);

xsnprintf

> +      fd_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
> +					       strlen (name_buf));
> +      status = regcache_raw_read (regcache, fd_regnum, reg_buf);
> +      if (status == REG_VALID)
> +	memcpy (buf, reg_buf + offset, 4);
> +    }
> +
> +  return status;
> +}
> +
> +/* Implement the gdbarch_pseudo_register_write method.  */
> +
> +static void
> +nds32_pseudo_register_write (struct gdbarch *gdbarch,
> +			     struct regcache *regcache, int regnum,
> +			     const gdb_byte *buf)
> +{
> +  char name_buf[8];
> +  gdb_byte reg_buf[8];
> +  int offset;
> +
> +  /* Sanity check.  */
> +  regnum -= gdbarch_num_regs (gdbarch);
> +  if (regnum > gdbarch_num_pseudo_regs (gdbarch))
> +    return;
> +
> +  /* Currently, only FSR are supported.  */
> +  if (regnum < 32)
> +    {
> +      int fd_regnum;
> +
> +      /* fs0 is always the high-part of fd0.  */
> +      if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
> +	offset = (regnum & 1) ? 4 : 0;
> +      else
> +	offset = (regnum & 1) ? 0 : 4;
> +
> +      sprintf (name_buf, "fd%d", regnum >> 1);
> +      fd_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
> +					       strlen (name_buf));
> +      regcache_raw_read (regcache, fd_regnum, reg_buf);
> +      memcpy (reg_buf + offset, buf, 4);
> +      regcache_raw_write (regcache, fd_regnum, reg_buf);
> +    }
> +
> +  return;
> +}
> +
> +/* Skip prologue should be conservative, and frame-unwind should be
> +   relative-aggressive.*/
> +
> +static int
> +nds32_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR pc,
> +			CORE_ADDR scan_limit, CORE_ADDR *pl_endptr)
> +{
> +  uint32_t insn;
> +  CORE_ADDR cpc = -1;		/* Candidate PC if no suitable PC is found.  */
> +  LONGEST return_value;
> +
> +  /* If there is no buffer to store result, ignore this prologue decoding.  */
> +  if (pl_endptr == NULL)
> +    {
> +      return 0;
> +    }
> +
> +  /* Look up end of prologue */
> +  for (; pc < scan_limit; )
> +    {
> +      insn = read_memory_unsigned_integer (pc, 4, BFD_ENDIAN_BIG);
> +
> +      if ((insn & 0x80000000) == 0)
> +	{
> +	  /* 32-bit instruction */
> +
> +	  pc += 4;
> +	  if (insn == N32_ALU1 (ADD, REG_GP, REG_TA, REG_GP))
> +	    {
> +	      /* add $gp, $ta, $gp */
> +	      continue;
> +	    }
> +	  else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ADDI, REG_SP, REG_SP, 0))
> +	    {
> +	      /* addi $sp, $sp, imm15 */
> +	      cpc = pc;
> +	      continue;
> +	    }
> +	  else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ADDI, REG_FP, REG_FP, 0))
> +	    {
> +	      /* addi $fp, $sp, imm15 */
> +	      cpc = pc;
> +	      continue;
> +	    }
> +	  else if (insn == N32_ALU2 (MFUSR, REG_TA, 31, 0))
> +	    {
> +	      /* mfusr $ta, PC  ; group=0, sub=0x20=mfusr */
> +	      continue;
> +	    }
> +	  else if (CHOP_BITS (insn, 20) == N32_TYPE1 (MOVI, REG_TA, 0))
> +	    {
> +	      /* movi $ta, imm20s */
> +	      continue;
> +	    }
> +	  else if (CHOP_BITS (insn, 20) == N32_TYPE1 (SETHI, REG_GP, 0))
> +	    {
> +	      /* sethi $gp, imm20 */
> +	      continue;
> +	    }
> +	  else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ORI, REG_GP, REG_GP, 0))
> +	    {
> +	      /* ori $gp, $gp, imm15 */
> +	      continue;
> +	    }
> +	  else if (CHOP_BITS (insn, 15) == N32_TYPE2 (SWI, REG_LP, REG_FP, 0))
> +	    {
> +	      /* Unlike swi, we should stop when lwi.  */
> +	      /* swi $lp, [$sp + (imm15s<<2)] */
> +	      continue;
> +	    }
> +	  else if (CHOP_BITS (insn, 15) == N32_TYPE2 (SWI_BI, REG_LP, REG_FP, 0))
> +	    {
> +	      /* swi.bi $rt, [$sp], (imm15s<<2) */
> +	      continue;
> +	    }
> +	  else if (N32_OP6 (insn) == N32_OP6_LSMW && (insn & __BIT (5)))
> +	    {
> +	      /* bit-5 for SMW */
> +
> +	      /* smwa?.(a|b)(d|i)m? rb,[ra],re,enable4 */
> +
> +	      int rb, re, ra, enable4, i;
> +	      int aligned;
> +	      int m = 0;
> +	      int di;		/* dec=-1 or inc=1 */
> +	      char enb4map[2][4] = {
> +		  {0, 1, 2, 3} /* smw */,
> +		  {3, 1, 2, 0} /* smwa */};
> +	      LONGEST base = ~1 + 1;
> +
> +	      rb = N32_RT5 (insn);
> +	      ra = N32_RA5 (insn);
> +	      re = N32_RB5 (insn);
> +	      enable4 = (insn >> 6) & 0x0F;
> +	      aligned = (insn & 3) ? 1 : 0;
> +	      di = (insn & (1 << 3)) ? -1 : 1;
> +
> +	      switch (ra)
> +		{
> +		case NDS32_FP_REGNUM:
> +		case NDS32_SP_REGNUM:
> +		  cpc = pc;
> +		  continue; /* found and continue */
> +		default:
> +		  break;
> +		}
> +	    }
> +
> +	  if (N32_OP6 (insn) == N32_OP6_COP && N32_COP_CP (insn) == 0
> +	      && (N32_COP_SUB (insn) == N32_FPU_FSS
> +		  || N32_COP_SUB (insn) == N32_FPU_FSD)
> +	      && (N32_RA5 (insn) == REG_SP || N32_RA5 (insn) == REG_FP))
> +	    {
> +	      /* CP shoud be CP0 */
> +	      /* fs[sd][.bi] $fst, [$sp + ($r0 << sv)] */
> +	      continue;
> +	    }
> +
> +	  /* fssi    $fst, [$ra + (imm12s << 2)]
> +	     fssi.bi $fst, [$ra], (imm12s << 2)
> +	     fsdi    $fdt, [$ra + (imm12s << 2)]
> +	     fsdi.bi $fdt, [$ra], (imm12s << 2) */
> +	  if ((N32_OP6 (insn) == N32_OP6_SWC || N32_OP6 (insn) == N32_OP6_SDC)
> +	      && (N32_RA5 (insn) == REG_SP || N32_RA5 (insn) == REG_FP))
> +	    {
> +	      /* BI bit is dont-care.  */
> +	      continue;
> +	    }
> +
> +	  pc -= 4;
> +	  break;
> +	}
> +      else
> +	{
> +	  /* 16-bit instruction */
> +	  pc += 2;
> +	  insn >>= 16;
> +
> +	  /* 1. If the instruction is j/b, then we stop
> +		i.e., OP starts with 10, and beqzs8, bnezs8.
> +	     2. If the operations will change sp/fp or based on sp/fp,
> +		then we are in the prologue.
> +	     3. If we don't know what's it, then stop.  */
> +
> +	  if (CHOP_BITS (insn, 10) == N16_TYPE10 (ADDI10S, 0))
> +	    {
> +	      /* addi10s */
> +	      continue;
> +	    }
> +	  else if (__GF (insn, 7, 8) == N16_T25_PUSH25)
> +	    {
> +	      /* push25 */
> +	      continue;
> +	    }
> +	  else if (insn == N16_TYPE55 (MOV55, REG_FP, REG_SP))
> +	    {
> +	      /* mov55 fp, sp */
> +	      continue;
> +	    }
> +
> +	  /* swi450 */
> +	  switch (insn & ~__MF (-1, 5, 4))
> +	    {
> +	      case N16_TYPE45 (SWI450, 0, REG_SP):
> +	      case N16_TYPE45 (SWI450, 0, REG_FP):
> +		break;
> +	    }
> +	  /* swi37 - implied fp */
> +	  if (__GF (insn, 11, 4) == N16_T37_XWI37
> +	      && (insn & __BIT (7)))
> +	    continue;
> +
> +	  /* swi37sp - implied */
> +	  if (__GF (insn, 11, 4) == N16_T37_XWI37SP
> +	      && (insn & __BIT (7)))
> +	    continue;
> +
> +	  /* If the a instruction is not accepted,
> +	     don't go futher.  */
> +	  pc -= 2;
> +	  break;
> +	}
> +    }
> +
> +  if (pc >= scan_limit)
> +    {
> +      /* If we can not find end of prologue before scan_limit,
> +	 we assume that end of prologue is on pc_after_stack_adject. */
> +      if (cpc != -1)
> +	pc = cpc;
> +    }
> +
> +  *pl_endptr = pc;
> +
> +  return 0;
> +}
> +
> +/* Implement the gdbarch_skip_prologue method.
> +
> +   Find the end of function prologue.  */
> +
> +static CORE_ADDR
> +nds32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
> +{
> +  CORE_ADDR func_addr, func_end;
> +  struct symtab_and_line sal = { 0 };
> +  LONGEST return_value;
> +  const char *func_name;
> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> +  const int search_limit = 128;
> +
> +  /* See what the symbol table says */
> +  if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end))
> +    {
> +      sal = find_pc_line (func_addr, 0);
> +
> +      if (sal.line != 0 && sal.end <= func_end)
> +	{
> +	  func_end = sal.end;
> +	}

Unnecessary brackets.

> +      else
> +	{
> +	  /* Either there's no line info, or the line after the prologue
> +	     is after the end of the function.  In this case, there probably
> +	     isn't a prologue.  */
> +	  func_end = min (func_end, func_addr + search_limit);
> +	}
> +    }
> +  else
> +    func_end = pc + search_limit;
> +
> +  /* If current instruction is not readable, just quit.  */
> +  if (!safe_read_memory_integer (pc, 4, byte_order, &return_value))
> +    return pc;
> +
> +  /* Find the end of prologue.  */
> +  if (nds32_analyze_prologue (gdbarch, pc, func_end, &sal.end) < 0)
> +    return pc;
> +
> +  return sal.end;
> +}
> +
> +struct nds32_unwind_cache
> +{
> +  /* The previous frame's inner most stack address.
> +     Used as this frame ID's stack_addr.  */
> +  CORE_ADDR prev_sp;
> +
> +  /* The frame's base, optionally used by the high-level debug info.  */
> +  CORE_ADDR base;
> +  int size;
> +
> +  /* How far the SP and FP have been offset from the start of
> +     the stack frame (as defined by the previous frame's stack
> +     pointer).  */
> +  LONGEST sp_offset;
> +  LONGEST fp_offset;
> +  int use_frame;
> +
> +  /* Table indicating the location of each and every register.  */
> +  struct trad_frame_saved_reg *saved_regs;
> +};
> +
> +static struct nds32_unwind_cache *
> +nds32_alloc_frame_cache (struct frame_info *this_frame)
> +{
> +  struct nds32_unwind_cache *cache;

Blank line is needed here.

> +  cache = FRAME_OBSTACK_ZALLOC (struct nds32_unwind_cache);
> +
> +  cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
> +  cache->size = 0;
> +  cache->sp_offset = 0;
> +  cache->fp_offset = 0;
> +  cache->use_frame = 0;
> +  cache->base = 0;
> +
> +  return cache;
> +}
> +
> +/* Implement the gdbarch_in_function_epilogue_p method.  */
> +
> +static int
> +nds32_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR addr)
> +{
> +  uint32_t insn;
> +  int r = 0;
> +
> +  insn = read_memory_unsigned_integer (addr, 4, BFD_ENDIAN_BIG);
> +  if ((insn & 0x80000000) == 0)
> +    {
> +      /* 32-bit instruction */
> +
> +      /* ret */
> +      if (insn == N32_JREG (JR, 0, REG_LP, 0, 1))
> +	r = 1;
> +      /* iret */
> +      else if (insn == N32_TYPE0 (MISC, N32_MISC_IRET))
> +	r = 2;
> +    }
> +  else
> +    {
> +      if (insn == N16_TYPE5 (RET5, REG_LP))
> +	r = 3;
> +    }
> +  return r > 0;
> +}
> +
> +/* Put here the code to store, into fi->saved_regs, the addresses of
> +   the saved registers of frame described by FRAME_INFO.  This
> +   includes special registers such as pc and fp saved in special ways
> +   in the stack frame.  sp is even more special: the address we return
> +   for it IS the sp for the next frame.  */
> +
> +static struct nds32_unwind_cache *
> +nds32_frame_unwind_cache (struct frame_info *this_frame,
> +			  void **this_prologue_cache)
> +{
> +  CORE_ADDR pc, scan_limit;
> +  ULONGEST prev_sp;
> +  ULONGEST next_base;
> +  ULONGEST fp_base;
> +  int i;
> +  int insn;
> +  struct nds32_unwind_cache *info;
> +  struct gdbarch *gdbarch = get_frame_arch (this_frame);
> +
> +  if ((*this_prologue_cache))
> +    return (*this_prologue_cache);
> +
> +  info = nds32_alloc_frame_cache (this_frame);
> +
> +  info->base = get_frame_register_unsigned (this_frame, NDS32_FP_REGNUM);
> +  (*this_prologue_cache) = info;
> +
> +  if (info->base == 0)
> +    return info;
> +
> +  pc = get_frame_func (this_frame);
> +  scan_limit = get_frame_pc (this_frame);
> +
> +  for (; pc > 0 && pc < scan_limit; )
> +    {
> +      insn = read_memory_unsigned_integer (pc, 4, BFD_ENDIAN_BIG);
> +
> +      if ((insn & 0x80000000) == 0)
> +	{
> +	  /* 32-bit instruction */
> +
> +	  pc += 4;
> +	  if (insn == N32_ALU1 (ADD, REG_GP, REG_TA, REG_GP))
> +	    {
> +	      /* add $gp, $ta, $gp */
> +	      continue;
> +	    }
> +	  if (N32_OP6 (insn) == N32_OP6_ADDI)
> +	    {
> +	      int rt = N32_RT5 (insn);
> +	      int ra = N32_RA5 (insn);
> +	      int imm15s = N32_IMM15S (insn);
> +
> +	      if (rt == ra && rt == NDS32_SP_REGNUM)
> +		{
> +		  info->sp_offset += imm15s;
> +		  continue;
> +		}
> +	      else if (rt == NDS32_FP_REGNUM && ra == NDS32_SP_REGNUM)
> +		{
> +		  info->fp_offset = info->sp_offset + imm15s;
> +		  info->use_frame = 1;
> +		  continue;
> +		}
> +	      else if (rt == ra)
> +		/* Prevent stop analyzing form iframe.  */
> +		continue;
> +	    }
> +
> +	  if (insn == N32_ALU2 (MFUSR, REG_TA, 31, 0))
> +	    {
> +	      /* mfusr $ta, PC  ; group=0, sub=0x20=mfusr */
> +	      continue;
> +	    }
> +	  if (CHOP_BITS (insn, 20) == N32_TYPE1 (MOVI, REG_TA, 0))
> +	    {
> +	      /* movi $ta, imm20s */
> +	      continue;
> +	    }
> +	  if (CHOP_BITS (insn, 20) == N32_TYPE1 (SETHI, REG_GP, 0))
> +	    {
> +	      /* sethi $gp, imm20 */
> +	      continue;
> +	    }
> +	  if (CHOP_BITS (insn, 15) == N32_TYPE2 (ORI, REG_GP, REG_GP, 0))
> +	    {
> +	      /* ori $gp, $gp, imm15 */
> +	      continue;
> +	    }
> +	  if (N32_OP6 (insn) == N32_OP6_LSMW && (insn & __BIT (5)))
> +	    {
> +	      /* smwa?.(a|b)(d|i)m? rb,[ra],re,enable4 */
> +
> +	      int rb, re, ra, enable4, i;
> +	      int aligned;
> +	      int m = 0;
> +	      int di;	   /* dec=-1 or inc=1 */
> +	      int rn;	   /* number of registers.  */
> +	      char enb4map[2][4] = {
> +		  {0, 1, 2, 3} /* smw */,
> +		  {3, 1, 2, 0} /* smwa */ };
> +	      /* `base' is the highest/last address for access memory.
> +		 e.g., [ lp ] ___ base shoule be here.
> +		       [ fp ]
> +		       [ r6 ] */
> +	      LONGEST base = ~1 + 1;
> +
> +	      rb = N32_RT5 (insn);
> +	      ra = N32_RA5 (insn);
> +	      re = N32_RB5 (insn);
> +	      enable4 = (insn >> 6) & 0x0F;
> +	      aligned = (insn & 3) ? 1 : 0;
> +	      di = (insn & (1 << 3)) ? -1 : 1;
> +
> +	      rn = 0;
> +	      rn += (enable4 & 0x1) ? 1 : 0;
> +	      rn += (enable4 & 0x2) ? 1 : 0;
> +	      rn += (enable4 & 0x4) ? 1 : 0;
> +	      rn += (enable4 & 0x8) ? 1 : 0;
> +	      if (rb < NDS32_FP_REGNUM && re < NDS32_FP_REGNUM)
> +		{
> +		  /* reg-list should not include fp,gp,lp,sp
> +		     ie, the rb==re==sp case, anyway... */
> +		  rn += (re - rb) + 1;
> +		}
> +
> +	      /* Let's consider how Ra should update.  */
> +	      if (insn & (1 << 0x2))    /* m-bit is set */
> +		{
> +		  m = rn * 4;			/* 4*TNReg */
> +		}
> +	      else
> +		m = 0;	  /* don't update Ra */
> +
> +	      switch (ra)
> +		{
> +		case NDS32_FP_REGNUM:
> +		  base = info->fp_offset;
> +		  info->fp_offset += m * di;
> +		  break;
> +		case NDS32_SP_REGNUM:
> +		  base = info->sp_offset;
> +		  info->sp_offset += m * di;
> +		  break;
> +		default:
> +		  /* sorry, only ra==sp || ra==fp is handled */
> +		  break;
> +		}
> +	      if (base == ~1 + 1)
                           ^^^^^^^ ??

> +		break;	  /* skip */
> +
> +	      if (insn & (1 << 0x4))	/* b:0, a:1 */
> +		base += 4 * di;		/* a: use Ra+4 (for i),
> +					      or Ra-4 (for d) */
> +	      /* else base = base;	b use Ra */
> +
> +	      /* Cole 3th Nov. 2010
> +		 We should consider both increasing and decreasing case.
> +
> +		 Either case stores registers in the same order.
> +		 To simplify the code (yes, the loops),
> +		 I used the same pushing order, but from different side.  */
> +
> +	      if (di == 1)		/* Increasing.  */
> +		base += (rn * 4 - 4);
> +	      /* else, in des case, we already are on the top */
> +
> +	      for (i = 0; i < 4; i++)
> +		{
> +		  if (enable4 & (1 << enb4map[aligned][i]))
> +		    {
> +		      info->saved_regs[NDS32_SP_REGNUM -
> +				       (enb4map[aligned][i])].addr = base;
> +		      base -= 4;
> +		    }
> +		}
> +
> +	      /* Skip re == rb == sp > fp.  */
> +	      for (i = re; i >= rb && rb < NDS32_FP_REGNUM; i--)
> +		{
> +		  info->saved_regs[i].addr = base;
> +		  base -= 4;
> +		}
> +
> +	      continue;
> +	    }
> +	  /* swi $lp, [$sp + (imm15s << 2)] */
> +	  /* We must check if $rt is $lp to determine it is
> +	     in prologue or not.  */
> +	  if (CHOP_BITS (insn, 15) == N32_TYPE2 (SWI, REG_LP, REG_FP, 0))
> +	    {
> +	      int imm15s;
> +
> +	      /* swi $lp, [$sp + (imm15s<<2)] */
> +	      imm15s = N32_IMM15S (insn);
> +	      info->saved_regs[NDS32_LP_REGNUM].addr = info->sp_offset
> +						       + (imm15s << 2);
> +	      continue;
> +	    }
> +	  /* swi.bi $rt, [$sp], (imm15s << 2) */
> +	  if (CHOP_BITS (insn, 15) == N32_TYPE2 (SWI_BI, REG_LP, REG_FP, 0))
> +	    {
> +	      unsigned int rt5 = 0;
> +	      unsigned int ra5 = 0;
> +	      int imm15s = 0;
> +	      rt5 = N32_RT5 (insn);
> +	      ra5 = N32_RA5 (insn);
> +	      imm15s = N32_IMM15S (insn);
> +
> +	      if (ra5 == NDS32_SP_REGNUM)
> +		{
> +		  info->saved_regs[rt5].addr = info->sp_offset;
> +		  info->sp_offset += (imm15s << 2);
> +		}
> +	      else if (ra5 == NDS32_FP_REGNUM)
> +		{
> +		  info->saved_regs[rt5].addr = info->fp_offset;
> +		  info->fp_offset += (imm15s << 2);
> +		}
> +	      continue;
> +	    }
> +
> +	  if (N32_OP6 (insn) == N32_OP6_COP && N32_COP_CP (insn) == 0
> +	      && (N32_COP_SUB (insn) == N32_FPU_FSS
> +		  || N32_COP_SUB (insn) == N32_FPU_FSD)
> +	      && (N32_RA5 (insn) == REG_SP || N32_RA5 (insn) == REG_FP))
> +	    {
> +	      /* CP shoud be CP0 */
> +	      /* fs[sd][.bi] $fst, [$sp + ($r0 << sv)] */
> +	      continue;
> +	    }
> +
> +	  /* fssi $fst, [$ra + (imm12s << 2)]
> +	     fssi.bi $fst, [$ra], (imm12s << 2)
> +	     fsdi $fdt, [$ra + (imm12s << 2)]
> +	     fsdi.bi $fdt, [$ra], (imm12s << 2) */
> +	  if ((N32_OP6 (insn) == N32_OP6_SWC || N32_OP6 (insn) == N32_OP6_SDC)
> +	      && (N32_RA5 (insn) == REG_SP || N32_RA5 (insn) == REG_FP))
> +	    {
> +	      /* fssi and fsdi have the same form.  */
> +	      /* Only .bi form should be handled to adjust reg.  */
> +	      unsigned int ra5 = 0;
> +	      unsigned int fs5 = 0;
> +	      int imm12s = 0;
> +
> +	      fs5 = N32_RT5 (insn);
> +	      ra5 = N32_RA5 (insn);
> +	      imm12s = N32_IMM12S (insn);
> +
> +	      if (imm12s & 0x800)
> +		imm12s = (imm12s - (0x800 << 1));
> +
> +	      switch (ra5)
> +		{
> +		case NDS32_FP_REGNUM:
> +		  info->fp_offset += (imm12s << 2);
> +		  break;
> +		case NDS32_SP_REGNUM:
> +		  info->sp_offset += (imm12s << 2);
> +		  break;
> +		}
> +
> +	      continue;
> +	    }
> +
> +	  /* TODO: Handle mfsr and addi for interrupt handlers.  */

Need to handle mfsr and addi now?

> +	  break;
> +	}
> +      else
> +	{
> +	  /* 16-bit instruction */
> +	  pc += 2;
> +	  insn >>= 16;
> +
> +	  /* 1. If the instruction is j/b, then we stop
> +		i.e., OP starts with 10, and beqzs8, bnezs8.
> +	     2. If the operations will change sp/fp or based on sp/fp,
> +		then we are in the prologue.
> +	     3. If we don't know what's it, then stop.  */
> +
> +	  if (__GF (insn, 13, 2) == 2)
> +	    {
> +	      /* These are all branch instructions.  */
> +	      pc -= 2;
> +	      break;
> +	    }
> +	  else if (__GF (insn, 9, 6) == 0x34)
> +	    {
> +	      /* beqzs8, bnezs8 */
> +	      pc -= 2;
> +	      break;
> +	    }
> +
> +	  if (CHOP_BITS (insn, 10) == N16_TYPE10 (ADDI10S, 0))
> +	    {
> +	      /* addi10s */
> +	      info->sp_offset += N16_IMM10S (insn);
> +	      continue;
> +	    }
> +
> +	  if (__GF (insn, 7, 8) == N16_T25_PUSH25)
> +	    {
> +	      /* push25 */
> +	      int imm8u = (insn & 0x1f) << 3;
> +	      int re = ((insn & 0x60) >> 5) & 0x3;
> +	      int res[] = {6, 8, 10, 14};
> +	      int m[] = {4, 6, 8, 12};
> +	      LONGEST base = info->sp_offset - 4;
> +
> +	      /* Operation 1 - smw.adm R6, [sp], Re, #0xe */
> +	      info->saved_regs[NDS32_LP_REGNUM].addr = info->sp_offset - 0x4;
> +	      info->saved_regs[NDS32_GP_REGNUM].addr = info->sp_offset - 0x8;
> +	      info->saved_regs[NDS32_FP_REGNUM].addr = info->sp_offset - 0xC;
> +	      info->sp_offset -= m[re] * 4;
> +
> +	      switch (re)
> +		{
> +		  case 3:
> +		    info->saved_regs[NDS32_R0_REGNUM + 14].addr = info->sp_offset + 0x20;
> +		    info->saved_regs[NDS32_R0_REGNUM + 13].addr = info->sp_offset + 0x1C;
> +		    info->saved_regs[NDS32_R0_REGNUM + 12].addr = info->sp_offset + 0x18;
> +		    info->saved_regs[NDS32_R0_REGNUM + 11].addr = info->sp_offset + 0x14;
> +		  case 2:
> +		    info->saved_regs[NDS32_R0_REGNUM + 10].addr = info->sp_offset + 0x10;
> +		    info->saved_regs[NDS32_R0_REGNUM + 9].addr = info->sp_offset + 0xC;
> +		  case 1:
> +		    info->saved_regs[NDS32_R0_REGNUM + 8].addr = info->sp_offset + 0x8;
> +		    info->saved_regs[NDS32_R0_REGNUM + 7].addr = info->sp_offset + 0x4;
> +		  case 0:
> +		    info->saved_regs[NDS32_R0_REGNUM + 6].addr = info->sp_offset;
> +		}
> +
> +	      /* Operation 2 - sp = sp - imm5u<<3 */
> +	      info->sp_offset -= imm8u;
> +
> +	      /* Operation 3 - if (Re >= 1) R8 = concat (PC(31,2), 2`b0) */
> +	      continue;
> +	    }
> +
> +	  /* mov55 fp, sp */
> +	  if (insn == N16_TYPE55 (MOV55, REG_FP, REG_SP))
> +	    {
> +		info->fp_offset = info->sp_offset;
> +		info->use_frame = 1;
> +		continue;
> +	    }
> +	  /* swi450 */
> +	  switch (insn & ~__MF (-1, 5, 4))
> +	    {
> +	      case N16_TYPE45 (SWI450, 0, REG_SP):
> +	      case N16_TYPE45 (SWI450, 0, REG_FP):
> +		break;
> +	    }
> +	  /* swi37 - implied fp */
> +	  if (__GF (insn, 11, 4) == N16_T37_XWI37
> +	      && (insn & __BIT (7)))
> +	    continue;
> +
> +	  /* swi37sp - implied */
> +	  if (__GF (insn, 11, 4) == N16_T37_XWI37SP
> +	      && (insn & __BIT (7)))
> +	    continue;
> +
> +	  break;
> +	  }
> +    }
> +
> +  info->size = -info->sp_offset;
> +  /* Compute the previous frame's stack pointer (which is also the
> +     frame's ID's stack address), and this frame's base pointer.
> +
> +     Assume that the FP is this frame's SP but with that pushed
> +     stack space added back.  */
> +  next_base = get_frame_register_unsigned (this_frame, NDS32_SP_REGNUM);
> +  prev_sp = next_base + info->size;
> +  fp_base = get_frame_register_unsigned (this_frame, NDS32_FP_REGNUM);
> +  if (info->use_frame && fp_base > 0)
> +    {
> +      /* Try to use FP if possible. */
> +      prev_sp = fp_base - info->fp_offset;
> +    }
> +
> +  /* Convert that SP/BASE into real addresses.  */
> +  info->prev_sp = prev_sp;
> +  info->base = next_base;
> +
> +  /* Adjust all the saved registers so that they contain addresses and
> +     not offsets.  */
> +  for (i = 0; i < gdbarch_num_regs (gdbarch) - 1; i++)
> +    {
> +      if (trad_frame_addr_p (info->saved_regs, i))
> +	{
> +	  info->saved_regs[i].addr = info->prev_sp + info->saved_regs[i].addr;
> +	}
> +    }
> +
> +  /* The previous frame's SP needed to be computed.
> +     Save the computed value.  */
> +  trad_frame_set_value (info->saved_regs, NDS32_SP_REGNUM, prev_sp);
> +
> +  return info;
> +}
> +


> +/* Implement the gdbarch_skip_permanent_breakpoint method.  */
> +
> +static void
> +nds32_skip_permanent_breakpoint (struct regcache *regcache)
> +{
> +  int insn;
> +  CORE_ADDR current_pc = regcache_read_pc (regcache);
> +
> +  /* On nds32, breakpoint may be BREAK or BREAK16.  */
> +  insn = read_memory_unsigned_integer (current_pc, 4, BFD_ENDIAN_BIG);
> +
> +  /* FIXME: Review this code.  */
> +  if (N32_OP6 (insn) == N32_OP6_MISC && N32_SUB5 (insn) == N32_MISC_BREAK)
> +    current_pc += 4;
> +  else if (__GF (insn, 9, 6) == 35 && N16_IMM9U (insn) < 32)
> +    current_pc += 2;
> +  else
> +    return;

Looks like there are two kinds of breakpoint instructions, 16-bit one 
and 32-bit one.  It is inconsistent with nds32_breakpoint_from_pc, where 
only 16-bit breakpoint instruction is used.

> +
> +  regcache_write_pc (regcache, current_pc);
> +}
> +

> +
> +/* Implement the gdbarch_push_dummy_call method.  */
> +
> +static CORE_ADDR
> +nds32_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
> +		       struct regcache *regcache, CORE_ADDR bp_addr,
> +		       int nargs, struct value **args, CORE_ADDR sp,
> +		       int struct_return, CORE_ADDR struct_addr)
> +{
> +  const int REND = 6;		/* Max arguments number.  */
> +  int goff = 0;			/* Current gpr for argument.  */
> +  int foff = 0;			/* Currnet gpr for argument.  */
> +  int soff = 0;			/* Current stack offset.  */
> +  int i;
> +  struct type *type;
> +  enum type_code typecode;
> +  CORE_ADDR regval;
> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> +  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
> +  int use_spill;
> +  int use_fpr;
> +  int fs0_regnum = -1, fd0_regnum = -1;
> +
> +  switch (tdep->nds32_abi)
> +    {
> +    case NDS32_ABI_V2:
> +    case NDS32_ABI_V2FP:
> +      use_spill = FALSE;
> +      break;
> +    default:
> +      use_spill = TRUE;
> +    break;
> +  }
> +
> +  /* Use FP registers for calling iff when ABI==V2FP.  */
> +  use_fpr = (tdep->nds32_abi == NDS32_ABI_V2FP);
> +  if (use_fpr)
> +    {
> +      fs0_regnum = user_reg_map_name_to_regnum (gdbarch, "fs0",
> strlen ("fs0"));
> +      fd0_regnum = user_reg_map_name_to_regnum (gdbarch, "fd0",
> strlen ("fd0"));
> +    }
> +
> +  /* Set the return address.  For the nds32, the return breakpoint is
> +     always at BP_ADDR.  */
> +  regcache_cooked_write_unsigned (regcache, NDS32_LP_REGNUM, bp_addr);
> +
> +  /* If STRUCT_RETURN is true, then the struct return address (in
> +     STRUCT_ADDR) will consume the first argument-passing register.
> +     Both adjust the register count and store that value.  */
> +  if (struct_return)
> +    {
> +      regcache_cooked_write_unsigned (regcache, NDS32_R0_REGNUM, struct_addr);
> +      goff++;
> +    }
> +
> +  /* Now make sure there's space on the stack */
> +  for (i = 0; i < nargs; i++)
> +    {
> +      struct type *type = value_type (args[i]);
> +      int align = nds32_type_align (type);
> +
> +      gdb_assert (align != 0);
> +      sp -= TYPE_LENGTH (type);
> +      if (align)
> +	{
> +	  /* FIXME: Handle empty structure?  */
> +	  sp &= ~(align - 1);
> +	}
> +    }
> +
> +  /* Stack must be 8-byte aligned.  */
> +  sp = sp & ~7;

I prefer using align_down (sp, 8) here.

> +
> +/* Given a return value in `regbuf' with a type `valtype',
> +   extract and copy its value into `valbuf'.  */

The comment is inconsistent with the parameters of this functions.

> +
> +static void
> +nds32_extract_return_value (struct type *type, struct regcache *regcache,
> +			    gdb_byte *readbuf)
> +{
> +  int len = TYPE_LENGTH (type);
> +  int typecode = TYPE_CODE (type);
> +  struct gdbarch *gdbarch = get_regcache_arch (regcache);
> +  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
> +  int i;
> +  int fs0_regnum, fd0_regnum;
> +  int use_fpr;
> +
> +  use_fpr = (tdep->nds32_abi == NDS32_ABI_V2FP);
> +
> +  /* TODO: one-float, one-double is special case in V2FP.
> +     Passed in FS/FD */
> +  gdb_assert (TYPE_LENGTH (type) <= 8);
> +  if (nds32_float_in_struct (type))
> +    typecode = TYPE_CODE_FLT;
> +
> +  if (typecode == TYPE_CODE_FLT && use_fpr)
> +    {
> +      fs0_regnum = user_reg_map_name_to_regnum (gdbarch, "fs0",
> strlen ("fs0"));
> +      fd0_regnum = user_reg_map_name_to_regnum (gdbarch, "fd0",
> strlen ("fd0"));
> +

Looks your mailer wrap up your patch, so that it can't be applied cleanly.

> +
> +static int
> +nds32_sigtramp_frame_sniffer (const struct frame_unwind *self,
> +			      struct frame_info *this_frame,
> +			      void **this_prologue_cache)
> +{
> +  struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (this_frame));
> +
> +  /* We shouldn't even bother if we don't have a sigcontext_addr
> +     handler.  */
> +  if (tdep->sigcontext_addr == NULL)
> +    return 0;
> +
> +  if (tdep->sigtramp_p != NULL)
> +    {
> +      if (tdep->sigtramp_p (this_frame))
> +	return 1;
> +    }
> +
> +#if 0
> +    /* TODO: extend the sniffer as following if (tdep->sigtramp_start != 0) */
> +    {
> +      CORE_ADDR pc = frame_pc_unwind (this_frame);
> +
> +      gdb_assert (tdep->sigtramp_end != 0);
> +      if (pc >= tdep->sigtramp_start && pc < tdep->sigtramp_end)
> +	return &nds32_sigtramp_frame_unwind;
> +    }
> +#endif

Remove it?

> +  return 0;
> +}
> +

> +
> +static int
> +gdb_print_insn_nds32 (bfd_vma memaddr, disassemble_info *info)
> +{
> +  struct gdbarch *gdbarch = info->application_data;
> +  struct obj_section * s = find_pc_section (memaddr);
> +  struct cleanup *back_to;
> +
> +  /* Reload symtab if abfd changed.

I am sorry that I can't get the points of doing this.  Can you elaborate 
please?

> +     In case there are multiple ITB in different shared objects.  */
> +  if (s == NULL || info->section != s->the_bfd_section)
> +    {
> +      xfree (info->symtab);
> +      info->symtab = NULL;
> +      info->symtab_size = 0;
> +    }
> +
> +  if (info->symtab == NULL && s && s->the_bfd_section)
> +    {
> +      long storage = bfd_get_symtab_upper_bound (s->objfile->obfd);
> +
> +      if (storage <= 0)
> +	goto done;
> +
> +      info->section = s->the_bfd_section;
> +      info->symtab = xmalloc (storage);
> +      info->symtab_size =
> +	bfd_canonicalize_symtab (s->the_bfd_section->owner, info->symtab);
> +    }
> +
> +done:
> +  return print_insn_nds32 (memaddr, info);
> +}
> +

> +
> +/* Callback for "nds32 dump" command.
> +
> +   Dump current register and stack for debug gdb.  */
> +

Do we really need this command?  You can examine registers via command 
'info registers', and examine stack as a piece of memory via command 'x'.

> +static void
> +nds32_dump_command (char *arg, int from_tty)
> +{
> +  ULONGEST val;
> +  ULONGEST sp;
> +  FILE *f_script;
> +  char cmdline[512];
> +  int i;
> +
> +  if (arg == NULL)
> +    {
> +      printf_unfiltered (_("Missing filename argument.\n"));
> +      return;
> +    }
> +
> +  regcache_raw_read_unsigned (get_current_regcache (), NDS32_SP_REGNUM, &sp);
> +
> +  sprintf (cmdline, "dump binary memory %s.stack 0x%lx 0x%lx",
> +	   arg, (long) sp, ((long) sp + 1024 - 1) & ~(1024 - 1));
> +  execute_command (cmdline, from_tty);
> +
> +  sprintf (cmdline, "%s.gdbinit", arg);
> +  f_script = fopen (cmdline, "w");
> +  if (f_script == NULL)
> +    {
> +      printf_unfiltered (_("Fail to generate dump .gdbinit."));
> +      return ;
> +    }
> +
> +  /* Gather all user registers.  */
> +  for (i = 0; i <= NDS32_D1HI_REGNUM; i++)
> +    {
> +      regcache_raw_read_unsigned (get_current_regcache (), i, &val);
> +      fprintf (f_script, "set $%s = 0x%lx\n", nds32_regnames[i], (long) val);
> +    }
> +
> +  fprintf (f_script, "restore %s.stack binary 0x%lx\n", arg, (long) sp);
> +  fclose (f_script);
> +}
> +
> +static int
> +nds32_config_int (const char *str, int def)
> +{
> +  int val = def;
> +
> +  if (getenv (str))
> +    val = atoi (getenv (str));
> +  if (val != def)
> +    printf ("%s=%d\n", str, val);

It is not good to print in this way.  If you really need to print some 
debug message, please add a flag "nds32_debug",

   if (nds32_debug)
     fprintf_unfiltered (gdb_stdlog, "%s = %d\n", str, val));

and add a command to contrl this flag.

See 'arm_debug' in arm-tdep.c.

> +  return val;
> +}
> +
> +static void
> +nds32_load_config (struct nds32_gdb_config *config)
> +{
> +  config->use_cfi = nds32_config_int ("USE_CFI", 1);
> +  config->use_abi = nds32_config_int ("USE_ABI", NDS32_ABI_AUTO);
> +}
> +
> +/* Callback for "nds32" command.  */
> +
> +static void
> +nds32_command (char *arg, int from_tty)
> +{
> +  printf_unfiltered (_("\"nds32\" must be followed by arguments\n"));
> +}
> +
> +struct cmd_list_element *nds32_cmdlist;
> +
> +void
> +_initialize_nds32_tdep (void)
> +{
> +  /* Internal used config for testing.  */
> +  nds32_load_config (&nds32_config);
> +
> +  add_prefix_cmd ("nds32", no_class, nds32_command,
> +		  _("Various nds32-specific commands."), &nds32_cmdlist,
> +		  "nds32 ", 0, &cmdlist);
> +
> +  add_cmd ("dump", class_files, nds32_dump_command,
> +	   _("dump stack and GPRs for debugging"), &nds32_cmdlist);

Not sure the command is necessary.  If it is, don't forget to update 
gdb/doc/gdb.texinfo about the new command.

> +
> +  nds32_init_remote_cmds ();
> +
> +  /* Initialize gdbarch.  */
> +  register_gdbarch_init (bfd_arch_nds32, nds32_gdbarch_init);
> +
> +  /* Following are NDS32 specific commands.  */
> +
> +  nds32_init_reggroups ();
> +
> +  register_remote_support_xml ("nds32");
> +}
> diff --git a/gdb/nds32-tdep.h b/gdb/nds32-tdep.h
> new file mode 100644
> index 0000000..2a71a14
> --- /dev/null
> +++ b/gdb/nds32-tdep.h
> @@ -0,0 +1,127 @@
> +/* Common target dependent code for GDB on nds32 systems.
> +

> +
> +/* All the possible NDS32 ABIs.  They must be consistent with elf/nds32.h.  */
> +enum nds32_abi
> +{
> +  NDS32_ABI_V0 = 0,
> +  NDS32_ABI_V1,
> +  NDS32_ABI_V2,
> +  NDS32_ABI_V2FP,
> +  NDS32_ABI_AABI,
> +  NDS32_ABI_END,
> +  NDS32_ABI_BEGIN = NDS32_ABI_V0,
> +  /* ABI flag is only 4-bits long.  */
> +  NDS32_ABI_AUTO = 0xFFFFFFFF
> +};

The ABI stuff should go to binutils, and GDB only uses them, right?

> +
> +/* ----------------------------------------------
> +   31   28 27                 8 7   4 3       0
> +   ----------------------------------------------
> +   | ARCH | CONFUGURAION FIELD | ABI | VERSION  |
> +   ---------------------------------------------- */

What does this comment mean?

The last but not least, the binutils patch hasn't go in yet, so it is 
pity that I can't build GDB with you patches for nds32 target.

You also need a news entry in gdb/NEWS about this new port.  Open 
gdb/NEWS, and then you will know how to add one entry for nds32 port :)

-- 
Yao (齐尧)


  parent reply	other threads:[~2013-07-10  5:38 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-07-08  9:28 Wei-cheng Wang
2013-07-08 23:58 ` Joseph S. Myers
2013-07-09 16:18   ` Wei-cheng Wang
2013-07-09  4:10 ` Yao Qi
2013-07-09 18:23   ` Wei-cheng Wang
2013-07-25  2:35     ` Yao Qi
2013-07-10  5:38 ` Yao Qi [this message]
2013-07-11 14:06   ` Wei-cheng Wang
2013-07-11 14:26   ` Wei-cheng Wang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=51DCF32E.9020303@codesourcery.com \
    --to=yao@codesourcery.com \
    --cc=cole945@gmail.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox