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

📄 arm.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 3 页
字号:
/* Output routines for GCC for ARM/RISCiX.   Copyright (C) 1991 Free Software Foundation, Inc.   Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)   	      and Martin Simmons (@harleqn.co.uk).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, 675 Mass Ave, Cambridge, MA 02139, USA.  */#include <stdio.h>#include "assert.h"#include "config.h"#include "rtl.h"#include "regs.h"#include "hard-reg-set.h"#include "real.h"#include "insn-config.h"#include "conditions.h"#include "insn-flags.h"#include "output.h"#include "insn-attr.h"#include "flags.h"/* The maximum number of insns skipped which will be conditionalised if   possible.  */#define MAX_INSNS_SKIPPED  5/* Some function declarations.  */extern FILE *asm_out_file;extern char *output_multi_immediate ();extern char *arm_output_asm_insn ();extern void arm_increase_location ();/* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we   must report the mode of the memory reference from PRINT_OPERAND to   PRINT_OPERAND_ADDRESS.  */int output_memory_reference_mode;/* Nonzero if the prologue must setup `fp'.  */int current_function_anonymous_args;/* Location counter of .text segment.  */int arm_text_location = 0;/* A hash table is used to store text segment labels and their associated   offset from the start of the text segment.  */struct label_offset{  char *name;  int offset;  struct label_offset *cdr;};#define LABEL_HASH_SIZE  257static struct label_offset *offset_table[LABEL_HASH_SIZE];/* For an explanation of these variables, see final_prescan_insn below.  */int arm_ccfsm_state;int arm_current_cc;rtx arm_target_insn;int arm_target_label;char *arm_condition_codes[];/* Return the number of mov instructions needed to get the constant VALUE into   a register.  */intarm_const_nmoves (value)     register int value;{  register int i;  if (value == 0)    return (1);  for (i = 0; value; i++, value &= ~0xff)    while ((value & 3) == 0)      value = (value >> 2) | ((value & 3) << 30);  return (i);} /* arm_const_nmoves *//* Return TRUE if int I is a valid immediate ARM constant.  */intconst_ok_for_arm (i)     int i;{  unsigned int mask = ~0xFF;  do    {      if ((i & mask) == 0)	return(TRUE);      mask = (mask << 2) | (mask >> (32 - 2));    } while (mask != ~0xFF);  return (FALSE);} /* const_ok_for_arm *//* Return TRUE if rtx X is a valid immediate FPU constant. */intconst_double_rtx_ok_for_fpu (x)     rtx x;{  double d;  union real_extract u;  u.i[0] = CONST_DOUBLE_LOW(x);  u.i[1] = CONST_DOUBLE_HIGH(x);  d = u.d;  return (d == 0.0 || d == 1.0 || d == 2.0 || d == 3.0	  || d == 4.0 || d == 5.0 || d == 0.5 || d == 10.0);} /* const_double_rtx_ok_for_fpu *//* Predicates for `match_operand' and `match_operator'.  *//* Return TRUE for valid operands for the rhs of an ARM instruction.  */intarm_rhs_operand (op, mode)     rtx op;     enum machine_mode mode;{  return (register_operand (op, mode)	  || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))));} /* arm_rhs_operand *//* Return TRUE for valid operands for the rhs of an FPU instruction.  */intfpu_rhs_operand (op, mode)     rtx op;     enum machine_mode mode;{  if (register_operand (op, mode))    return(TRUE);  else if (GET_CODE (op) == CONST_DOUBLE)    return (const_double_rtx_ok_for_fpu (op));  else return (FALSE);} /* fpu_rhs_operand *//* Return nonzero if OP is a constant power of two.  */intpower_of_two_operand (op, mode)     rtx op;     enum machine_mode mode;{  if (GET_CODE (op) == CONST_INT)    {      int value = INTVAL(op);      return (value != 0  &&  (value & (value-1)) == 0);    }  return (FALSE);} /* power_of_two_operand *//* Return TRUE for a valid operand of a DImode operation.   Either: REG, CONST_DOUBLE or MEM(offsettable).   Note that this disallows MEM(REG+REG).  */intdi_operand (op, mode)     rtx op;     enum machine_mode mode;{  if (register_operand (op, mode))    return (TRUE);  switch (GET_CODE (op))    {    case CONST_DOUBLE:    case CONST_INT:      return (TRUE);    case MEM:      return (memory_address_p (DImode, XEXP (op, 0))	      && offsettable_address_p (FALSE, DImode, XEXP (op, 0)));    default:      return (FALSE);    }} /* di_operand *//* Return TRUE for valid index operands. */intindex_operand (op, mode)     rtx op;     enum machine_mode mode;{  return (register_operand(op, mode)	  || (immediate_operand (op, mode) && abs (INTVAL (op)) < 4096));} /* index_operand *//* Return TRUE for arithmetic operators which can be combined with a multiply   (shift).  */intshiftable_operator (x, mode)     rtx x;     enum machine_mode mode;{  if (GET_MODE (x) != mode)    return FALSE;  else    {      enum rtx_code code = GET_CODE (x);      return (code == PLUS || code == MINUS	      || code == IOR || code == XOR || code == AND);    }} /* shiftable_operator *//* Return TRUE for shift operators. */intshift_operator (x, mode)     rtx x;     enum machine_mode mode;{  if (GET_MODE (x) != mode)    return FALSE;  else    {      enum rtx_code code = GET_CODE (x);      return (code == ASHIFT || code == LSHIFT	      || code == ASHIFTRT || code == LSHIFTRT);    }} /* shift_operator *//* Routines to output assembly language.  *//* Output the operands of a LDM/STM instruction to STREAM.   MASK is the ARM register set mask of which only bits 0-15 are important.   INSTR is the possibly suffixed base register.  HAT unequals zero if a hat   must follow the register list.  */voidprint_multi_reg (stream, instr, mask, hat)     FILE *stream;     char *instr;     int mask, hat;{  int i;  int not_first = FALSE;  fprintf (stream, "\t%s, {", instr);  for (i = 0; i < 16; i++)    if (mask & (1 << i))      {	if (not_first)	  fprintf (stream, ", ");	fprintf (stream, "%s", reg_names[i]);	not_first = TRUE;      }  fprintf (stream, "}%s\n", hat ? "^" : "");} /* print_multi_reg *//* Output a 'call' insn. */char *output_call (operands)	rtx operands[];{  operands[0] = XEXP (operands[0], 0);  /* Handle calls to lr using ip (which may be clobbered in subr anyway). */  if (REGNO (operands[0]) == 14)    {      operands[0] = gen_rtx (REG, SImode, 12);      arm_output_asm_insn ("mov\t%0, lr", operands);    }  arm_output_asm_insn ("mov\tlr, pc", operands);  arm_output_asm_insn ("mov\tpc, %0", operands);  return ("");} /* output_call *//* Output a move from arm registers to an fpu registers.   OPERANDS[0] is an fpu register.   OPERANDS[1] is the first registers of an arm register pair.  */char *output_mov_double_fpu_from_arm (operands)     rtx operands[];{  int arm_reg0 = REGNO (operands[1]);  rtx ops[2];  if (arm_reg0 == 12)    abort();  ops[0] = gen_rtx (REG, SImode, arm_reg0);  ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);  arm_output_asm_insn ("stmfd\tsp!, {%0, %1}", ops);  arm_output_asm_insn ("ldfd\t%0, [sp], #8", operands);  return ("");} /* output_mov_double_fpu_from_arm *//* Output a move from an fpu register to arm registers.   OPERANDS[0] is the first registers of an arm register pair.   OPERANDS[1] is an fpu register.  */char *output_mov_double_arm_from_fpu (operands)     rtx operands[];{  int arm_reg0 = REGNO (operands[0]);  rtx ops[2];  if (arm_reg0 == 12)    abort();  ops[0] = gen_rtx (REG, SImode, arm_reg0);  ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);  arm_output_asm_insn ("stfd\t%1, [sp, #-8]!", operands);  arm_output_asm_insn ("ldmfd\tsp!, {%0, %1}", ops);  return("");} /* output_mov_double_arm_from_fpu *//* Output a move between double words.   It must be REG<-REG, REG<-CONST_DOUBLE, REG<-CONST_INT, REG<-MEM   or MEM<-REG and all MEMs must be offsettable addresses.  */char *output_move_double (operands)     rtx operands[];{  enum rtx_code code0 = GET_CODE (operands[0]);  enum rtx_code code1 = GET_CODE (operands[1]);  rtx otherops[2];  if (code0 == REG)    {      int reg0 = REGNO (operands[0]);      otherops[0] = gen_rtx (REG, SImode, 1 + reg0);      if (code1 == REG)	{	  int reg1 = REGNO (operands[1]);	  if (reg1 == 12)	    abort();	  otherops[1] = gen_rtx (REG, SImode, 1 + reg1);	  /* Ensure the second source is not overwritten */	  if (reg0 == 1 + reg1)	    {	      arm_output_asm_insn("mov\t%0, %1", otherops);	      arm_output_asm_insn("mov\t%0, %1", operands);	    }	  else	    {	      arm_output_asm_insn("mov\t%0, %1", operands);	      arm_output_asm_insn("mov\t%0, %1", otherops);	    }	}      else if (code1 == CONST_DOUBLE)	{	  otherops[1] = gen_rtx (CONST_INT, VOIDmode,				 CONST_DOUBLE_HIGH (operands[1]));	  operands[1] = gen_rtx (CONST_INT, VOIDmode,				 CONST_DOUBLE_LOW (operands[1]));	  arm_output_asm_insn ("mov\t%0, %1", operands);	  arm_output_asm_insn ("mov\t%0, %1", otherops);	}      else if (code1 == CONST_INT)	{	  otherops[1] = const0_rtx;	  arm_output_asm_insn ("mov\t%0, %1", operands);	  arm_output_asm_insn ("mov\t%0, %1", otherops);	}      else if (code1 == MEM)	{	  if (GET_CODE (XEXP (operands[1], 0)) == REG)	    {	      /* Handle the simple case where address is [r, #0] more		 efficient.  */	      operands[1] = XEXP (operands[1], 0);	      arm_output_asm_insn ("ldmia\t%1, %M0", operands);	    }	  else	    {	      otherops[1] = adj_offsettable_operand (operands[1], 4);	      /* Take care of overlapping base/data reg.  */	      if (reg_mentioned_p (operands[0], operands[1]))		{		  arm_output_asm_insn ("ldr\t%0, %1", otherops);		  arm_output_asm_insn ("ldr\t%0, %1", operands);		}	      else		{		  arm_output_asm_insn ("ldr\t%0, %1", operands);		  arm_output_asm_insn ("ldr\t%0, %1", otherops);		}	    }	}      else abort();  /* Constraints should prevent this */    }  else if (code0 == MEM && code1 == REG)    {      if (REGNO (operands[1]) == 12)	abort();      if (GET_CODE (XEXP (operands[0], 0)) == REG)	{	  operands[0] = XEXP (operands[0], 0);	  arm_output_asm_insn ("stmia\t%0, %M1", operands);	}      else	{	  otherops[0] = adj_offsettable_operand (operands[0], 4);	  otherops[1] = gen_rtx (REG, SImode, 1 + REGNO (operands[1]));	  arm_output_asm_insn ("str\t%1, %0", operands);	  arm_output_asm_insn ("str\t%1, %0", otherops);	}    }  else abort();  /* Constraints should prevent this */  return("");} /* output_move_double *//* Output an arbitrary MOV reg, #n.   OPERANDS[0] is a register.  OPERANDS[1] is a const_int.  */char *output_mov_immediate (operands)     rtx operands[2];{  int n = INTVAL (operands[1]);  int n_ones = 0;  int i;  /* Try to use one MOV */  if (const_ok_for_arm (n))    return (arm_output_asm_insn ("mov\t%0, %1", operands));

⌨️ 快捷键说明

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