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

📄 expmed.c

📁 这是完整的gcc源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* Medium-level subroutines: convert bit-field store and extract   and shifts, multiplies and divides to rtl instructions.   Copyright (C) 1987, 1988, 1989 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 1, 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 "config.h"#include "rtl.h"#include "tree.h"#include "flags.h"#include "insn-flags.h"#include "insn-codes.h"#include "insn-config.h"#include "expr.h"#include "recog.h"static rtx extract_split_bit_field ();static rtx extract_fixed_bit_field ();static void store_split_bit_field ();static void store_fixed_bit_field ();/* Return an rtx representing minus the value of X.   MODE is the intended mode of the result,   useful if X is a CONST_INT.  */rtxnegate_rtx (mode, x)     enum machine_mode mode;     rtx x;{  if (GET_CODE (x) == CONST_INT)    {      int val = - INTVAL (x);      if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_INT)	{	  /* Sign extend the value from the bits that are significant.  */	  if (val & (1 << (GET_MODE_BITSIZE (mode) - 1)))	    val |= (-1) << GET_MODE_BITSIZE (mode);	  else	    val &= (1 << GET_MODE_BITSIZE (mode)) - 1;	}      return gen_rtx (CONST_INT, VOIDmode, val);    }  else    return expand_unop (GET_MODE (x), neg_optab, x, 0, 0);}/* Generate code to store value from rtx VALUE   into a bit-field within structure STR_RTX   containing BITSIZE bits starting at bit BITNUM.   FIELDMODE is the machine-mode of the FIELD_DECL node for this field.   ALIGN is the alignment that STR_RTX is known to have, measured in bytes.   TOTAL_SIZE is the size of the structure in bytes, or -1 if unknown.  */rtxstore_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)     rtx str_rtx;     register int bitsize;     int bitnum;     enum machine_mode fieldmode;     rtx value;     int align;     int total_size;{  int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;  register int offset = bitnum / unit;  register int bitpos = bitnum % unit;  register rtx op0 = str_rtx;  rtx value1;  /* At this point, BITPOS counts within UNIT for a memref.     For a register or a subreg, it actually counts within the width     of the mode of OP0.  However, BITNUM never exceeds that width,     so the % operation above never really does anything.     We will adjust BITPOS later to count properly within UNIT     in the case of a register.  */  /* Discount the part of the structure before the desired byte.     We need to know how many bytes are safe to reference after it.  */  if (total_size >= 0)    total_size -= (bitpos / BIGGEST_ALIGNMENT		   * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));  while (GET_CODE (op0) == SUBREG)    {#ifdef BYTES_BIG_ENDIAN      /* Keep BITPOS counting within the size of op0.  */      bitpos += (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))		 - GET_MODE_BITSIZE (GET_MODE (op0)));#endif      offset += SUBREG_WORD (op0);      op0 = SUBREG_REG (op0);    }  value = protect_from_queue (value, 0);  if (flag_force_mem)    value = force_not_mem (value);  if (GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD      && GET_MODE_BITSIZE (fieldmode) == bitsize      && bitpos % BITS_PER_WORD == 0      && GET_CODE (op0) == REG)    {      /* Storing in a full-word or multi-word field in a register	 can be done with just SUBREG.  */      if (GET_MODE (op0) != fieldmode)	op0 = gen_rtx (SUBREG, fieldmode, op0, offset);      emit_move_insn (op0, value);      return value;    }#ifdef BYTES_BIG_ENDIAN  /* If OP0 is a register, BITPOS must count within UNIT, which should be SI.     But as we have it, it counts within whatever size OP0 now has.     These are not the same, so convert if big-endian.  */  if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0)))    {      bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));      /* Change the mode now so we don't adjust BITPOS again.  */      if (GET_CODE (op0) == SUBREG)	PUT_MODE (op0, SImode);      else	op0 = gen_rtx (SUBREG, SImode, op0, 0);    }#endif  /* Storing an lsb-aligned field in a register     can be done with a movestrict instruction.  */  if (GET_CODE (op0) != MEM#ifdef BYTES_BIG_ENDIAN      && bitpos + bitsize == unit#else      && bitpos == 0#endif      && (GET_MODE (op0) == fieldmode	  || (movstrict_optab->handlers[(int) fieldmode].insn_code	      != CODE_FOR_nothing)))    {      /* Get appropriate low part of the value being stored.  */      if (GET_CODE (value) == CONST_INT || GET_CODE (value) == REG)	value = gen_lowpart (fieldmode, value);      else if (!(GET_CODE (value) == SYMBOL_REF		 || GET_CODE (value) == LABEL_REF		 || GET_CODE (value) == CONST))	value = convert_to_mode (fieldmode, value, 0);      if (GET_MODE (op0) == fieldmode)	emit_move_insn (op0, value);      else	{	  if (GET_CODE (op0) == SUBREG)	    PUT_MODE (op0, fieldmode);	  else	    op0 = gen_rtx (SUBREG, fieldmode, op0, offset);	  emit_insn (GEN_FCN (movstrict_optab->handlers[(int) fieldmode].insn_code)		     (op0, value));	}      return value;    }  /* Handle fields bigger than a word.  */  if (bitsize > BITS_PER_WORD)    {      int low_size = BITS_PER_WORD;      int low_pos = bitpos + offset * unit;      int high_size = bitsize - low_size;      int high_pos;#ifdef BYTES_BIG_ENDIAN      high_pos = low_pos;      low_pos += high_size;#else      high_pos = low_pos + low_size;#endif      if (GET_MODE (value) != VOIDmode)	value = force_reg (GET_MODE (value), value);       store_bit_field (op0, low_size, low_pos, SImode,		       gen_lowpart (SImode, value), align, total_size);      store_bit_field (op0, high_size, high_pos, SImode,		       gen_highpart (SImode, value), align, total_size);      return value;    }  /* From here on we can assume that the field to be stored in is an integer,     since it is shorter than a word.  */  /* OFFSET is the number of words or bytes (UNIT says which)     from STR_RTX to the first word or byte containing part of the field.  */  if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)    {      /* If not in memory, merge in the offset now.  */      if (offset != 0	  || GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (SImode))	{	  if (GET_CODE (op0) == SUBREG)	    SUBREG_WORD (op0) += offset;	  else	    op0 = gen_rtx (SUBREG, SImode, op0, offset);	}      offset = 0;    }  else    {      op0 = protect_from_queue (op0, 1);    }  /* Now OFFSET is nonzero only if OP0 is memory     and is therefore always measured in bytes.  */#ifdef HAVE_insv  if (HAVE_insv      && !(bitsize == 1 && GET_CODE (value) == CONST_INT))    {      int xbitpos = bitpos;      rtx xop0 = op0;      rtx last = get_last_insn ();      rtx pat;      extern int volatile_ok;      int save_volatile_ok = volatile_ok;      volatile_ok = 1;      /* If this machine's insv can only insert into a register,	 copy OP0 into a register and save it back later.  */      if (GET_CODE (op0) == MEM	  && ! (*insn_operand_predicate[(int) CODE_FOR_insv][0]) (op0, VOIDmode))	{	  rtx tempreg;	  enum machine_mode trymode, bestmode = VOIDmode, insn_mode;	  /* Don't use a mode bigger than the one of the value to be stored.	     That mode must be okay, since a bit field can be that big.  */	  int maxsize	    = GET_MODE_SIZE (insn_operand_mode[(int) CODE_FOR_insv][3]);	  /* This used to use the mode desired for operand 0,	     but that is normally QImode on most machines,	     and QImode won't work for fields that cross byte	     boundaries.  */	  /* Also don't use a mode bigger than the structure.  */	  if (total_size >= 0 && maxsize > total_size)	    maxsize = total_size;	  /* Find biggest machine mode we can safely use	     to fetch from this structure.	     But don't use a bigger mode than the insn wants.  */	  for (trymode = QImode;	       trymode && GET_MODE_SIZE (trymode) <= maxsize;	       trymode = GET_MODE_WIDER_MODE (trymode))	    if (GET_MODE_SIZE (trymode) <= align		|| align == BIGGEST_ALIGNMENT / BITS_PER_UNIT)	      bestmode = trymode;	  if (! bestmode)	    abort ();	  /* Adjust address to point to the containing unit of that mode.  */	  unit = GET_MODE_BITSIZE (bestmode);	  /* Compute offset as multiple of this unit, counting in bytes.  */	  offset = (bitnum / unit) * GET_MODE_SIZE (bestmode);	  bitpos = bitnum % unit;	  op0 = change_address (op0, bestmode, 				plus_constant (XEXP (op0, 0), offset));	  /* Fetch that unit, store the bitfield in it, then store the unit.  */	  tempreg = copy_to_reg (op0);	  /* To actually store in TEMPREG,	     look at it in the mode this insn calls for.	     (Probably SImode.)  */	  insn_mode = SImode;#ifdef BYTES_BIG_ENDIAN	  if (GET_MODE_BITSIZE (insn_mode) > unit)	    bitpos += GET_MODE_BITSIZE (insn_mode) - unit;#endif	  store_bit_field (gen_rtx (SUBREG, insn_mode, tempreg, 0),			   bitsize, bitpos, fieldmode, value,			   align, total_size);	  emit_move_insn (op0, tempreg);	  return value;	}      volatile_ok = save_volatile_ok;      /* Add OFFSET into OP0's address.  */      if (GET_CODE (xop0) == MEM)	xop0 = change_address (xop0, QImode,			       plus_constant (XEXP (xop0, 0), offset));      /* If xop0 is a register, we need it in SImode	 to make it acceptable to the format of insv.  */      if (GET_CODE (xop0) == SUBREG)	PUT_MODE (xop0, SImode);      if (GET_CODE (xop0) == REG && GET_MODE (xop0) != SImode)	{#ifdef BYTES_BIG_ENDIAN	  xbitpos += (GET_MODE_BITSIZE (SImode)		      - GET_MODE_BITSIZE (GET_MODE (xop0)));#endif	  xop0 = gen_rtx (SUBREG, SImode, xop0, 0);	}      /* Convert VALUE to SImode (which insv insn wants) in VALUE1.  */      value1 = value;      if (GET_MODE (value) != SImode)	{	  if (GET_MODE_BITSIZE (GET_MODE (value)) >= bitsize)	    {	      /* Optimization: Don't bother really extending VALUE		 if it has all the bits we will actually use.  */	      /* Avoid making subreg of a subreg, or of a mem.  */	      if (GET_CODE (value1) != REG)		value1 = copy_to_reg (value1);	      value1 = gen_rtx (SUBREG, SImode, value1, 0);	    }	  else if (!CONSTANT_P (value))	    /* Parse phase is supposed to make VALUE's data type	       match that of the component reference, which is a type	       at least as wide as the field; so VALUE should have	       a mode that corresponds to that type.  */	    abort ();	}      /* If this machine's insv insists on a register,	 get VALUE1 into a register.  */      if (! (*insn_operand_predicate[(int) CODE_FOR_insv][3]) (value1, SImode))	value1 = force_reg (SImode, value1);      /* On big-endian machines, we count bits from the most significant.	 If the bit field insn does not, we must invert.  */#if defined (BITS_BIG_ENDIAN) != defined (BYTES_BIG_ENDIAN)      xbitpos = unit - 1 - xbitpos;#endif      pat = gen_insv (xop0,		      gen_rtx (CONST_INT, VOIDmode, bitsize),		      gen_rtx (CONST_INT, VOIDmode, xbitpos),		      value1);      if (pat)	emit_insn (pat);      else        {	  delete_insns_since (last);	  store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align);	}    }  else#endif    /* Insv is not available; store using shifts and boolean ops.  */    store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align);  return value;}/* Use shifts and boolean operations to store VALUE   into a bit field of width BITSIZE   in a memory location specified by OP0 except offset by OFFSET bytes.     (OFFSET must be 0 if OP0 is a register.)   The field starts at position BITPOS within the byte.    (If OP0 is a register, it may be SImode or a narrower mode,     but BITPOS still counts within a full word,     which is significant on bigendian machines.)   STRUCT_ALIGN is the alignment the structure is known to have (in bytes).   Note that protect_from_queue has already been done on OP0 and VALUE.  */static voidstore_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)     register rtx op0;     register int offset, bitsize, bitpos;     register rtx value;     int struct_align;{  register enum machine_mode mode;  int total_bits = BITS_PER_WORD;  rtx subtarget;  int all_zero = 0;  int all_one = 0;  /* Add OFFSET to OP0's address (if it is in memory)     and if a single byte contains the whole bit field     change OP0 to a byte.  */  /* There is a case not handled here:     a structure with a known alignment of just a halfword     and a field split across two aligned halfwords within the structure.     Or likewise a structure with a known alignment of just a byte     and a field split across two bytes.     Such cases are not supposed to be able to occur.  */  if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)    {      if (offset != 0)	abort ();      /* Special treatment for a bit field split across two registers.  */      if (bitsize + bitpos > BITS_PER_WORD)	{	  store_split_bit_field (op0, bitsize, bitpos, value, BITS_PER_WORD);	  return;	}    }  else if (bitsize + bitpos <= BITS_PER_UNIT	   && (! SLOW_BYTE_ACCESS	       || (struct_align == 1		   && BIGGEST_ALIGNMENT > 1)))    {      /* It fits in one byte, and either bytes are fast	 or the alignment won't let us use anything bigger.  */      total_bits = BITS_PER_UNIT;      op0 = change_address (op0, QImode, 			    plus_constant (XEXP (op0, 0), offset));    }  else if ((bitsize + bitpos + (offset % GET_MODE_SIZE (HImode)) * BITS_PER_UNIT	    <= GET_MODE_BITSIZE (HImode))	   /* If halfwords are fast, use them whenever valid.  */	   && (! SLOW_BYTE_ACCESS	       /* Use halfwords if larger is invalid due to alignment.  */	       || (struct_align == GET_MODE_SIZE (HImode)		   && BIGGEST_ALIGNMENT > GET_MODE_SIZE (HImode))))    {      /* It fits in an aligned halfword within the structure,	 and either halfwords are fast	 or the alignment won't let us use anything bigger.  */      total_bits = GET_MODE_BITSIZE (HImode);      /* Get ref to halfword containing the field.  */      bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT;      offset -= (offset % (total_bits / BITS_PER_UNIT));      op0 = change_address (op0, HImode, 			    plus_constant (XEXP (op0, 0), offset));    }  else    {      /* Get ref to an aligned word containing the field.  */      /* Adjust BITPOS to be position within a word,	 and OFFSET to be the offset of that word.	 Then alter OP0 to refer to that word.  */      bitpos += (offset % (BITS_PER_WORD / BITS_PER_UNIT)) * BITS_PER_UNIT;      offset -= (offset % (BITS_PER_WORD / BITS_PER_UNIT));      op0 = change_address (op0, SImode,			    plus_constant (XEXP (op0, 0), offset));      /* Special treatment for a bit field split across two aligned words.  */      if (bitsize + bitpos > BITS_PER_WORD)	{	  store_split_bit_field (op0, bitsize, bitpos, value, struct_align);	  return;	}    }  mode = GET_MODE (op0);  /* Now MODE is either QImode, HImode or SImode for a MEM as OP0,     or is SImode for a REG as OP0.  TOTAL_BITS corresponds.     The bit field is contained entirely within OP0.     BITPOS is the starting bit number within OP0.     (OP0's mode may actually be narrower than MODE.)  */#ifdef BYTES_BIG_ENDIAN

⌨️ 快捷键说明

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