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

📄 expmed.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 5 页
字号:
/* Medium-level subroutines: convert bit-field store and extract   and shifts, multiplies and divides to rtl instructions.   Copyright (C) 1987, 1988, 1989, 1992 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, 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 "real.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 ();static rtx mask_rtx ();static rtx lshift_value ();#define CEIL(x,y) (((x) + (y) - 1) / (y))/* Non-zero means multiply instructions are cheaper than shifts.  */int mult_is_very_cheap;/* Non-zero means divides or modulus operations are relatively cheap for   powers of two, so don't use branches; emit the operation instead.    Usually, this will mean that the MD file will emit non-branch   sequences.  */static int sdiv_pow2_cheap, smod_pow2_cheap;/* Cost of various pieces of RTL.  */static int add_cost, shift_cost, mult_cost, negate_cost, lea_cost;/* Max scale factor for scaled address in lea instruction.  */static int lea_max_mul;voidinit_expmed (){  char *free_point = (char *) oballoc (1);  /* This is "some random pseudo register" for purposes of calling recog     to see what insns exist.  */  rtx reg = gen_rtx (REG, word_mode, FIRST_PSEUDO_REGISTER);  rtx pow2 = GEN_INT (32);  rtx lea;  HOST_WIDE_INT i;  int dummy;  add_cost = rtx_cost (gen_rtx (PLUS, word_mode, reg, reg), SET);  shift_cost = rtx_cost (gen_rtx (LSHIFT, word_mode, reg,				  /* Using a constant gives better				     estimate of typical costs.				     1 or 2 might have quirks.  */				  GEN_INT (3)), SET);  mult_cost = rtx_cost (gen_rtx (MULT, word_mode, reg, reg), SET);  negate_cost = rtx_cost (gen_rtx (NEG, word_mode, reg), SET);  /* 999999 is chosen to avoid any plausible faster special case.  */  mult_is_very_cheap    = (rtx_cost (gen_rtx (MULT, word_mode, reg, GEN_INT (999999)), SET)       < rtx_cost (gen_rtx (LSHIFT, word_mode, reg, GEN_INT (7)), SET));  sdiv_pow2_cheap    = rtx_cost (gen_rtx (DIV, word_mode, reg, pow2), SET) <= 2 * add_cost;  smod_pow2_cheap    = rtx_cost (gen_rtx (MOD, word_mode, reg, pow2), SET) <= 2 * add_cost;  init_recog ();  for (i = 2;; i <<= 1)    {      lea = gen_rtx (SET, VOIDmode, reg,		     gen_rtx (PLUS, word_mode,			      gen_rtx (MULT, word_mode, reg, GEN_INT (i)),			      reg));      /* Using 0 as second argument is not quite right,	 but what else is there to do?  */      if (recog (lea, 0, &dummy) < 0)	break;      lea_max_mul = i;      lea_cost = rtx_cost (SET_SRC (lea), SET);    }  /* Free the objects we just allocated.  */  obfree (free_point);}/* 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)    {      HOST_WIDE_INT val = - INTVAL (x);      if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT)	{	  /* Sign extend the value from the bits that are significant.  */	  if (val & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))	    val |= (HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (mode);	  else	    val &= ((HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (mode)) - 1;	}      return GEN_INT (val);    }  else    return expand_unop (GET_MODE (x), neg_optab, x, NULL_RTX, 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 varying.  *//* ??? Note that there are two different ideas here for how   to determine the size to count bits within, for a register.   One is BITS_PER_WORD, and the other is the size of operand 3   of the insv pattern.  (The latter assumes that an n-bit machine   will be able to insert bit fields up to n bits wide.)   It isn't certain that either of these is right.   extract_bit_field has the same quandary.  */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;  if (GET_CODE (str_rtx) == MEM && ! MEM_IN_STRUCT_P (str_rtx))    abort ();  /* 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)    {      /* The following line once was done only if WORDS_BIG_ENDIAN,	 but I think that is a mistake.  WORDS_BIG_ENDIAN is	 meaningful at a much higher level; when structures are copied	 between memory and regs, the higher-numbered regs	 always get higher addresses.  */      offset += SUBREG_WORD (op0);      /* We used to adjust BITPOS here, but now we do the whole adjustment	 right after the loop.  */      op0 = SUBREG_REG (op0);    }#if BYTES_BIG_ENDIAN  /* If OP0 is a register, BITPOS must count within a word.     But as we have it, it counts within whatever size OP0 now has.     On a bigendian machine, these are not the same, so convert.  */  if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0)))    bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));#endif  value = protect_from_queue (value, 0);  if (flag_force_mem)    value = force_not_mem (value);  /* Note that the adjustment of BITPOS above has no effect on whether     BITPOS is 0 in a REG bigger than a word.  */  if (GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD      && (! STRICT_ALIGNMENT || GET_CODE (op0) != MEM)      && bitpos == 0 && bitsize == GET_MODE_BITSIZE (fieldmode))    {      /* Storing in a full-word or multi-word field in a register	 can be done with just SUBREG.  */      if (GET_MODE (op0) != fieldmode)	if (GET_CODE (op0) == REG)	  op0 = gen_rtx (SUBREG, fieldmode, op0, offset);	else	  op0 = change_address (op0, fieldmode,				plus_constant (XEXP (op0, 0), offset));      emit_move_insn (op0, value);      return value;    }  /* Storing an lsb-aligned field in a register     can be done with a movestrict instruction.  */  if (GET_CODE (op0) != MEM#if BYTES_BIG_ENDIAN      && bitpos + bitsize == unit#else      && bitpos == 0#endif      && bitsize == GET_MODE_BITSIZE (fieldmode)      && (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	{	  int icode = movstrict_optab->handlers[(int) fieldmode].insn_code;	  if(! (*insn_operand_predicate[icode][1]) (value, fieldmode))	    value = copy_to_mode_reg (fieldmode, value);	  emit_insn (GEN_FCN (icode)		   (gen_rtx (SUBREG, fieldmode, op0, offset), value));	}      return value;    }  /* Handle fields bigger than a word.  */  if (bitsize > BITS_PER_WORD)    {      /* Here we transfer the words of the field	 in the order least significant first.	 This is because the most significant word is the one which may	 be less than full.  */      int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;      int i;      /* This is the mode we must force value to, so that there will be enough	 subwords to extract.  Note that fieldmode will often (always?) be	 VOIDmode, because that is what store_field uses to indicate that this	 is a bit field, but passing VOIDmode to operand_subword_force will	 result in an abort.  */      fieldmode = mode_for_size (nwords * BITS_PER_WORD, MODE_INT, 0);      for (i = 0; i < nwords; i++)	{	  /* If I is 0, use the low-order word in both field and target;	     if I is 1, use the next to lowest word; and so on.  */	  int wordnum = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i);	  int bit_offset = (WORDS_BIG_ENDIAN			    ? MAX (bitsize - (i + 1) * BITS_PER_WORD, 0)			    : i * BITS_PER_WORD);	  store_bit_field (op0, MIN (BITS_PER_WORD,				     bitsize - i * BITS_PER_WORD),			   bitnum + bit_offset, word_mode,			   operand_subword_force (value, wordnum, fieldmode),			   align, total_size);	}      return value;    }  /* From here on we can assume that the field to be stored in is     a full-word (whatever type that is), 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)    {      if (offset != 0	  || GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD)	op0 = gen_rtx (SUBREG, TYPE_MODE (type_for_size (BITS_PER_WORD, 0)),		       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)      /* Ensure insv's size is wide enough for this field.  */      && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_insv][3])	  >= bitsize))    {      int xbitpos = bitpos;      rtx value1;      rtx xop0 = op0;      rtx last = get_last_insn ();      rtx pat;      enum machine_mode maxmode	= insn_operand_mode[(int) CODE_FOR_insv][3];      int save_volatile_ok = volatile_ok;      volatile_ok = 1;      /* If this machine's insv can only insert into a register, or if we	 are to force MEMs into a register, copy OP0 into a register and	 save it back later.  */      if (GET_CODE (op0) == MEM	  && (flag_force_mem	      || ! ((*insn_operand_predicate[(int) CODE_FOR_insv][0])		    (op0, VOIDmode))))	{	  rtx tempreg;	  enum machine_mode bestmode;	  /* Get the mode to use for inserting into this field.  If OP0 is	     BLKmode, get the smallest mode consistent with the alignment. If	     OP0 is a non-BLKmode object that is no wider than MAXMODE, use its	     mode. Otherwise, use the smallest mode containing the field.  */	  if (GET_MODE (op0) == BLKmode	      || GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (maxmode))	    bestmode	      = get_best_mode (bitsize, bitnum, align * BITS_PER_UNIT, maxmode,			       MEM_VOLATILE_P (op0));	  else	    bestmode = GET_MODE (op0);	  if (bestmode == VOIDmode)	    goto insv_loses;	  /* 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);	  store_bit_field (tempreg, 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, byte_mode,			       plus_constant (XEXP (xop0, 0), offset));      /* If xop0 is a register, we need it in MAXMODE	 to make it acceptable to the format of insv.  */      if (GET_CODE (xop0) == SUBREG)	PUT_MODE (xop0, maxmode);      if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode)	xop0 = gen_rtx (SUBREG, maxmode, xop0, 0);      /* On big-endian machines, we count bits from the most significant.	 If the bit field insn does not, we must invert.  */#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN      xbitpos = unit - bitsize - xbitpos;#endif      /* We have been counting XBITPOS within UNIT.	 Count instead within the size of the register.  */#if BITS_BIG_ENDIAN      if (GET_CODE (xop0) != MEM)	xbitpos += GET_MODE_BITSIZE (maxmode) - unit;#endif      unit = GET_MODE_BITSIZE (maxmode);      /* Convert VALUE to maxmode (which insv insn wants) in VALUE1.  */      value1 = value;      if (GET_MODE (value) != maxmode)	{	  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.  However,		 if we must narrow it, be sure we do it correctly.  */	      if (GET_MODE_SIZE (GET_MODE (value)) < GET_MODE_SIZE (maxmode))		{		  /* Avoid making subreg of a subreg, or of a mem.  */		  if (GET_CODE (value1) != REG)		value1 = copy_to_reg (value1);		  value1 = gen_rtx (SUBREG, maxmode, value1, 0);		}	      else		value1 = gen_lowpart (maxmode, value1);	    }

⌨️ 快捷键说明

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