| /* Target-dependent code for the LoongArch architecture, for GDB. | 
 |  | 
 |    Copyright (C) 2022 Free Software Foundation, Inc. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program 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. | 
 |  | 
 |    This program 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.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include "defs.h" | 
 | #include "arch-utils.h" | 
 | #include "dwarf2/frame.h" | 
 | #include "elf-bfd.h" | 
 | #include "frame-unwind.h" | 
 | #include "gdbcore.h" | 
 | #include "loongarch-tdep.h" | 
 | #include "target.h" | 
 | #include "target-descriptions.h" | 
 | #include "trad-frame.h" | 
 | #include "user-regs.h" | 
 |  | 
 | /* Fetch the instruction at PC.  */ | 
 |  | 
 | static insn_t | 
 | loongarch_fetch_instruction (CORE_ADDR pc) | 
 | { | 
 |   size_t insn_len = loongarch_insn_length (0); | 
 |   gdb_byte buf[insn_len]; | 
 |   int err; | 
 |  | 
 |   err = target_read_memory (pc, buf, insn_len); | 
 |   if (err) | 
 |     memory_error (TARGET_XFER_E_IO, pc); | 
 |  | 
 |   return extract_unsigned_integer (buf, insn_len, BFD_ENDIAN_LITTLE); | 
 | } | 
 |  | 
 | /* Return TRUE if INSN is a unconditional branch instruction, otherwise return FALSE.  */ | 
 |  | 
 | static bool | 
 | loongarch_insn_is_uncond_branch (insn_t insn) | 
 | { | 
 |   if ((insn & 0xfc000000) == 0x4c000000		/* jirl  */ | 
 |       || (insn & 0xfc000000) == 0x50000000	/* b     */ | 
 |       || (insn & 0xfc000000) == 0x54000000)	/* bl    */ | 
 |     return true; | 
 |   return false; | 
 | } | 
 |  | 
 | /* Return TRUE if INSN is a conditional branch instruction, otherwise return FALSE.  */ | 
 |  | 
 | static bool | 
 | loongarch_insn_is_cond_branch (insn_t insn) | 
 | { | 
 |   if ((insn & 0xfc000000) == 0x58000000		/* beq   */ | 
 |       || (insn & 0xfc000000) == 0x5c000000	/* bne   */ | 
 |       || (insn & 0xfc000000) == 0x60000000	/* blt   */ | 
 |       || (insn & 0xfc000000) == 0x64000000	/* bge	 */ | 
 |       || (insn & 0xfc000000) == 0x68000000	/* bltu  */ | 
 |       || (insn & 0xfc000000) == 0x6c000000	/* bgeu  */ | 
 |       || (insn & 0xfc000000) == 0x40000000	/* beqz  */ | 
 |       || (insn & 0xfc000000) == 0x44000000)	/* bnez  */ | 
 |     return true; | 
 |   return false; | 
 | } | 
 |  | 
 | /* Return TRUE if INSN is a branch instruction, otherwise return FALSE.  */ | 
 |  | 
 | static bool | 
 | loongarch_insn_is_branch (insn_t insn) | 
 | { | 
 |   bool is_uncond = loongarch_insn_is_uncond_branch (insn); | 
 |   bool is_cond = loongarch_insn_is_cond_branch (insn); | 
 |  | 
 |   return (is_uncond || is_cond); | 
 | } | 
 |  | 
 | /* Return TRUE if INSN is a Load Linked instruction, otherwise return FALSE.  */ | 
 |  | 
 | static bool | 
 | loongarch_insn_is_ll (insn_t insn) | 
 | { | 
 |   if ((insn & 0xff000000) == 0x20000000		/* ll.w  */ | 
 |       || (insn & 0xff000000) == 0x22000000)	/* ll.d  */ | 
 |     return true; | 
 |   return false; | 
 | } | 
 |  | 
 | /* Return TRUE if INSN is a Store Conditional instruction, otherwise return FALSE.  */ | 
 |  | 
 | static bool | 
 | loongarch_insn_is_sc (insn_t insn) | 
 | { | 
 |   if ((insn & 0xff000000) == 0x21000000		/* sc.w  */ | 
 |       || (insn & 0xff000000) == 0x23000000)	/* sc.d  */ | 
 |     return true; | 
 |   return false; | 
 | } | 
 |  | 
 | /* Analyze the function prologue from START_PC to LIMIT_PC. | 
 |    Return the address of the first instruction past the prologue.  */ | 
 |  | 
 | static CORE_ADDR | 
 | loongarch_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc, | 
 | 			 CORE_ADDR limit_pc, struct frame_info *this_frame, | 
 | 			 struct trad_frame_cache *this_cache) | 
 | { | 
 |   CORE_ADDR cur_pc = start_pc, prologue_end = 0; | 
 |   int32_t sp = LOONGARCH_SP_REGNUM; | 
 |   int32_t fp = LOONGARCH_FP_REGNUM; | 
 |   int32_t reg_value[32] = {0}; | 
 |   int32_t reg_used[32] = {1, 0}; | 
 |  | 
 |   while (cur_pc < limit_pc) | 
 |     { | 
 |       insn_t insn = loongarch_fetch_instruction (cur_pc); | 
 |       size_t insn_len = loongarch_insn_length (insn); | 
 |       int32_t rd = loongarch_decode_imm ("0:5", insn, 0); | 
 |       int32_t rj = loongarch_decode_imm ("5:5", insn, 0); | 
 |       int32_t rk = loongarch_decode_imm ("10:5", insn, 0); | 
 |       int32_t si12 = loongarch_decode_imm ("10:12", insn, 1); | 
 |       int32_t si20 = loongarch_decode_imm ("5:20", insn, 1); | 
 |  | 
 |       if ((insn & 0xffc00000) == 0x02c00000	/* addi.d sp,sp,si12  */ | 
 | 	  && rd == sp && rj == sp && si12 < 0) | 
 | 	{ | 
 | 	  prologue_end = cur_pc + insn_len; | 
 | 	} | 
 |       else if ((insn & 0xffc00000) == 0x02c00000 /* addi.d fp,sp,si12  */ | 
 | 	       && rd == fp && rj == sp && si12 > 0) | 
 | 	{ | 
 | 	  prologue_end = cur_pc + insn_len; | 
 | 	} | 
 |       else if ((insn & 0xffc00000) == 0x29c00000 /* st.d rd,sp,si12  */ | 
 | 	       && rj == sp) | 
 | 	{ | 
 | 	  prologue_end = cur_pc + insn_len; | 
 | 	} | 
 |       else if ((insn & 0xff000000) == 0x27000000 /* stptr.d rd,sp,si14  */ | 
 | 	       && rj == sp) | 
 | 	{ | 
 | 	  prologue_end = cur_pc + insn_len; | 
 | 	} | 
 |       else if ((insn & 0xfe000000) == 0x14000000) /* lu12i.w rd,si20  */ | 
 | 	{ | 
 | 	  reg_value[rd] = si20 << 12; | 
 | 	  reg_used[rd] = 1; | 
 | 	} | 
 |       else if ((insn & 0xffc00000) == 0x03800000) /* ori rd,rj,si12  */ | 
 | 	{ | 
 | 	  if (reg_used[rj]) | 
 | 	  { | 
 | 	    reg_value[rd] = reg_value[rj] | (si12 & 0xfff); | 
 | 	    reg_used[rd] = 1; | 
 | 	  } | 
 | 	} | 
 |       else if ((insn & 0xffff8000) == 0x00108000 /* add.d sp,sp,rk  */ | 
 | 	       && rd == sp && rj == sp) | 
 | 	{ | 
 | 	  if (reg_used[rk] == 1 && reg_value[rk] < 0) | 
 | 	    { | 
 | 	      prologue_end = cur_pc + insn_len; | 
 | 	      break; | 
 | 	    } | 
 | 	} | 
 |       else if (loongarch_insn_is_branch (insn)) | 
 | 	{ | 
 | 	  break; | 
 | 	} | 
 |  | 
 |       cur_pc += insn_len; | 
 |     } | 
 |  | 
 |   if (prologue_end == 0) | 
 |     prologue_end = cur_pc; | 
 |  | 
 |   return prologue_end; | 
 | } | 
 |  | 
 | /* Implement the loongarch_skip_prologue gdbarch method.  */ | 
 |  | 
 | static CORE_ADDR | 
 | loongarch_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) | 
 | { | 
 |   CORE_ADDR func_addr; | 
 |  | 
 |   /* See if we can determine the end of the prologue via the symbol table. | 
 |      If so, then return either PC, or the PC after the prologue, whichever | 
 |      is greater.  */ | 
 |   if (find_pc_partial_function (pc, nullptr, &func_addr, nullptr)) | 
 |     { | 
 |       CORE_ADDR post_prologue_pc | 
 | 	= skip_prologue_using_sal (gdbarch, func_addr); | 
 |       if (post_prologue_pc != 0) | 
 | 	return std::max (pc, post_prologue_pc); | 
 |     } | 
 |  | 
 |   /* Can't determine prologue from the symbol table, need to examine | 
 |      instructions.  */ | 
 |  | 
 |   /* Find an upper limit on the function prologue using the debug | 
 |      information.  If the debug information could not be used to provide | 
 |      that bound, then use an arbitrary large number as the upper bound.  */ | 
 |   CORE_ADDR limit_pc = skip_prologue_using_sal (gdbarch, pc); | 
 |   if (limit_pc == 0) | 
 |     limit_pc = pc + 100;	/* Arbitrary large number.  */ | 
 |  | 
 |   return loongarch_scan_prologue (gdbarch, pc, limit_pc, nullptr, nullptr); | 
 | } | 
 |  | 
 | /* Decode the current instruction and determine the address of the | 
 |    next instruction.  */ | 
 |  | 
 | static CORE_ADDR | 
 | loongarch_next_pc (struct regcache *regcache, CORE_ADDR cur_pc) | 
 | { | 
 |   struct gdbarch *gdbarch = regcache->arch (); | 
 |   loongarch_gdbarch_tdep *tdep = (loongarch_gdbarch_tdep *) gdbarch_tdep (gdbarch); | 
 |   insn_t insn = loongarch_fetch_instruction (cur_pc); | 
 |   size_t insn_len = loongarch_insn_length (insn); | 
 |   CORE_ADDR next_pc = cur_pc + insn_len; | 
 |  | 
 |   if ((insn & 0xfc000000) == 0x4c000000)		/* jirl rd, rj, offs16  */ | 
 |     { | 
 |       LONGEST rj = regcache_raw_get_signed (regcache, | 
 | 		     loongarch_decode_imm ("5:5", insn, 0)); | 
 |       next_pc = rj + loongarch_decode_imm ("10:16<<2", insn, 1); | 
 |     } | 
 |   else if ((insn & 0xfc000000) == 0x50000000		/* b    offs26  */ | 
 | 	   || (insn & 0xfc000000) == 0x54000000)	/* bl	offs26  */ | 
 |     { | 
 |       next_pc = cur_pc + loongarch_decode_imm ("0:10|10:16<<2", insn, 1); | 
 |     } | 
 |   else if ((insn & 0xfc000000) == 0x58000000)		/* beq	rj, rd, offs16  */ | 
 |     { | 
 |       LONGEST rj = regcache_raw_get_signed (regcache, | 
 | 		     loongarch_decode_imm ("5:5", insn, 0)); | 
 |       LONGEST rd = regcache_raw_get_signed (regcache, | 
 | 		     loongarch_decode_imm ("0:5", insn, 0)); | 
 |       if (rj == rd) | 
 | 	next_pc = cur_pc + loongarch_decode_imm ("10:16<<2", insn, 1); | 
 |     } | 
 |   else if ((insn & 0xfc000000) == 0x5c000000)		/* bne	rj, rd, offs16  */ | 
 |     { | 
 |       LONGEST rj = regcache_raw_get_signed (regcache, | 
 | 		     loongarch_decode_imm ("5:5", insn, 0)); | 
 |       LONGEST rd = regcache_raw_get_signed (regcache, | 
 | 		     loongarch_decode_imm ("0:5", insn, 0)); | 
 |       if (rj != rd) | 
 | 	next_pc = cur_pc + loongarch_decode_imm ("10:16<<2", insn, 1); | 
 |     } | 
 |   else if ((insn & 0xfc000000) == 0x60000000)		/* blt	rj, rd, offs16  */ | 
 |     { | 
 |       LONGEST rj = regcache_raw_get_signed (regcache, | 
 | 		     loongarch_decode_imm ("5:5", insn, 0)); | 
 |       LONGEST rd = regcache_raw_get_signed (regcache, | 
 | 		     loongarch_decode_imm ("0:5", insn, 0)); | 
 |       if (rj < rd) | 
 | 	next_pc = cur_pc + loongarch_decode_imm ("10:16<<2", insn, 1); | 
 |     } | 
 |   else if ((insn & 0xfc000000) == 0x64000000)		/* bge	rj, rd, offs16  */ | 
 |     { | 
 |       LONGEST rj = regcache_raw_get_signed (regcache, | 
 | 		     loongarch_decode_imm ("5:5", insn, 0)); | 
 |       LONGEST rd = regcache_raw_get_signed (regcache, | 
 | 		     loongarch_decode_imm ("0:5", insn, 0)); | 
 |       if (rj >= rd) | 
 | 	next_pc = cur_pc + loongarch_decode_imm ("10:16<<2", insn, 1); | 
 |     } | 
 |   else if ((insn & 0xfc000000) == 0x68000000)		/* bltu	rj, rd, offs16  */ | 
 |     { | 
 |       ULONGEST rj = regcache_raw_get_unsigned (regcache, | 
 | 		      loongarch_decode_imm ("5:5", insn, 0)); | 
 |       ULONGEST rd = regcache_raw_get_unsigned (regcache, | 
 | 		      loongarch_decode_imm ("0:5", insn, 0)); | 
 |       if (rj < rd) | 
 | 	next_pc = cur_pc + loongarch_decode_imm ("10:16<<2", insn, 1); | 
 |     } | 
 |   else if ((insn & 0xfc000000) == 0x6c000000)		/* bgeu	rj, rd, offs16  */ | 
 |     { | 
 |       ULONGEST rj = regcache_raw_get_unsigned (regcache, | 
 | 		      loongarch_decode_imm ("5:5", insn, 0)); | 
 |       ULONGEST rd = regcache_raw_get_unsigned (regcache, | 
 | 		      loongarch_decode_imm ("0:5", insn, 0)); | 
 |       if (rj >= rd) | 
 | 	next_pc = cur_pc + loongarch_decode_imm ("10:16<<2", insn, 1); | 
 |     } | 
 |   else if ((insn & 0xfc000000) == 0x40000000)		/* beqz	rj, offs21  */ | 
 |     { | 
 |       LONGEST rj = regcache_raw_get_signed (regcache, | 
 | 		     loongarch_decode_imm ("5:5", insn, 0)); | 
 |       if (rj == 0) | 
 | 	next_pc = cur_pc + loongarch_decode_imm ("0:5|10:16<<2", insn, 1); | 
 |     } | 
 |   else if ((insn & 0xfc000000) == 0x44000000)		/* bnez	rj, offs21  */ | 
 |     { | 
 |       LONGEST rj = regcache_raw_get_signed (regcache, | 
 | 		     loongarch_decode_imm ("5:5", insn, 0)); | 
 |       if (rj != 0) | 
 | 	next_pc = cur_pc + loongarch_decode_imm ("0:5|10:16<<2", insn, 1); | 
 |     } | 
 |   else if ((insn & 0xffff8000) == 0x002b0000)		/* syscall  */ | 
 |     { | 
 |       if (tdep->syscall_next_pc != nullptr) | 
 | 	next_pc = tdep->syscall_next_pc (get_current_frame ()); | 
 |     } | 
 |  | 
 |   return next_pc; | 
 | } | 
 |  | 
 | /* We can't put a breakpoint in the middle of a ll/sc atomic sequence, | 
 |    so look for the end of the sequence and put the breakpoint there.  */ | 
 |  | 
 | static std::vector<CORE_ADDR> | 
 | loongarch_deal_with_atomic_sequence (struct regcache *regcache, CORE_ADDR cur_pc) | 
 | { | 
 |   CORE_ADDR next_pc; | 
 |   std::vector<CORE_ADDR> next_pcs; | 
 |   insn_t insn = loongarch_fetch_instruction (cur_pc); | 
 |   size_t insn_len = loongarch_insn_length (insn); | 
 |   const int atomic_sequence_length = 16; | 
 |   bool found_atomic_sequence_endpoint = false; | 
 |  | 
 |   /* Look for a Load Linked instruction which begins the atomic sequence.  */ | 
 |   if (!loongarch_insn_is_ll (insn)) | 
 |     return {}; | 
 |  | 
 |   /* Assume that no atomic sequence is longer than "atomic_sequence_length" instructions.  */ | 
 |   for (int insn_count = 0; insn_count < atomic_sequence_length; ++insn_count) | 
 |     { | 
 |       cur_pc += insn_len; | 
 |       insn = loongarch_fetch_instruction (cur_pc); | 
 |  | 
 |       /* Look for a unconditional branch instruction, fallback to the standard code.  */ | 
 |       if (loongarch_insn_is_uncond_branch (insn)) | 
 | 	{ | 
 | 	  return {}; | 
 | 	} | 
 |       /* Look for a conditional branch instruction, put a breakpoint in its destination address.  */ | 
 |       else if (loongarch_insn_is_cond_branch (insn)) | 
 | 	{ | 
 | 	  next_pc = loongarch_next_pc (regcache, cur_pc); | 
 | 	  next_pcs.push_back (next_pc); | 
 | 	} | 
 |       /* Look for a Store Conditional instruction which closes the atomic sequence.  */ | 
 |       else if (loongarch_insn_is_sc (insn)) | 
 | 	{ | 
 | 	  found_atomic_sequence_endpoint = true; | 
 | 	  next_pc = cur_pc + insn_len; | 
 | 	  next_pcs.push_back (next_pc); | 
 | 	  break; | 
 | 	} | 
 |     } | 
 |  | 
 |   /* We didn't find a closing Store Conditional instruction, fallback to the standard code.  */ | 
 |   if (!found_atomic_sequence_endpoint) | 
 |     return {}; | 
 |  | 
 |   return next_pcs; | 
 | } | 
 |  | 
 | /* Implement the software_single_step gdbarch method  */ | 
 |  | 
 | static std::vector<CORE_ADDR> | 
 | loongarch_software_single_step (struct regcache *regcache) | 
 | { | 
 |   CORE_ADDR cur_pc = regcache_read_pc (regcache); | 
 |   std::vector<CORE_ADDR> next_pcs | 
 |     = loongarch_deal_with_atomic_sequence (regcache, cur_pc); | 
 |  | 
 |   if (!next_pcs.empty ()) | 
 |     return next_pcs; | 
 |  | 
 |   CORE_ADDR next_pc = loongarch_next_pc (regcache, cur_pc); | 
 |  | 
 |   return {next_pc}; | 
 | } | 
 |  | 
 | /* Implement the frame_align gdbarch method.  */ | 
 |  | 
 | static CORE_ADDR | 
 | loongarch_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr) | 
 | { | 
 |   return align_down (addr, 16); | 
 | } | 
 |  | 
 | /* Generate, or return the cached frame cache for frame unwinder.  */ | 
 |  | 
 | static struct trad_frame_cache * | 
 | loongarch_frame_cache (struct frame_info *this_frame, void **this_cache) | 
 | { | 
 |   struct trad_frame_cache *cache; | 
 |   CORE_ADDR pc; | 
 |  | 
 |   if (*this_cache != nullptr) | 
 |     return (struct trad_frame_cache *) *this_cache; | 
 |  | 
 |   cache = trad_frame_cache_zalloc (this_frame); | 
 |   *this_cache = cache; | 
 |  | 
 |   trad_frame_set_reg_realreg (cache, LOONGARCH_PC_REGNUM, LOONGARCH_RA_REGNUM); | 
 |  | 
 |   pc = get_frame_address_in_block (this_frame); | 
 |   trad_frame_set_id (cache, frame_id_build_unavailable_stack (pc)); | 
 |  | 
 |   return cache; | 
 | } | 
 |  | 
 | /* Implement the this_id callback for frame unwinder.  */ | 
 |  | 
 | static void | 
 | loongarch_frame_this_id (struct frame_info *this_frame, void **prologue_cache, | 
 | 			 struct frame_id *this_id) | 
 | { | 
 |   struct trad_frame_cache *info; | 
 |  | 
 |   info = loongarch_frame_cache (this_frame, prologue_cache); | 
 |   trad_frame_get_id (info, this_id); | 
 | } | 
 |  | 
 | /* Implement the prev_register callback for frame unwinder.  */ | 
 |  | 
 | static struct value * | 
 | loongarch_frame_prev_register (struct frame_info *this_frame, | 
 | 			       void **prologue_cache, int regnum) | 
 | { | 
 |   struct trad_frame_cache *info; | 
 |  | 
 |   info = loongarch_frame_cache (this_frame, prologue_cache); | 
 |   return trad_frame_get_register (info, this_frame, regnum); | 
 | } | 
 |  | 
 | static const struct frame_unwind loongarch_frame_unwind = { | 
 |   "loongarch prologue", | 
 |   /*.type	   =*/NORMAL_FRAME, | 
 |   /*.stop_reason   =*/default_frame_unwind_stop_reason, | 
 |   /*.this_id	   =*/loongarch_frame_this_id, | 
 |   /*.prev_register =*/loongarch_frame_prev_register, | 
 |   /*.unwind_data   =*/nullptr, | 
 |   /*.sniffer	   =*/default_frame_sniffer, | 
 |   /*.dealloc_cache =*/nullptr, | 
 |   /*.prev_arch	   =*/nullptr, | 
 | }; | 
 |  | 
 | static void | 
 | pass_in_gar (struct regcache *regcache, unsigned int gar, const gdb_byte *val) | 
 | { | 
 |   unsigned int regnum = LOONGARCH_ARG_REGNUM - gar + LOONGARCH_A0_REGNUM; | 
 |   regcache->cooked_write (regnum, val); | 
 | } | 
 |  | 
 | static void | 
 | pass_in_far (struct regcache *regcache, unsigned int far, const gdb_byte *val) | 
 | { | 
 |   unsigned int regnum = LOONGARCH_ARG_REGNUM - far + LOONGARCH_BADV_REGNUM + 1; | 
 |   regcache->cooked_write (regnum, val); | 
 | } | 
 |  | 
 | static __attribute__((aligned(16))) gdb_byte buf[1024] = { 0 }; | 
 | static gdb_byte *addr = buf; | 
 |  | 
 | static void | 
 | pass_on_stack (struct regcache *regcache, const gdb_byte *val, size_t len, int align) | 
 | { | 
 |   align = align_up (align, 8); | 
 |   if (align > 16) | 
 |     align = 16; | 
 |  | 
 |   CORE_ADDR align_addr = (CORE_ADDR) addr; | 
 |   align_addr = align_up (align_addr, align); | 
 |   addr = (gdb_byte *) align_addr; | 
 |   memcpy (addr, val, len); | 
 |   addr += len; | 
 | } | 
 |  | 
 | static unsigned int fixed_point_members = 0; | 
 | static unsigned int floating_point_members = 0; | 
 | static bool first_member_is_fixed_point = false; | 
 |  | 
 | static void | 
 | compute_struct_member (struct type *type) | 
 | { | 
 |   for (int i = 0; i < type->num_fields (); i++) | 
 |     { | 
 |       struct type *field_type = check_typedef (type->field (i).type ()); | 
 |  | 
 |       if (field_type->code () == TYPE_CODE_INT | 
 | 	  || field_type->code () == TYPE_CODE_BOOL | 
 | 	  || field_type->code () == TYPE_CODE_CHAR | 
 | 	  || field_type->code () == TYPE_CODE_RANGE | 
 | 	  || field_type->code () == TYPE_CODE_ENUM | 
 | 	  || field_type->code () == TYPE_CODE_PTR) | 
 |       { | 
 | 	fixed_point_members++; | 
 |  | 
 | 	if (floating_point_members == 0) | 
 | 	  first_member_is_fixed_point = true; | 
 |       } | 
 |       else if (field_type->code () == TYPE_CODE_FLT) | 
 | 	floating_point_members++; | 
 |       else if (field_type->code () == TYPE_CODE_STRUCT) | 
 | 	compute_struct_member (field_type); | 
 |       else if (field_type->code () == TYPE_CODE_COMPLEX) | 
 | 	floating_point_members += 2; | 
 |     } | 
 | } | 
 |  | 
 | /* Implement the push_dummy_call gdbarch method.  */ | 
 |  | 
 | static CORE_ADDR | 
 | loongarch_push_dummy_call (struct gdbarch *gdbarch, | 
 | 			   struct value *function, | 
 | 			   struct regcache *regcache, | 
 | 			   CORE_ADDR bp_addr, | 
 | 			   int nargs, | 
 | 			   struct value **args, | 
 | 			   CORE_ADDR sp, | 
 | 			   function_call_return_method return_method, | 
 | 			   CORE_ADDR struct_addr) | 
 | { | 
 |   int regsize = register_size (gdbarch, 0); | 
 |   unsigned int gar = LOONGARCH_ARG_REGNUM; | 
 |   unsigned int far = LOONGARCH_ARG_REGNUM; | 
 |  | 
 |   if (return_method != return_method_normal) | 
 |     pass_in_gar (regcache, gar--, (gdb_byte *) &struct_addr); | 
 |  | 
 |   addr = buf; | 
 |   for (int i = 0; i < nargs; i++) | 
 |     { | 
 |       struct value *arg = args[i]; | 
 |       const gdb_byte *val = value_contents (arg).data (); | 
 |       struct type *type = check_typedef (value_type (arg)); | 
 |       size_t len = TYPE_LENGTH (type); | 
 |       int align = type_align (type); | 
 |       enum type_code code = type->code (); | 
 |  | 
 |       switch (code) | 
 | 	{ | 
 | 	case TYPE_CODE_INT: | 
 | 	case TYPE_CODE_BOOL: | 
 | 	case TYPE_CODE_CHAR: | 
 | 	case TYPE_CODE_RANGE: | 
 | 	case TYPE_CODE_ENUM: | 
 | 	case TYPE_CODE_PTR: | 
 | 	  { | 
 | 	    /* integer or pointer type is passed in GAR. | 
 | 	     * If no GAR is available, it's passed on the stack. | 
 | 	     * When passed in registers or on the stack, | 
 | 	     * the unsigned integer scalars are zero-extended to GRLEN bits, | 
 | 	     * and the signed integer scalars are sign-extended.  */ | 
 | 	  if (type->is_unsigned ()) | 
 | 	    { | 
 |               ULONGEST data = extract_unsigned_integer (val, len, BFD_ENDIAN_LITTLE); | 
 | 	      if (gar > 0) | 
 | 		pass_in_gar (regcache, gar--, (gdb_byte *) &data); | 
 | 	      else | 
 | 		pass_on_stack (regcache, (gdb_byte *) &data, len, align); | 
 |             } | 
 | 	  else | 
 | 	    { | 
 | 	      LONGEST data = extract_signed_integer (val, len, BFD_ENDIAN_LITTLE); | 
 | 	      if (gar > 0) | 
 | 		pass_in_gar (regcache, gar--, (gdb_byte *) &data); | 
 | 	      else | 
 | 		pass_on_stack (regcache, (gdb_byte *) &data, len, align); | 
 |             } | 
 | 	  } | 
 | 	  break; | 
 | 	case TYPE_CODE_FLT: | 
 | 	  if (len == 2 * regsize) | 
 | 	    { | 
 | 	      /* long double type is passed in a pair of GAR, | 
 | 	       * with the low-order GRLEN bits in the lower-numbered register | 
 | 	       * and the high-order GRLEN bits in the higher-numbered register. | 
 | 	       * If exactly one register is available, | 
 | 	       * the low-order GRLEN bits are passed in the register | 
 | 	       * and the high-order GRLEN bits are passed on the stack. | 
 | 	       * If no GAR is available, it's passed on the stack.  */ | 
 | 	      if (gar >= 2) | 
 | 		{ | 
 | 		  pass_in_gar (regcache, gar--, val); | 
 | 		  pass_in_gar (regcache, gar--, val + regsize); | 
 | 		} | 
 | 	      else if (gar == 1) | 
 | 		{ | 
 | 		  pass_in_gar (regcache, gar--, val); | 
 | 		  pass_on_stack (regcache, val + regsize, len - regsize, align); | 
 | 		} | 
 | 	      else | 
 | 		{ | 
 | 		  pass_on_stack (regcache, val, len, align); | 
 | 		} | 
 | 	    } | 
 | 	  else | 
 | 	    { | 
 | 	      /* The other floating-point type is passed in FAR. | 
 | 	       * If no FAR is available, it's passed in GAR. | 
 | 	       * If no GAR is available, it's passed on the stack.  */ | 
 | 	      if (far > 0) | 
 | 		  pass_in_far (regcache, far--, val); | 
 | 	      else if (gar > 0) | 
 | 		  pass_in_gar (regcache, gar--, val); | 
 | 	      else | 
 | 		  pass_on_stack (regcache, val, len, align); | 
 | 	    } | 
 | 	  break; | 
 | 	case TYPE_CODE_STRUCT: | 
 | 	  { | 
 | 	    fixed_point_members = 0; | 
 | 	    floating_point_members = 0; | 
 | 	    first_member_is_fixed_point = false; | 
 | 	    compute_struct_member (type); | 
 |  | 
 | 	    if (len > 0 && len <= regsize) | 
 | 	      { | 
 | 		/* The structure has only fixed-point members.  */ | 
 | 		if (fixed_point_members > 0 && floating_point_members == 0) | 
 | 		  { | 
 | 		    /* If there is an available GAR, | 
 | 		     * the structure is passed through the GAR by value passing; | 
 | 		     * If no GAR is available, it's passed on the stack.  */ | 
 | 		    if (gar > 0) | 
 | 		      pass_in_gar (regcache, gar--, val); | 
 | 		    else | 
 | 		      pass_on_stack (regcache, val, len, align); | 
 | 		  } | 
 | 		/* The structure has only floating-point members.  */ | 
 | 		else if (fixed_point_members == 0 && floating_point_members > 0) | 
 | 		  { | 
 | 		    /* One floating-point member. | 
 | 		     * The argument is passed in a FAR. | 
 | 		     * If no FAR is available, the value is passed in a GAR. | 
 | 		     * if no GAR is available, the value is passed on the stack.  */ | 
 | 		    if (floating_point_members == 1) | 
 | 		      { | 
 | 			if (far > 0) | 
 | 			  pass_in_far (regcache, far--, val); | 
 | 			else if (gar > 0) | 
 | 			  pass_in_gar (regcache, gar--, val); | 
 | 			else | 
 | 			  pass_on_stack (regcache, val, len, align); | 
 | 		      } | 
 | 		    /* Two floating-point members. | 
 | 		     * The argument is passed in a pair of available FAR, | 
 | 		     * with the low-order float member bits in the lower-numbered FAR | 
 | 		     * and the high-order float member bits in the higher-numbered FAR. | 
 | 		     * If the number of available FAR is less than 2, it's passed in a GAR, | 
 | 		     * and passed on the stack if no GAR is available.  */ | 
 | 		    else if (floating_point_members == 2) | 
 | 		      { | 
 | 			if (far >= 2) | 
 | 			  { | 
 | 			    pass_in_far (regcache, far--, val); | 
 | 			    pass_in_far (regcache, far--, val + align); | 
 | 			  } | 
 | 			else if (gar > 0) | 
 | 			  { | 
 | 			    pass_in_gar (regcache, gar--, val); | 
 | 			  } | 
 | 			else | 
 | 			  { | 
 | 			    pass_on_stack (regcache, val, len, align); | 
 | 			  } | 
 | 		      } | 
 | 		  } | 
 | 		/* The structure has both fixed-point and floating-point members.  */ | 
 | 		else if (fixed_point_members > 0 && floating_point_members > 0) | 
 | 		  { | 
 | 		    /* One float member and multiple fixed-point members. | 
 | 		     * If there are available GAR, the structure is passed in a GAR, | 
 | 		     * and passed on the stack if no GAR is available.  */ | 
 | 		    if (floating_point_members == 1 && fixed_point_members > 1) | 
 | 		      { | 
 | 			if (gar > 0) | 
 | 			  pass_in_gar (regcache, gar--, val); | 
 | 			else | 
 | 			  pass_on_stack (regcache, val, len, align); | 
 | 		      } | 
 | 		    /* One float member and only one fixed-point member. | 
 | 		     * If one FAR and one GAR are available, | 
 | 		     * the floating-point member of the structure is passed in the FAR, | 
 | 		     * and the fixed-point member of the structure is passed in the GAR. | 
 | 		     * If no floating-point register but one GAR is available, it's passed in GAR; | 
 | 		     * If no GAR is available, it's passed on the stack.  */ | 
 | 		    else if (floating_point_members == 1 && fixed_point_members == 1) | 
 | 		      { | 
 | 			if (far > 0 && gar > 0) | 
 | 			  { | 
 | 			    if (first_member_is_fixed_point == false) | 
 | 			      { | 
 | 				pass_in_far (regcache, far--, val); | 
 | 				pass_in_gar (regcache, gar--, val + align); | 
 | 			      } | 
 | 			    else | 
 | 			      { | 
 | 				pass_in_gar (regcache, gar--, val); | 
 | 				pass_in_far (regcache, far--, val + align); | 
 | 			      } | 
 | 			  } | 
 | 			else | 
 | 			  { | 
 | 			    if (gar > 0) | 
 | 			      pass_in_gar (regcache, gar--, val); | 
 | 			    else | 
 | 			      pass_on_stack (regcache, val, len, align); | 
 | 			  } | 
 | 		      } | 
 | 		  } | 
 | 	      } | 
 | 	    else if (len > regsize && len <= 2 * regsize) | 
 | 	      { | 
 | 		/* Only fixed-point members.  */ | 
 | 		if (fixed_point_members > 0 && floating_point_members == 0) | 
 | 		  { | 
 | 		    /* The argument is passed in a pair of available GAR, | 
 | 		     * with the low-order bits in the lower-numbered GAR | 
 | 		     * and the high-order bits in the higher-numbered GAR. | 
 | 		     * If only one GAR is available, | 
 | 		     * the low-order bits are in the GAR | 
 | 		     * and the high-order bits are on the stack, | 
 | 		     * and passed on the stack if no GAR is available.  */ | 
 | 		    if (gar >= 2) | 
 | 		      { | 
 | 			pass_in_gar (regcache, gar--, val); | 
 | 			pass_in_gar (regcache, gar--, val + regsize); | 
 | 		      } | 
 | 		    else if (gar == 1) | 
 | 		      { | 
 | 			pass_in_gar (regcache, gar--, val); | 
 | 			pass_on_stack (regcache, val + regsize, len - regsize, align); | 
 | 		      } | 
 | 		    else | 
 | 		      { | 
 | 			pass_on_stack (regcache, val, len, align); | 
 | 		      } | 
 | 		  } | 
 | 		/* Only floating-point members.  */ | 
 | 		else if (fixed_point_members == 0 && floating_point_members > 0) | 
 | 		  { | 
 | 		    /* The structure has one long double member | 
 | 		     * or one double member and two adjacent float members | 
 | 		     * or 3-4 float members. | 
 | 		     * The argument is passed in a pair of available GAR, | 
 | 		     * with the low-order bits in the lower-numbered GAR | 
 | 		     * and the high-order bits in the higher-numbered GAR. | 
 | 		     * If only one GAR is available, | 
 | 		     * the low-order bits are in the GAR | 
 | 		     * and the high-order bits are on the stack, | 
 | 		     * and passed on the stack if no GAR is available.  */ | 
 | 		    if ((len == 16 && floating_point_members == 1) | 
 | 			 || (len == 16 && floating_point_members == 3) | 
 | 			 || (len == 12 && floating_point_members == 3) | 
 | 			 || (len == 16 && floating_point_members == 4)) | 
 | 		      { | 
 | 			if (gar >= 2) | 
 | 			  { | 
 | 			    pass_in_gar (regcache, gar--, val); | 
 | 			    pass_in_gar (regcache, gar--, val + regsize); | 
 | 			  } | 
 | 			else if (gar == 1) | 
 | 			  { | 
 | 			    pass_in_gar (regcache, gar--, val); | 
 | 			    pass_on_stack (regcache, val + regsize, len - regsize, align); | 
 | 			  } | 
 | 			else | 
 | 			  { | 
 | 			    pass_on_stack (regcache, val, len, align); | 
 | 			  } | 
 | 		      } | 
 | 		    /* The structure with two double members | 
 | 		     * is passed in a pair of available FAR, | 
 | 		     * with the low-order bits in the lower-numbered FAR | 
 | 		     * and the high-order bits in the higher-numbered FAR. | 
 | 		     * If no a pair of available FAR, | 
 | 		     * it's passed in a pair of available GAR, | 
 | 		     * with the low-order bits in the lower-numbered GAR | 
 | 		     * and the high-order bits in the higher-numbered GAR. | 
 | 		     * If only one GAR is available, | 
 | 		     * the low-order bits are in the GAR | 
 | 		     * and the high-order bits are on stack, | 
 | 		     * and passed on the stack if no GAR is available. | 
 | 		     * A structure with one double member and one float member is same.  */ | 
 | 		    else if ((len == 16 && floating_point_members == 2) | 
 | 			     || (len == 12 && floating_point_members == 2)) | 
 | 		      { | 
 | 			if (far >= 2) | 
 | 			  { | 
 | 			    pass_in_far (regcache, far--, val); | 
 | 			    pass_in_far (regcache, far--, val + regsize); | 
 | 			  } | 
 | 			else if (gar >= 2) | 
 | 			  { | 
 | 			    pass_in_gar (regcache, gar--, val); | 
 | 			    pass_in_gar (regcache, gar--, val + regsize); | 
 | 			  } | 
 | 			else if (gar == 1) | 
 | 			  { | 
 | 			    pass_in_gar (regcache, gar--, val); | 
 | 			    pass_on_stack (regcache, val + regsize, len - regsize, align); | 
 | 			  } | 
 | 			else | 
 | 			  { | 
 | 			    pass_on_stack (regcache, val, len, align); | 
 | 			  } | 
 | 		      } | 
 | 		  } | 
 | 		/* Both fixed-point and floating-point members.  */ | 
 | 		else if (fixed_point_members > 0 && floating_point_members > 0) | 
 | 		  { | 
 | 		    /* The structure has one floating-point member and only one fixed-point member.  */ | 
 | 		    if (floating_point_members == 1 && fixed_point_members == 1) | 
 | 		      { | 
 | 			/* If one FAR and one GAR are available, | 
 | 			 * the floating-point member of the structure is passed in the FAR, | 
 | 			 * and the fixed-point member of the structure is passed in the GAR; | 
 | 			 * If no floating-point registers but two GARs are available, | 
 | 			 * it's passed in the two GARs; | 
 | 			 * If only one GAR is available, | 
 | 			 * the low-order bits are in the GAR | 
 | 			 * and the high-order bits are on the stack; | 
 | 			 * And it's passed on the stack if no GAR is available.  */ | 
 | 			if (far > 0 && gar > 0) | 
 | 			  { | 
 | 			    if (first_member_is_fixed_point == false) | 
 | 			      { | 
 | 				pass_in_far (regcache, far--, val); | 
 | 				pass_in_gar (regcache, gar--, val + regsize); | 
 | 			      } | 
 | 			    else | 
 | 			      { | 
 | 				pass_in_gar (regcache, gar--, val); | 
 | 				pass_in_far (regcache, far--, val + regsize); | 
 | 			      } | 
 | 			  } | 
 | 			else if (far == 0 && gar >= 2) | 
 | 			  { | 
 | 			    pass_in_gar (regcache, gar--, val); | 
 | 			    pass_in_gar (regcache, gar--, val + regsize); | 
 | 			  } | 
 | 			else if (far == 0 && gar == 1) | 
 | 			  { | 
 | 			    pass_in_gar (regcache, gar--, val); | 
 | 			    pass_on_stack (regcache, val + regsize, len - regsize, align); | 
 | 			  } | 
 | 			else if (far == 0 && gar == 0) | 
 | 			  { | 
 | 			    pass_on_stack (regcache, val, len, align); | 
 | 			  } | 
 | 		      } | 
 | 		    else | 
 | 		      { | 
 | 			/* The argument is passed in a pair of available GAR, | 
 | 			 * with the low-order bits in the lower-numbered GAR | 
 | 			 * and the high-order bits in the higher-numbered GAR. | 
 | 			 * If only one GAR is available, | 
 | 			 * the low-order bits are in the GAR | 
 | 			 * and the high-order bits are on the stack, | 
 | 			 * and passed on the stack if no GAR is available.  */ | 
 | 			if (gar >= 2) | 
 | 			  { | 
 | 			    pass_in_gar (regcache, gar--, val); | 
 | 			    pass_in_gar (regcache, gar--, val + regsize); | 
 | 			  } | 
 | 			else if (gar == 1) | 
 | 			  { | 
 | 			    pass_in_gar (regcache, gar--, val); | 
 | 			    pass_on_stack (regcache, val + regsize, len - regsize, align); | 
 | 			  } | 
 | 			else | 
 | 			  { | 
 | 			    pass_on_stack (regcache, val, len, align); | 
 | 			  } | 
 | 		      } | 
 | 		  } | 
 | 	      } | 
 | 	    else if (len > 2 * regsize) | 
 | 	      { | 
 | 	        /* It's passed by reference and are replaced in the argument list with the address. | 
 | 		 * If there is an available GAR, the reference is passed in the GAR, | 
 | 		 * and passed on the stack if no GAR is available.  */ | 
 | 		sp = align_down (sp - len, 16); | 
 | 		write_memory (sp, val, len); | 
 |  | 
 | 		if (gar > 0) | 
 | 		  pass_in_gar (regcache, gar--, (const gdb_byte *) &sp); | 
 | 		else | 
 | 		  pass_on_stack (regcache, (const gdb_byte*) &sp, len, regsize); | 
 | 	      } | 
 | 	  } | 
 | 	  break; | 
 | 	case TYPE_CODE_UNION: | 
 | 	  /* Union is passed in GAR or stack.  */ | 
 | 	  if (len > 0 && len <= regsize) | 
 | 	    { | 
 | 	      /* The argument is passed in a GAR, | 
 | 	       * or on the stack by value if no GAR is available.  */ | 
 | 	      if (gar > 0) | 
 | 		pass_in_gar (regcache, gar--, val); | 
 | 	      else | 
 | 		pass_on_stack (regcache, val, len, align); | 
 | 	    } | 
 | 	  else if (len > regsize && len <= 2 * regsize) | 
 | 	    { | 
 | 	      /* The argument is passed in a pair of available GAR, | 
 | 	       * with the low-order bits in the lower-numbered GAR | 
 | 	       * and the high-order bits in the higher-numbered GAR. | 
 | 	       * If only one GAR is available, | 
 | 	       * the low-order bits are in the GAR | 
 | 	       * and the high-order bits are on the stack. | 
 | 	       * The arguments are passed on the stack when no GAR is available.  */ | 
 | 	      if (gar >= 2) | 
 | 		{ | 
 | 		  pass_in_gar (regcache, gar--, val); | 
 | 		  pass_in_gar (regcache, gar--, val + regsize); | 
 | 		} | 
 | 	      else if (gar == 1) | 
 | 		{ | 
 | 		  pass_in_gar (regcache, gar--, val); | 
 | 		  pass_on_stack (regcache, val + regsize, len - regsize, align); | 
 | 		} | 
 | 	      else | 
 | 		{ | 
 | 		  pass_on_stack (regcache, val, len, align); | 
 | 		} | 
 | 	    } | 
 | 	  else if (len > 2 * regsize) | 
 | 	    { | 
 | 	      /* It's passed by reference and are replaced in the argument list with the address. | 
 | 	       * If there is an available GAR, the reference is passed in the GAR, | 
 | 	       * and passed on the stack if no GAR is available.  */ | 
 | 	      sp = align_down (sp - len, 16); | 
 | 	      write_memory (sp, val, len); | 
 |  | 
 | 	      if (gar > 0) | 
 | 		pass_in_gar (regcache, gar--, (const gdb_byte *) &sp); | 
 | 	      else | 
 | 		pass_on_stack (regcache, (const gdb_byte*) &sp, len, regsize); | 
 | 	    } | 
 | 	  break; | 
 | 	case TYPE_CODE_COMPLEX: | 
 | 	  { | 
 | 	    struct type *target_type = check_typedef (TYPE_TARGET_TYPE (type)); | 
 | 	    size_t target_len = TYPE_LENGTH (target_type); | 
 |  | 
 | 	    if (target_len < regsize) | 
 | 	      { | 
 | 		/* The complex with two float members | 
 | 		 * is passed in a pair of available FAR, | 
 | 		 * with the low-order float member bits in the lower-numbered FAR | 
 | 		 * and the high-order float member bits in the higher-numbered FAR. | 
 | 		 * If the number of available FAR is less than 2, it's passed in a GAR, | 
 | 		 * and passed on the stack if no GAR is available.  */ | 
 | 		if (far >= 2) | 
 | 		  { | 
 | 		    pass_in_far (regcache, far--, val); | 
 | 		    pass_in_far (regcache, far--, val + align); | 
 | 		  } | 
 | 		else if (gar > 0) | 
 | 		  { | 
 | 		    pass_in_gar (regcache, gar--, val); | 
 | 		  } | 
 | 		else | 
 | 		  { | 
 | 		    pass_on_stack (regcache, val, len, align); | 
 | 		  } | 
 | 	      } | 
 | 	    else if (target_len == regsize) | 
 | 	      { | 
 | 		/* The complex with two double members | 
 | 		 * is passed in a pair of available FAR, | 
 | 		 * with the low-order bits in the lower-numbered FAR | 
 | 		 * and the high-order bits in the higher-numbered FAR. | 
 | 		 * If no a pair of available FAR, | 
 | 		 * it's passed in a pair of available GAR, | 
 | 		 * with the low-order bits in the lower-numbered GAR | 
 | 		 * and the high-order bits in the higher-numbered GAR. | 
 | 		 * If only one GAR is available, | 
 | 		 * the low-order bits are in the GAR | 
 | 		 * and the high-order bits are on stack, | 
 | 		 * and passed on the stack if no GAR is available.  */ | 
 | 		{ | 
 | 		  if (far >= 2) | 
 | 		    { | 
 | 		      pass_in_far (regcache, far--, val); | 
 | 		      pass_in_far (regcache, far--, val + align); | 
 | 		    } | 
 | 		  else if (gar >= 2) | 
 | 		    { | 
 | 		      pass_in_gar (regcache, gar--, val); | 
 | 		      pass_in_gar (regcache, gar--, val + align); | 
 | 		    } | 
 | 		  else if (gar == 1) | 
 | 		    { | 
 | 		      pass_in_gar (regcache, gar--, val); | 
 | 		      pass_on_stack (regcache, val + align, len - align, align); | 
 | 		    } | 
 | 		  else | 
 | 		    { | 
 | 		      pass_on_stack (regcache, val, len, align); | 
 | 		    } | 
 | 		} | 
 | 	      } | 
 | 	    else if (target_len == 2 * regsize) | 
 | 	      { | 
 | 		/* The complex with two long double members | 
 | 		 * is passed by reference and are replaced in the argument list with the address. | 
 | 	         * If there is an available GAR, the reference is passed in the GAR, | 
 | 	         * and passed on the stack if no GAR is available.  */ | 
 | 		sp = align_down (sp - len, 16); | 
 | 		write_memory (sp, val, len); | 
 |  | 
 | 		if (gar > 0) | 
 | 		  pass_in_gar (regcache, gar--, (const gdb_byte *) &sp); | 
 | 		else | 
 | 		  pass_on_stack (regcache, (const gdb_byte*) &sp, regsize, regsize); | 
 | 	      } | 
 | 	  } | 
 | 	  break; | 
 | 	default: | 
 | 	  break; | 
 | 	} | 
 |     } | 
 |  | 
 |   if (addr > buf) | 
 |     { | 
 |       sp -= addr - buf; | 
 |       sp = align_down (sp, 16); | 
 |       write_memory (sp, buf, addr - buf); | 
 |     } | 
 |  | 
 |   regcache_cooked_write_unsigned (regcache, LOONGARCH_RA_REGNUM, bp_addr); | 
 |   regcache_cooked_write_unsigned (regcache, LOONGARCH_SP_REGNUM, sp); | 
 |  | 
 |   return sp; | 
 | } | 
 |  | 
 | /* Implement the return_value gdbarch method.  */ | 
 |  | 
 | static enum return_value_convention | 
 | loongarch_return_value (struct gdbarch *gdbarch, struct value *function, | 
 | 			struct type *type, struct regcache *regcache, | 
 | 			gdb_byte *readbuf, const gdb_byte *writebuf) | 
 | { | 
 |   int len = TYPE_LENGTH (type); | 
 |   int regnum = -1; | 
 |  | 
 |   /* See if our value is returned through a register.  If it is, then | 
 |      store the associated register number in REGNUM.  */ | 
 |   switch (type->code ()) | 
 |     { | 
 |       case TYPE_CODE_INT: | 
 | 	regnum = LOONGARCH_A0_REGNUM; | 
 | 	break; | 
 |     } | 
 |  | 
 |   /* Extract the return value from the register where it was stored.  */ | 
 |   if (readbuf != nullptr) | 
 |     regcache->raw_read_part (regnum, 0, len, readbuf); | 
 |   if (writebuf != nullptr) | 
 |     regcache->raw_write_part (regnum, 0, len, writebuf); | 
 |  | 
 |   return RETURN_VALUE_REGISTER_CONVENTION; | 
 | } | 
 |  | 
 | /* Implement the dwarf2_reg_to_regnum gdbarch method.  */ | 
 |  | 
 | static int | 
 | loongarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int regnum) | 
 | { | 
 |   if (regnum >= 0 && regnum < 32) | 
 |     return regnum; | 
 |   else | 
 |     return -1; | 
 | } | 
 |  | 
 | static constexpr gdb_byte loongarch_default_breakpoint[] = {0x05, 0x00, 0x2a, 0x00}; | 
 | typedef BP_MANIPULATION (loongarch_default_breakpoint) loongarch_breakpoint; | 
 |  | 
 | /* Extract a set of required target features out of ABFD.  If ABFD is nullptr | 
 |    then a LOONGARCH_GDBARCH_FEATURES is returned in its default state.  */ | 
 |  | 
 | static struct loongarch_gdbarch_features | 
 | loongarch_features_from_bfd (const bfd *abfd) | 
 | { | 
 |   struct loongarch_gdbarch_features features; | 
 |  | 
 |   /* Now try to improve on the defaults by looking at the binary we are | 
 |      going to execute.  We assume the user knows what they are doing and | 
 |      that the target will match the binary.  Remember, this code path is | 
 |      only used at all if the target hasn't given us a description, so this | 
 |      is really a last ditched effort to do something sane before giving | 
 |      up.  */ | 
 |   if (abfd != nullptr && bfd_get_flavour (abfd) == bfd_target_elf_flavour) | 
 |     { | 
 |       unsigned char eclass = elf_elfheader (abfd)->e_ident[EI_CLASS]; | 
 |  | 
 |       if (eclass == ELFCLASS32) | 
 | 	features.xlen = 4; | 
 |       else if (eclass == ELFCLASS64) | 
 | 	features.xlen = 8; | 
 |       else | 
 | 	internal_error (__FILE__, __LINE__, | 
 | 			_("unknown ELF header class %d"), eclass); | 
 |     } | 
 |  | 
 |   return features; | 
 | } | 
 |  | 
 | /* Find a suitable default target description.  Use the contents of INFO, | 
 |    specifically the bfd object being executed, to guide the selection of a | 
 |    suitable default target description.  */ | 
 |  | 
 | static const struct target_desc * | 
 | loongarch_find_default_target_description (const struct gdbarch_info info) | 
 | { | 
 |   /* Extract desired feature set from INFO.  */ | 
 |   struct loongarch_gdbarch_features features | 
 |     = loongarch_features_from_bfd (info.abfd); | 
 |  | 
 |   /* If the XLEN field is still 0 then we got nothing useful from INFO.BFD, | 
 |      maybe there was no bfd object.  In this case we fall back to a minimal | 
 |      useful target with no floating point, the x-register size is selected | 
 |      based on the architecture from INFO.  */ | 
 |   if (features.xlen == 0) | 
 |     features.xlen = info.bfd_arch_info->bits_per_address == 32 ? 4 : 8; | 
 |  | 
 |   /* Now build a target description based on the feature set.  */ | 
 |   return loongarch_lookup_target_description (features); | 
 | } | 
 |  | 
 | /* Initialize the current architecture based on INFO  */ | 
 |  | 
 | static struct gdbarch * | 
 | loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) | 
 | { | 
 |   const struct target_desc *tdesc = info.target_desc; | 
 |  | 
 |   /* Ensure we always have a target description.  */ | 
 |   if (!tdesc_has_registers (tdesc)) | 
 |     tdesc = loongarch_find_default_target_description (info); | 
 |  | 
 |   const struct tdesc_feature *feature_cpu | 
 |     = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.base"); | 
 |   if (feature_cpu == nullptr) | 
 |     return nullptr; | 
 |  | 
 |   int xlen_bitsize = tdesc_register_bitsize (feature_cpu, "pc"); | 
 |   struct loongarch_gdbarch_features features; | 
 |   features.xlen = (xlen_bitsize / 8); | 
 |  | 
 |   size_t regnum = 0; | 
 |   tdesc_arch_data_up tdesc_data = tdesc_data_alloc (); | 
 |   loongarch_gdbarch_tdep *tdep = new loongarch_gdbarch_tdep; | 
 |  | 
 |   /* Validate the description provides the mandatory base registers | 
 |      and allocate their numbers.  */ | 
 |   bool valid_p = true; | 
 |   for (int i = 0; i < 32; i++) | 
 |     valid_p &= tdesc_numbered_register (feature_cpu, tdesc_data.get (), regnum++, | 
 | 					loongarch_r_normal_name[i] + 1); | 
 |   valid_p &= tdesc_numbered_register (feature_cpu, tdesc_data.get (), regnum++, "orig_a0"); | 
 |   valid_p &= tdesc_numbered_register (feature_cpu, tdesc_data.get (), regnum++, "pc"); | 
 |   valid_p &= tdesc_numbered_register (feature_cpu, tdesc_data.get (), regnum++, "badv"); | 
 |   if (!valid_p) | 
 |     return nullptr; | 
 |  | 
 |   /* LoongArch code is always little-endian.  */ | 
 |   info.byte_order_for_code = BFD_ENDIAN_LITTLE; | 
 |  | 
 |   /* Have a look at what the supplied (if any) bfd object requires of the | 
 |      target, then check that this matches with what the target is | 
 |      providing.  */ | 
 |   struct loongarch_gdbarch_features abi_features | 
 |     = loongarch_features_from_bfd (info.abfd); | 
 |  | 
 |   /* If the ABI_FEATURES xlen is 0 then this indicates we got no useful abi | 
 |      features from the INFO object.  In this case we just treat the | 
 |      hardware features as defining the abi.  */ | 
 |   if (abi_features.xlen == 0) | 
 |     abi_features = features; | 
 |  | 
 |   /* Find a candidate among the list of pre-declared architectures.  */ | 
 |   for (arches = gdbarch_list_lookup_by_info (arches, &info); | 
 |        arches != nullptr; | 
 |        arches = gdbarch_list_lookup_by_info (arches->next, &info)) | 
 |     { | 
 |       /* Check that the feature set of the ARCHES matches the feature set | 
 | 	 we are looking for.  If it doesn't then we can't reuse this | 
 | 	 gdbarch.  */ | 
 |       loongarch_gdbarch_tdep *candidate_tdep | 
 | 	= (loongarch_gdbarch_tdep *) gdbarch_tdep (arches->gdbarch); | 
 |  | 
 |       if (candidate_tdep->abi_features != abi_features) | 
 | 	continue; | 
 |  | 
 |       break; | 
 |     } | 
 |  | 
 |   if (arches != nullptr) | 
 |     return arches->gdbarch; | 
 |  | 
 |   /* None found, so create a new architecture from the information provided.  */ | 
 |   struct gdbarch *gdbarch = gdbarch_alloc (&info, tdep); | 
 |   tdep->abi_features = abi_features; | 
 |  | 
 |   /* Target data types.  */ | 
 |   set_gdbarch_short_bit (gdbarch, 16); | 
 |   set_gdbarch_int_bit (gdbarch, 32); | 
 |   set_gdbarch_long_bit (gdbarch, info.bfd_arch_info->bits_per_address); | 
 |   set_gdbarch_long_long_bit (gdbarch, 64); | 
 |   set_gdbarch_float_bit (gdbarch, 32); | 
 |   set_gdbarch_double_bit (gdbarch, 64); | 
 |   set_gdbarch_long_double_bit (gdbarch, 128); | 
 |   set_gdbarch_long_double_format (gdbarch, floatformats_ieee_quad); | 
 |   set_gdbarch_ptr_bit (gdbarch, info.bfd_arch_info->bits_per_address); | 
 |   set_gdbarch_char_signed (gdbarch, 0); | 
 |  | 
 |   info.target_desc = tdesc; | 
 |   info.tdesc_data = tdesc_data.get (); | 
 |  | 
 |   /* Information about registers.  */ | 
 |   set_gdbarch_num_regs (gdbarch, regnum); | 
 |   set_gdbarch_sp_regnum (gdbarch, LOONGARCH_SP_REGNUM); | 
 |   set_gdbarch_pc_regnum (gdbarch, LOONGARCH_PC_REGNUM); | 
 |  | 
 |   /* Finalise the target description registers.  */ | 
 |   tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data)); | 
 |  | 
 |   /* Functions handling dummy frames.  */ | 
 |   set_gdbarch_push_dummy_call (gdbarch, loongarch_push_dummy_call); | 
 |  | 
 |   /* Return value info  */ | 
 |   set_gdbarch_return_value (gdbarch, loongarch_return_value); | 
 |  | 
 |   /* Advance PC across function entry code.  */ | 
 |   set_gdbarch_skip_prologue (gdbarch, loongarch_skip_prologue); | 
 |  | 
 |   /* Stack grows downward.  */ | 
 |   set_gdbarch_inner_than (gdbarch, core_addr_lessthan); | 
 |  | 
 |   /* Frame info.  */ | 
 |   set_gdbarch_frame_align (gdbarch, loongarch_frame_align); | 
 |  | 
 |   /* Breakpoint manipulation.  */ | 
 |   set_gdbarch_software_single_step (gdbarch, loongarch_software_single_step); | 
 |   set_gdbarch_breakpoint_kind_from_pc (gdbarch, loongarch_breakpoint::kind_from_pc); | 
 |   set_gdbarch_sw_breakpoint_from_kind (gdbarch, loongarch_breakpoint::bp_from_kind); | 
 |  | 
 |   /* Frame unwinders. Use DWARF debug info if available, otherwise use our own unwinder.  */ | 
 |   set_gdbarch_dwarf2_reg_to_regnum (gdbarch, loongarch_dwarf2_reg_to_regnum); | 
 |   dwarf2_append_unwinders (gdbarch); | 
 |   frame_unwind_append_unwinder (gdbarch, &loongarch_frame_unwind); | 
 |  | 
 |   /* Hook in OS ABI-specific overrides, if they have been registered.  */ | 
 |   gdbarch_init_osabi (info, gdbarch); | 
 |  | 
 |   return gdbarch; | 
 | } | 
 |  | 
 | void _initialize_loongarch_tdep (); | 
 | void | 
 | _initialize_loongarch_tdep () | 
 | { | 
 |   gdbarch_register (bfd_arch_loongarch, loongarch_gdbarch_init, nullptr); | 
 | } |