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

📄 sh.c

📁 gcc-2.95.3 Linux下最常用的C编译器
💻 C
📖 第 1 页 / 共 5 页
字号:
/* Output routines for GCC for Hitachi Super-H.   Copyright (C) 1993-1998 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"#include "insn-attr.h"int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;#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 the trap_exit attribute for functions.   It specifies   a trap number to be used in a trapa instruction at function exit   (instead of an rte instruction).  */int trap_exit;/* This is used by the sp_switch attribute for functions.  It specifies   a variable holding the address of the stack the interrupt function   should switch to/from at entry/exit.  */rtx sp_switch;/* 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 set by #pragma nosave_low_regs.  This is useful on the SH3,   which has a separate set of low regs for User and Supervisor modes.   This should only be used for the lowest level of interrupts.  Higher levels   of interrupts must save the registers in case they themselves are   interrupted.  */int pragma_nosave_low_regs;/* 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;enum machine_mode sh_addr_diff_vec_mode;/* 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, FPUL_REGS, GENERAL_REGS,  FP0_REGS,FP_REGS, FP_REGS, FP_REGS,  FP_REGS, FP_REGS, FP_REGS, FP_REGS,  FP_REGS, FP_REGS, FP_REGS, FP_REGS,  FP_REGS, FP_REGS, FP_REGS, FP_REGS,  DF_REGS, DF_REGS, DF_REGS, DF_REGS,  DF_REGS, DF_REGS, DF_REGS, DF_REGS,  FPSCR_REGS,};char fp_reg_names[][5] ={  "fr0", "fr1", "fr2", "fr3", "fr4", "fr5", "fr6", "fr7",  "fr8", "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15",  "fpul",  "xd0","xd2","xd4", "xd6", "xd8", "xd10", "xd12", "xd14",};/* Provide reg_class from a letter such as appears in the machine   description.  */enum reg_class reg_class_from_letter[] ={  /* a */ ALL_REGS, /* b */ NO_REGS, /* c */ FPSCR_REGS, /* d */ DF_REGS,  /* e */ NO_REGS, /* f */ FP_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 */ FP0_REGS, /* x */ MAC_REGS,  /* y */ FPUL_REGS, /* z */ R0_REGS};int assembler_dialect;rtx get_fpscr_rtx ();void emit_sf_insn ();void emit_df_insn ();static void split_branches PROTO ((rtx));/* Print the operand address in x to the stream.  */voidprint_operand_address (stream, x)     FILE *stream;     rtx x;{  switch (GET_CODE (x))    {    case REG:    case SUBREG:      fprintf (stream, "@%s", reg_names[true_regnum (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[true_regnum (base)]);	    break;	  case REG:	  case SUBREG:	    {	      int base_num = true_regnum (base);	      int index_num = true_regnum (index);	      fprintf (stream, "@(r0,%s)",		       reg_names[MAX (base_num, index_num)]);	      break;	    }	  default:	    debug_rtx (x);	    abort ();	  }      }      break;    case PRE_DEC:      fprintf (stream, "@-%s", reg_names[true_regnum (XEXP (x, 0))]);      break;    case POST_INC:      fprintf (stream, "@%s+", reg_names[true_regnum (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 LOCAL_LABEL_PREFIX   '@'  print trap, 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.   'o'  output an operator.  */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, ASSEMBLER_DIALECT ? "/s" : ".s");      break;    case ',':      fprintf (stream, "%s", LOCAL_LABEL_PREFIX);      break;    case '@':      {	int interrupt_handler;	if ((lookup_attribute	     ("interrupt_handler",	      DECL_MACHINE_ATTRIBUTES (current_function_decl)))	    != NULL_TREE)	  interrupt_handler = 1;	else	  interrupt_handler = 0;	      if (trap_exit)	fprintf (stream, "trapa #%d", trap_exit);      else if (interrupt_handler)	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:	  if (GET_CODE (XEXP (x, 0)) != PRE_DEC	      && GET_CODE (XEXP (x, 0)) != POST_INC)	    x = adj_offsettable_operand (x, 4);	  print_operand_address (stream, XEXP (x, 0));	  break;	}      break;    case 'o':      switch (GET_CODE (x))	{	case PLUS:  fputs ("add", stream); break;	case MINUS: fputs ("sub", stream); break;	case MULT:  fputs ("mul", stream); break;	case DIV:   fputs ("div", stream); break;	}      break;    default:      switch (GET_CODE (x))	{	case REG:	  if (REGNO (x) >= FIRST_FP_REG && REGNO (x) <= LAST_FP_REG	      && GET_MODE_SIZE (GET_MODE (x)) > 4)	    fprintf ((stream), "d%s", reg_names[REGNO (x)]+1);	  else	    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;    }}static void force_into PROTO ((rtx, rtx));/* Like force_operand, but guarantees that VALUE ends up in TARGET.  */static voidforce_into (value, target)     rtx value, target;{  value = force_operand (value, target);  if (! rtx_equal_p (value, target))    emit_insn (gen_move_insn (target, value));}/* 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 (TARGET_HARD_SH4)    {      if (bytes < 12)	return 0;      else if (bytes == 12)	{	  tree entry_name;	  rtx func_addr_rtx;	  rtx r4 = gen_rtx (REG, SImode, 4);	  rtx r5 = gen_rtx (REG, SImode, 5);	  entry_name = get_identifier ("__movstrSI12_i4");	  func_addr_rtx	    = copy_to_mode_reg (Pmode,				gen_rtx_SYMBOL_REF (Pmode,						    IDENTIFIER_POINTER (entry_name)));	  force_into (XEXP (operands[0], 0), r4);	  force_into (XEXP (operands[1], 0), r5);	  emit_insn (gen_block_move_real_i4 (func_addr_rtx));	  return 1;	}      else if (! TARGET_SMALLCODE)	{	  tree entry_name;	  rtx func_addr_rtx;	  int dwords;	  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 (bytes & 4				       ? "__movstr_i4_odd"				       : "__movstr_i4_even");	  func_addr_rtx	    = copy_to_mode_reg (Pmode,				gen_rtx_SYMBOL_REF (Pmode,						    IDENTIFIER_POINTER (entry_name)));	  force_into (XEXP (operands[0], 0), r4);	  force_into (XEXP (operands[1], 0), r5);	  dwords = bytes >> 3;	  emit_insn (gen_move_insn (r6, GEN_INT (dwords - 1)));	  emit_insn (gen_block_lump_real_i4 (func_addr_rtx));	  return 1;	}      else	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)));      force_into (XEXP (operands[0], 0), r4);      force_into (XEXP (operands[1], 0), r5);      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)));      force_into (XEXP (operands[0], 0), r4);      force_into (XEXP (operands[1], 0), r5);      /* 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;{  if (! reload_in_progress && ! reload_completed)    {      /* Copy the source to a register if both operands aren't registers.  */      if (! register_operand (operands[0], mode)	  && ! register_operand (operands[1], mode))	operands[1] = copy_to_mode_reg (mode, operands[1]);      /* This case can happen while generating code to move the result	 of a library call to the target.  Reject `st r0,@(rX,rY)' because	 reload will fail to find a spill register for rX, since r0 is already	 being used for the source.  */      else if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == 0	       && GET_CODE (operands[0]) == MEM	       && GET_CODE (XEXP (operands[0], 0)) == PLUS	       && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == REG)	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.  */rtx

⌨️ 快捷键说明

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