📄 expmed.c
字号:
/* 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 + -