⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sh.c

📁 GUN开源阻止下的编译器GCC
💻 C
📖 第 1 页 / 共 4 页
字号:
/* Output routines for GCC for Hitachi Super-H.   Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.This file is part of GNU CC.GNU CC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2, or (at your option)any later version.GNU CC is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING.  If not, write tothe Free Software Foundation, 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA.  *//* Contributed by Steve Chamberlain (sac@cygnus.com).   Improved by Jim Wilson (wilson@cygnus.com).  */#include "config.h"#include <stdio.h>#include "rtl.h"#include "tree.h"#include "flags.h"#include "insn-flags.h"#include "expr.h"#include "regs.h"#include "hard-reg-set.h"#include "output.h"#define MSW (TARGET_LITTLE_ENDIAN ? 1 : 0)#define LSW (TARGET_LITTLE_ENDIAN ? 0 : 1)/* ??? The pragma interrupt support will not work for SH3.  *//* This is set by #pragma interrupt and #pragma trapa, and causes gcc to   output code for the next function appropriate for an interrupt handler.  */int pragma_interrupt;/* This is set by #pragma trapa, and is similar to the above, except that   the compiler doesn't emit code to preserve all registers.  */static int pragma_trapa;/* This is used for communication between SETUP_INCOMING_VARARGS and   sh_expand_prologue.  */int current_function_anonymous_args;/* Global variables from toplev.c and final.c that are used within, but   not declared in any header file.  */extern char *version_string;extern int *insn_addresses;/* Global variables for machine-dependent things. *//* Which cpu are we scheduling for.  */enum processor_type sh_cpu;/* Saved operands from the last compare to use when we generate an scc   or bcc insn.  */rtx sh_compare_op0;rtx sh_compare_op1;/* Provides the class number of the smallest class containing   reg number.  */int regno_reg_class[FIRST_PSEUDO_REGISTER] ={  R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,  GENERAL_REGS, PR_REGS, T_REGS, NO_REGS,  MAC_REGS, MAC_REGS,};/* Provide reg_class from a letter such as appears in the machine   description.  */enum reg_class reg_class_from_letter[] ={  /* a */ NO_REGS, /* b */ NO_REGS, /* c */ NO_REGS, /* d */ NO_REGS,  /* e */ NO_REGS, /* f */ NO_REGS, /* g */ NO_REGS, /* h */ NO_REGS,  /* i */ NO_REGS, /* j */ NO_REGS, /* k */ NO_REGS, /* l */ PR_REGS,  /* m */ NO_REGS, /* n */ NO_REGS, /* o */ NO_REGS, /* p */ NO_REGS,  /* q */ NO_REGS, /* r */ NO_REGS, /* s */ NO_REGS, /* t */ T_REGS,  /* u */ NO_REGS, /* v */ NO_REGS, /* w */ NO_REGS, /* x */ MAC_REGS,  /* y */ NO_REGS, /* z */ R0_REGS};/* Print the operand address in x to the stream.  */voidprint_operand_address (stream, x)     FILE *stream;     rtx x;{  switch (GET_CODE (x))    {    case REG:      fprintf (stream, "@%s", reg_names[REGNO (x)]);      break;    case PLUS:      {	rtx base = XEXP (x, 0);	rtx index = XEXP (x, 1);	switch (GET_CODE (index))	  {	  case CONST_INT:	    fprintf (stream, "@(%d,%s)", INTVAL (index),		     reg_names[REGNO (base)]);	    break;	  case REG:	    fprintf (stream, "@(r0,%s)",		     reg_names[MAX (REGNO (base), REGNO (index))]);	    break;	  default:	    debug_rtx (x);	    abort ();	  }      }      break;    case PRE_DEC:      fprintf (stream, "@-%s", reg_names[REGNO (XEXP (x, 0))]);      break;    case POST_INC:      fprintf (stream, "@%s+", reg_names[REGNO (XEXP (x, 0))]);      break;    default:      output_addr_const (stream, x);      break;    }}/* Print operand x (an rtx) in assembler syntax to file stream   according to modifier code.   '.'  print a .s if insn needs delay slot   '@'  print rte or rts depending upon pragma interruptness   '#'  output a nop if there is nothing to put in the delay slot   'O'  print a constant without the #   'R'  print the LSW of a dp value - changes if in little endian   'S'  print the MSW of a dp value - changes if in little endian   'T'  print the next word of a dp value - same as 'R' in big endian mode.  */voidprint_operand (stream, x, code)     FILE *stream;     rtx x;     int code;{  switch (code)    {    case '.':      if (final_sequence	  && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0)))	fprintf (stream, ".s");      break;    case '@':      if (pragma_interrupt)	fprintf (stream, "rte");      else	fprintf (stream, "rts");      break;    case '#':      /* Output a nop if there's nothing in the delay slot.  */      if (dbr_sequence_length () == 0)	fprintf (stream, "\n\tnop");      break;    case 'O':      output_addr_const (stream, x);      break;    case 'R':      fputs (reg_names[REGNO (x) + LSW], (stream));      break;    case 'S':      fputs (reg_names[REGNO (x) + MSW], (stream));      break;    case 'T':      /* Next word of a double.  */      switch (GET_CODE (x))	{	case REG:	  fputs (reg_names[REGNO (x) + 1], (stream));	  break;	case MEM:	  print_operand_address (stream,				 XEXP (adj_offsettable_operand (x, 4), 0));	  break;	}      break;    default:      switch (GET_CODE (x))	{	case REG:	  fputs (reg_names[REGNO (x)], (stream));	  break;	case MEM:	  output_address (XEXP (x, 0));	  break;	default:	  fputc ('#', stream);	  output_addr_const (stream, x);	  break;	}      break;    }}/* Emit code to perform a block move.  Choose the best method.   OPERANDS[0] is the destination.   OPERANDS[1] is the source.   OPERANDS[2] is the size.   OPERANDS[3] is the alignment safe to use.  */intexpand_block_move (operands)     rtx *operands;{  int align = INTVAL (operands[3]);  int constp = (GET_CODE (operands[2]) == CONST_INT);  int bytes = (constp ? INTVAL (operands[2]) : 0);  /* If it isn't a constant number of bytes, or if it doesn't have 4 byte     alignment, or if it isn't a multiple of 4 bytes, then fail.  */  if (! constp || align < 4 || (bytes % 4 != 0))    return 0;  if (bytes < 64)    {      char entry[30];      tree entry_name;      rtx func_addr_rtx;      rtx r4 = gen_rtx (REG, SImode, 4);      rtx r5 = gen_rtx (REG, SImode, 5);      sprintf (entry, "__movstrSI%d", bytes);      entry_name = get_identifier (entry);      func_addr_rtx	= copy_to_mode_reg (Pmode,			    gen_rtx (SYMBOL_REF, Pmode,				     IDENTIFIER_POINTER (entry_name)));      emit_insn (gen_move_insn (r4, XEXP (operands[0], 0)));      emit_insn (gen_move_insn (r5, XEXP (operands[1], 0)));      emit_insn (gen_block_move_real (func_addr_rtx));      return 1;    }  /* This is the same number of bytes as a memcpy call, but to a different     less common function name, so this will occasionally use more space.  */  if (! TARGET_SMALLCODE)    {      tree entry_name;      rtx func_addr_rtx;      int final_switch, while_loop;      rtx r4 = gen_rtx (REG, SImode, 4);      rtx r5 = gen_rtx (REG, SImode, 5);      rtx r6 = gen_rtx (REG, SImode, 6);      entry_name = get_identifier ("__movstr");      func_addr_rtx	= copy_to_mode_reg (Pmode,			    gen_rtx (SYMBOL_REF, Pmode,				     IDENTIFIER_POINTER (entry_name)));      emit_insn (gen_move_insn (r4, XEXP (operands[0], 0)));      emit_insn (gen_move_insn (r5, XEXP (operands[1], 0)));      /* r6 controls the size of the move.  16 is decremented from it	 for each 64 bytes moved.  Then the negative bit left over is used	 as an index into a list of move instructions.  e.g., a 72 byte move	 would be set up with size(r6) = 14, for one iteration through the	 big while loop, and a switch of -2 for the last part.  */      final_switch = 16 - ((bytes / 4) % 16);      while_loop = ((bytes / 4) / 16 - 1) * 16;      emit_insn (gen_move_insn (r6, GEN_INT (while_loop + final_switch)));      emit_insn (gen_block_lump_real (func_addr_rtx));      return 1;    }  return 0;}/* Prepare operands for a move define_expand; specifically, one of the   operands must be in a register.  */intprepare_move_operands (operands, mode)     rtx operands[];     enum machine_mode mode;{  /* Copy the source to a register if both operands aren't registers.  */  if (! reload_in_progress && ! reload_completed      && ! register_operand (operands[0], mode)      && ! register_operand (operands[1], mode))    operands[1] = copy_to_mode_reg (mode, operands[1]);  return 0;}/* Prepare the operands for an scc instruction; make sure that the   compare has been done.  */rtxprepare_scc_operands (code)     enum rtx_code code;{  rtx t_reg = gen_rtx (REG, SImode, T_REG);  enum rtx_code oldcode = code;  enum machine_mode mode;  /* First need a compare insn.  */  switch (code)    {    case NE:      /* It isn't possible to handle this case.  */      abort ();    case LT:      code = GT;      break;    case LE:      code = GE;      break;    case LTU:      code = GTU;      break;    case LEU:      code = GEU;      break;    }  if (code != oldcode)    {      rtx tmp = sh_compare_op0;      sh_compare_op0 = sh_compare_op1;      sh_compare_op1 = tmp;    }  mode = GET_MODE (sh_compare_op0);  if (mode == VOIDmode)    mode = GET_MODE (sh_compare_op1);  sh_compare_op0 = force_reg (mode, sh_compare_op0);  if (code != EQ && code != NE      && (sh_compare_op1 != const0_rtx	  || code == GTU  || code == GEU || code == LTU || code == LEU))    sh_compare_op1 = force_reg (mode, sh_compare_op1);  emit_insn (gen_rtx (SET, VOIDmode, t_reg,		      gen_rtx (code, SImode, sh_compare_op0,			       sh_compare_op1)));  return t_reg;}/* Called from the md file, set up the operands of a compare instruction.  */voidfrom_compare (operands, code)     rtx *operands;     int code;{  if (code != EQ && code != NE)    {      /* Force args into regs, since we can't use constants here.  */      sh_compare_op0 = force_reg (SImode, sh_compare_op0);      if (sh_compare_op1 != const0_rtx	  || code == GTU  || code == GEU || code == LTU || code == LEU)	sh_compare_op1 = force_reg (SImode, sh_compare_op1);    }  operands[1] = sh_compare_op0;  operands[2] = sh_compare_op1;}/* Functions to output assembly code.  *//* Return a sequence of instructions to perform DI or DF move.   Since the SH cannot move a DI or DF in one instruction, we have   to take care when we see overlapping source and dest registers.  */char *output_movedouble (insn, operands, mode)     rtx insn;     rtx operands[];     enum machine_mode mode;{  rtx dst = operands[0];  rtx src = operands[1];  if (GET_CODE (dst) == MEM      && GET_CODE (XEXP (dst, 0)) == PRE_DEC)    return "mov.l	%T1,%0\n\tmov.l	%1,%0";  if (register_operand (dst, mode)      && register_operand (src, mode))    {      if (REGNO (src) == MACH_REG)	return "sts	mach,%S0\n\tsts	macl,%R0";      /* When mov.d r1,r2 do r2->r3 then r1->r2;         when mov.d r1,r0 do r1->r0 then r2->r1.  */      if (REGNO (src) + 1 == REGNO (dst))	return "mov	%T1,%T0\n\tmov	%1,%0";      else	return "mov	%1,%0\n\tmov	%T1,%T0";    }  else if (GET_CODE (src) == CONST_INT)    {      if (INTVAL (src) < 0)	output_asm_insn ("mov	#-1,%S0", operands);      else	output_asm_insn ("mov	#0,%S0", operands);      return "mov	%1,%R0";    }  else if (GET_CODE (src) == MEM)    {      int ptrreg = -1;      int dreg = REGNO (dst);      rtx inside = XEXP (src, 0);      if (GET_CODE (inside) == REG)	ptrreg = REGNO (inside);      else if (GET_CODE (inside) == SUBREG)	ptrreg = REGNO (SUBREG_REG (inside)) + SUBREG_WORD (inside);      else if (GET_CODE (inside) == PLUS)	{	  ptrreg = REGNO (XEXP (inside, 0));	  /* ??? A r0+REG address shouldn't be possible here, because it isn't	     an offsettable address.  Unfortunately, offsettable addresses use	     QImode to check the offset, and a QImode offsettable address	     requires r0 for the other operand, which is not currently	     supported, so we can't use the 'o' constraint.	     Thus we must check for and handle r0+REG addresses here.	     We punt for now, since this is likely very rare.  */	  if (GET_CODE (XEXP (inside, 1)) == REG)	    abort ();	}      else if (GET_CODE (inside) == LABEL_REF)	return "mov.l	%1,%0\n\tmov.l	%1+4,%T0";      else if (GET_CODE (inside) == POST_INC)	return "mov.l	%1,%0\n\tmov.l	%1,%T0";      else	abort ();      /* Work out the safe way to copy.  Copy into the second half first.  */      if (dreg == ptrreg)	return "mov.l	%T1,%T0\n\tmov.l	%1,%0";    }  return "mov.l	%1,%0\n\tmov.l	%T1,%T0";}/* Print an instruction which would have gone into a delay slot after   another instruction, but couldn't because the other instruction expanded   into a sequence where putting the slot insn at the end wouldn't work.  */static voidprint_slot (insn)     rtx insn;{  final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 0, 1);  INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;}/* We can't tell if we need a register as a scratch for the jump   until after branch shortening, and then it's too late to allocate a   register the 'proper' way.  These instruction sequences are rare   anyway, so to avoid always using a reg up from our limited set, we'll   grab one when we need one on output.  *//* ??? Should fix compiler so that using a clobber scratch in jump   instructions works, and then this will be unnecessary.  */char *

⌨️ 快捷键说明

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