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

📄 sh.c

📁 GCC编译器源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* Output routines for GCC for Hitachi Super-H.   Copyright (C) 1993, 1994, 1995, 1996, 1997 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;rtx *uid_align;int uid_align_max;/* 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,};/* 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 */ 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};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:      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 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.  */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 '@':      if (trap_exit)	fprintf (stream, "trapa #%d", trap_exit);      else 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;{  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.  */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))      || TARGET_SH3E && GET_MODE_CLASS (mode) == MODE_FLOAT)    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;{  enum machine_mode mode = GET_MODE (sh_compare_op0);  rtx insn;  if (mode == VOIDmode)    mode = GET_MODE (sh_compare_op1);  if (code != EQ      || mode == DImode      || (TARGET_SH3E && GET_MODE_CLASS (mode) == MODE_FLOAT))    {      /* Force args into regs, since we can't use constants here.  */      sh_compare_op0 = force_reg (mode, sh_compare_op0);      if (sh_compare_op1 != const0_rtx	  || code == GTU  || code == GEU	  || (TARGET_SH3E && GET_MODE_CLASS (mode) == MODE_FLOAT))	sh_compare_op1 = force_reg (mode, sh_compare_op1);    }  if (TARGET_SH3E && GET_MODE_CLASS (mode) == MODE_FLOAT && code == GE)    {      from_compare (operands, GT);      insn = gen_ieee_ccmpeqsf_t (sh_compare_op0, sh_compare_op1);    }  else    insn = gen_rtx (SET, VOIDmode,		    gen_rtx (REG, SImode, 18),		    gen_rtx (code, SImode, sh_compare_op0, sh_compare_op1));  emit_insn (insn);}/* 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))

⌨️ 快捷键说明

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