sparc.c

来自「GCC编译器源代码」· C语言 代码 · 共 2,284 行 · 第 1/5 页

C
2,284
字号
{  register rtx addr;  register rtx base;  register rtx offset;  if (GET_CODE (mem) != MEM)    return 0;	/* It's gotta be a MEM! */  addr = XEXP (mem, 0);  /* Now that all misaligned double parms are copied on function entry,     we can assume any 64-bit object is 64-bit aligned except those which     are at unaligned offsets from the stack or frame pointer.  If the     TARGET_UNALIGNED_DOUBLES switch is given, we do not make this     assumption.  */  /* See what register we use in the address.  */  base = 0;  if (GET_CODE (addr) == PLUS)    {      if (GET_CODE (XEXP (addr, 0)) == REG	  && GET_CODE (XEXP (addr, 1)) == CONST_INT)	{	  base = XEXP (addr, 0);	  offset = XEXP (addr, 1);	}    }  else if (GET_CODE (addr) == REG)    {      base = addr;      offset = const0_rtx;    }  /* If it's the stack or frame pointer, check offset alignment.     We can have improper alignment in the function entry code.  */  if (base      && (REGNO (base) == FRAME_POINTER_REGNUM	  || REGNO (base) == STACK_POINTER_REGNUM))    {      if (((INTVAL (offset) - SPARC_STACK_BIAS) & 0x7) == 0)	return 1;    }  /* Anything else we know is properly aligned unless TARGET_UNALIGNED_DOUBLES     is true, in which case we can only assume that an access is aligned if     it is to a constant address, or the address involves a LO_SUM.     We used to assume an address was aligned if MEM_IN_STRUCT_P was true.     That assumption was deleted so that gcc generated code can be used with     memory allocators that only guarantee 4 byte alignment.  */  else if (! TARGET_UNALIGNED_DOUBLES || CONSTANT_P (addr)	   || GET_CODE (addr) == LO_SUM)    return 1;  /* An obviously unaligned address.  */  return 0;}enum optype { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP };/* Output assembler code to perform a doubleword move insn   with operands OPERANDS.  This is very similar to the following   output_move_quad function.  */char *output_move_double (operands)     rtx *operands;{  register rtx op0 = operands[0];  register rtx op1 = operands[1];  register enum optype optype0;  register enum optype optype1;  rtx latehalf[2];  rtx addreg0 = 0;  rtx addreg1 = 0;  int highest_first = 0;  int no_addreg1_decrement = 0;  /* First classify both operands.  */  if (REG_P (op0))    optype0 = REGOP;  else if (offsettable_memref_p (op0))    optype0 = OFFSOP;  else if (GET_CODE (op0) == MEM)    optype0 = MEMOP;  else    optype0 = RNDOP;  if (REG_P (op1))    optype1 = REGOP;  else if (CONSTANT_P (op1))    optype1 = CNSTOP;  else if (offsettable_memref_p (op1))    optype1 = OFFSOP;  else if (GET_CODE (op1) == MEM)    optype1 = MEMOP;  else    optype1 = RNDOP;  /* Check for the cases that the operand constraints are not     supposed to allow to happen.  Abort if we get one,     because generating code for these cases is painful.  */  if (optype0 == RNDOP || optype1 == RNDOP      || (optype0 == MEM && optype1 == MEM))    abort ();  /* If an operand is an unoffsettable memory ref, find a register     we can increment temporarily to make it refer to the second word.  */  if (optype0 == MEMOP)    addreg0 = find_addr_reg (XEXP (op0, 0));  if (optype1 == MEMOP)    addreg1 = find_addr_reg (XEXP (op1, 0));  /* Ok, we can do one word at a time.     Set up in LATEHALF the operands to use for the     high-numbered (least significant) word and in some cases alter the     operands in OPERANDS to be suitable for the low-numbered word.  */  if (optype0 == REGOP)    latehalf[0] = gen_rtx (REG, SImode, REGNO (op0) + 1);  else if (optype0 == OFFSOP)    latehalf[0] = adj_offsettable_operand (op0, 4);  else    latehalf[0] = op0;  if (optype1 == REGOP)    latehalf[1] = gen_rtx (REG, SImode, REGNO (op1) + 1);  else if (optype1 == OFFSOP)    latehalf[1] = adj_offsettable_operand (op1, 4);  else if (optype1 == CNSTOP)    {      if (TARGET_ARCH64)	{	  if (arith_double_operand (op1, DImode))	    {	      operands[1] = gen_rtx (CONST_INT, VOIDmode,				     CONST_DOUBLE_LOW (op1));	      return "mov %1,%0";	    }	  else	    {	      /* The only way to handle CONST_DOUBLEs or other 64 bit		 constants here is to use a temporary, such as is done		 for the V9 DImode sethi insn pattern.  This is not		 a practical solution, so abort if we reach here.		 The md file should always force such constants to		 memory.  */	      abort ();	    }	}      else	split_double (op1, &operands[1], &latehalf[1]);    }  else    latehalf[1] = op1;  /* Easy case: try moving both words at once.  Check for moving between     an even/odd register pair and a memory location.  */  if ((optype0 == REGOP && optype1 != REGOP && optype1 != CNSTOP       && (TARGET_ARCH64 || (REGNO (op0) & 1) == 0))      || (optype0 != REGOP && optype0 != CNSTOP && optype1 == REGOP	  && (TARGET_ARCH64 || (REGNO (op1) & 1) == 0)))    {      register rtx mem,reg;      if (optype0 == REGOP)	mem = op1, reg = op0;      else	mem = op0, reg = op1;      /* In v9, ldd can be used for word aligned addresses, so technically	 some of this logic is unneeded.  We still avoid ldd if the address	 is obviously unaligned though.  */      if (mem_aligned_8 (mem)	  /* If this is a floating point register higher than %f31,	     then we *must* use an aligned load, since `ld' will not accept	     the register number.  */	  || (TARGET_V9 && REGNO (reg) >= 64))	{	  if (FP_REG_P (reg) || ! TARGET_ARCH64)	    return (mem == op1 ? "ldd %1,%0" : "std %1,%0");	  else	    return (mem == op1 ? "ldx %1,%0" : "stx %1,%0");	}    }  if (TARGET_ARCH64)    {      if (optype0 == REGOP && optype1 == REGOP)	{	  if (FP_REG_P (op0))	    return "fmovd %1,%0";	  else	    return "mov %1,%0";	}    }  /* If the first move would clobber the source of the second one,     do them in the other order.  */  /* Overlapping registers.  */  if (optype0 == REGOP && optype1 == REGOP      && REGNO (op0) == REGNO (latehalf[1]))    {      /* Do that word.  */      output_asm_insn (singlemove_string (latehalf), latehalf);      /* Do low-numbered word.  */      return singlemove_string (operands);    }  /* Loading into a register which overlaps a register used in the address.  */  else if (optype0 == REGOP && optype1 != REGOP	   && reg_overlap_mentioned_p (op0, op1))    {      /* If both halves of dest are used in the src memory address,	 add the two regs and put them in the low reg (op0).	 Then it works to load latehalf first.  */      if (reg_mentioned_p (op0, XEXP (op1, 0))	  && reg_mentioned_p (latehalf[0], XEXP (op1, 0)))	{	  rtx xops[2];	  xops[0] = latehalf[0];	  xops[1] = op0;	  output_asm_insn ("add %1,%0,%1", xops);	  operands[1] = gen_rtx (MEM, DImode, op0);	  latehalf[1] = adj_offsettable_operand (operands[1], 4);	  addreg1 = 0;	  highest_first = 1;	}      /* Only one register in the dest is used in the src memory address,	 and this is the first register of the dest, so we want to do	 the late half first here also.  */      else if (! reg_mentioned_p (latehalf[0], XEXP (op1, 0)))	highest_first = 1;      /* Only one register in the dest is used in the src memory address,	 and this is the second register of the dest, so we want to do	 the late half last.  If addreg1 is set, and addreg1 is the same	 register as latehalf, then we must suppress the trailing decrement,	 because it would clobber the value just loaded.  */      else if (addreg1 && reg_mentioned_p (addreg1, latehalf[0]))	no_addreg1_decrement = 1;    }  /* Normal case: do the two words, low-numbered first.     Overlap case (highest_first set): do high-numbered word first.  */  if (! highest_first)    output_asm_insn (singlemove_string (operands), operands);  /* Make any unoffsettable addresses point at high-numbered word.  */  if (addreg0)    output_asm_insn ("add %0,0x4,%0", &addreg0);  if (addreg1)    output_asm_insn ("add %0,0x4,%0", &addreg1);  /* Do that word.  */  output_asm_insn (singlemove_string (latehalf), latehalf);  /* Undo the adds we just did.  */  if (addreg0)    output_asm_insn ("add %0,-0x4,%0", &addreg0);  if (addreg1 && ! no_addreg1_decrement)    output_asm_insn ("add %0,-0x4,%0", &addreg1);  if (highest_first)    output_asm_insn (singlemove_string (operands), operands);  return "";}/* Output assembler code to perform a quadword move insn   with operands OPERANDS.  This is very similar to the preceding   output_move_double function.  */char *output_move_quad (operands)     rtx *operands;{  register rtx op0 = operands[0];  register rtx op1 = operands[1];  register enum optype optype0;  register enum optype optype1;  rtx wordpart[4][2];  rtx addreg0 = 0;  rtx addreg1 = 0;  /* First classify both operands.  */  if (REG_P (op0))    optype0 = REGOP;  else if (offsettable_memref_p (op0))    optype0 = OFFSOP;  else if (GET_CODE (op0) == MEM)    optype0 = MEMOP;  else    optype0 = RNDOP;  if (REG_P (op1))    optype1 = REGOP;  else if (CONSTANT_P (op1))    optype1 = CNSTOP;  else if (offsettable_memref_p (op1))    optype1 = OFFSOP;  else if (GET_CODE (op1) == MEM)    optype1 = MEMOP;  else    optype1 = RNDOP;  /* Check for the cases that the operand constraints are not     supposed to allow to happen.  Abort if we get one,     because generating code for these cases is painful.  */  if (optype0 == RNDOP || optype1 == RNDOP      || (optype0 == MEM && optype1 == MEM))    abort ();  /* If an operand is an unoffsettable memory ref, find a register     we can increment temporarily to make it refer to the later words.  */  if (optype0 == MEMOP)    addreg0 = find_addr_reg (XEXP (op0, 0));  if (optype1 == MEMOP)    addreg1 = find_addr_reg (XEXP (op1, 0));  /* Ok, we can do one word at a time.     Set up in wordpart the operands to use for each word of the arguments.  */  if (optype0 == REGOP)    {      wordpart[0][0] = gen_rtx (REG, word_mode, REGNO (op0) + 0);      wordpart[1][0] = gen_rtx (REG, word_mode, REGNO (op0) + 1);      if (TARGET_ARCH32)	{	  wordpart[2][0] = gen_rtx (REG, word_mode, REGNO (op0) + 2);	  wordpart[3][0] = gen_rtx (REG, word_mode, REGNO (op0) + 3);	}    }  else if (optype0 == OFFSOP)    {      wordpart[0][0] = adj_offsettable_operand (op0, 0);      if (TARGET_ARCH32)	{	  wordpart[1][0] = adj_offsettable_operand (op0, 4);	  wordpart[2][0] = adj_offsettable_operand (op0, 8);	  wordpart[3][0] = adj_offsettable_operand (op0, 12);	}      else	wordpart[1][0] = adj_offsettable_operand (op0, 8);    }  else    {      wordpart[0][0] = op0;      wordpart[1][0] = op0;      wordpart[2][0] = op0;      wordpart[3][0] = op0;    }  if (optype1 == REGOP)    {      wordpart[0][1] = gen_rtx (REG, word_mode, REGNO (op1) + 0);      wordpart[1][1] = gen_rtx (REG, word_mode, REGNO (op1) + 1);      if (TARGET_ARCH32)	{	  wordpart[2][1] = gen_rtx (REG, word_mode, REGNO (op1) + 2);	  wordpart[3][1] = gen_rtx (REG, word_mode, REGNO (op1) + 3);	}    }  else if (optype1 == OFFSOP)    {      wordpart[0][1] = adj_offsettable_operand (op1, 0);      if (TARGET_ARCH32)	{	  wordpart[1][1] = adj_offsettable_operand (op1, 4);	  wordpart[2][1] = adj_offsettable_operand (op1, 8);	  wordpart[3][1] = adj_offsettable_operand (op1, 12);	}      else	wordpart[1][1] = adj_offsettable_operand (op1, 8);    }  else if (optype1 == CNSTOP)    {      REAL_VALUE_TYPE r;      long l[4];      /* This only works for TFmode floating point constants.  */      if (GET_CODE (op1) != CONST_DOUBLE || GET_MODE (op1) != TFmode)	abort ();      REAL_VALUE_FROM_CONST_DOUBLE (r, op1);      REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, l);            wordpart[0][1] = GEN_INT (l[0]);      wordpart[1][1] = GEN_INT (l[1]);      wordpart[2][1] = GEN_INT (l[2]);      wordpart[3][1] = GEN_INT (l[3]);    }  else    {      wordpart[0][1] = op1;      wordpart[1][1] = op1;      wordpart[2][1] = op1;      wordpart[3][1] = op1;    }  /* Easy case: try moving the quad as two pairs.  Check for moving between     an even/odd register pair and a memory location.     Also handle new v9 fp regs here.  */  /* ??? Should also handle the case of non-offsettable addresses here.     We can at least do the first pair as a ldd/std, and then do the third     and fourth words individually.  */  if ((optype0 == REGOP && optype1 == OFFSOP && (REGNO (op0) & 1) == 0)      || (optype0 == OFFSOP && optype1 == REGOP && (REGNO (op1) & 1) == 0))    {      rtx mem, reg;      if (optype0 == REGOP)	mem = op1, reg = op0;      else	mem = op0, reg = op1;      if (mem_aligned_8 (mem)	  /* If this is a floating point register higher than %f31,	     then we *must* use an aligned load, since `ld' will not accept	     the register number.  */	  || (TARGET_V9 && REGNO (reg) >= SPARC_FIRST_V9_FP_REG))	{	  if (TARGET_V9 && FP_REG_P (reg) && TARGET_HARD_QUAD)	    {	      if ((REGNO (reg) & 3) != 0)		abort ();	      /* ??? Can `mem' have an inappropriate alignment here?  */	      return (mem == op1 ? "ldq %1,%0" : "stq %1,%0");	    }	  operands[2] = adj_offsettable_operand (mem, 8);	  /* ??? In arch64 case, shouldn't we use ldd/std for fp regs.  */	  if (mem == op1)	    return TARGET_ARCH64 ? "ldx %1,%0;ldx %2,%R0" : "ldd %1,%0;ldd %2,%S0";	  else	    return TARGET_ARCH64 ? "stx %1,%0;stx %R1,%2" : "std %1,%0;std %S1,%2";	}    }  /* If the first move would clobber the source of the second one,     do them in the other order.  */  /* Overlapping registers?  */  if (TARGET_ARCH32)    {      if (optype0 == REGOP && optype1 == REGOP	  && (REGNO (op0) == REGNO (wordpart[1][3])	      || REGNO (op0) == REGNO (wordpart[1][2])	      || REGNO (op0)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?