📄 expmed.c
字号:
/* Medium-level subroutines: convert bit-field store and extract and shifts, multiplies and divides to rtl instructions. Copyright (C) 1987, 88, 89, 92, 93, 94, 1995 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. */#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 void store_fixed_bit_field PROTO((rtx, int, int, int, rtx, int));static void store_split_bit_field PROTO((rtx, int, int, rtx, int));static rtx extract_fixed_bit_field PROTO((enum machine_mode, rtx, int, int, int, rtx, int, int));static rtx mask_rtx PROTO((enum machine_mode, int, int, int));static rtx lshift_value PROTO((enum machine_mode, rtx, int, int));static rtx extract_split_bit_field PROTO((rtx, int, int, int, int));#define CEIL(x,y) (((x) + (y) - 1) / (y))/* 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;#ifndef SLOW_UNALIGNED_ACCESS#define SLOW_UNALIGNED_ACCESS STRICT_ALIGNMENT#endif/* For compilers that support multiple targets with different word sizes, MAX_BITS_PER_WORD contains the biggest value of BITS_PER_WORD. An example is the H8/300(H) compiler. */#ifndef MAX_BITS_PER_WORD#define MAX_BITS_PER_WORD BITS_PER_WORD#endif/* Cost of various pieces of RTL. Note that some of these are indexed by shift count, and some by mode. */static int add_cost, negate_cost, zero_cost;static int shift_cost[MAX_BITS_PER_WORD];static int shiftadd_cost[MAX_BITS_PER_WORD];static int shiftsub_cost[MAX_BITS_PER_WORD];static int mul_cost[NUM_MACHINE_MODES];static int div_cost[NUM_MACHINE_MODES];static int mul_widen_cost[NUM_MACHINE_MODES];static int mul_highpart_cost[NUM_MACHINE_MODES];voidinit_expmed (){ char *free_point; /* This is "some random pseudo register" for purposes of calling recog to see what insns exist. */ rtx reg = gen_rtx (REG, word_mode, 10000); rtx shift_insn, shiftadd_insn, shiftsub_insn; int dummy; int m; enum machine_mode mode, wider_mode; start_sequence (); /* Since we are on the permanent obstack, we must be sure we save this spot AFTER we call start_sequence, since it will reuse the rtl it makes. */ free_point = (char *) oballoc (0); zero_cost = rtx_cost (const0_rtx, 0); add_cost = rtx_cost (gen_rtx (PLUS, word_mode, reg, reg), SET); shift_insn = emit_insn (gen_rtx (SET, VOIDmode, reg, gen_rtx (ASHIFT, word_mode, reg, const0_rtx))); shiftadd_insn = emit_insn (gen_rtx (SET, VOIDmode, reg, gen_rtx (PLUS, word_mode, gen_rtx (MULT, word_mode, reg, const0_rtx), reg))); shiftsub_insn = emit_insn (gen_rtx (SET, VOIDmode, reg, gen_rtx (MINUS, word_mode, gen_rtx (MULT, word_mode, reg, const0_rtx), reg))); init_recog (); shift_cost[0] = 0; shiftadd_cost[0] = shiftsub_cost[0] = add_cost; for (m = 1; m < BITS_PER_WORD; m++) { shift_cost[m] = shiftadd_cost[m] = shiftsub_cost[m] = 32000; XEXP (SET_SRC (PATTERN (shift_insn)), 1) = GEN_INT (m); if (recog (PATTERN (shift_insn), shift_insn, &dummy) >= 0) shift_cost[m] = rtx_cost (SET_SRC (PATTERN (shift_insn)), SET); XEXP (XEXP (SET_SRC (PATTERN (shiftadd_insn)), 0), 1) = GEN_INT ((HOST_WIDE_INT) 1 << m); if (recog (PATTERN (shiftadd_insn), shiftadd_insn, &dummy) >= 0) shiftadd_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftadd_insn)), SET); XEXP (XEXP (SET_SRC (PATTERN (shiftsub_insn)), 0), 1) = GEN_INT ((HOST_WIDE_INT) 1 << m); if (recog (PATTERN (shiftsub_insn), shiftsub_insn, &dummy) >= 0) shiftsub_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftsub_insn)), SET); } negate_cost = rtx_cost (gen_rtx (NEG, word_mode, reg), SET); sdiv_pow2_cheap = (rtx_cost (gen_rtx (DIV, word_mode, reg, GEN_INT (32)), SET) <= 2 * add_cost); smod_pow2_cheap = (rtx_cost (gen_rtx (MOD, word_mode, reg, GEN_INT (32)), SET) <= 2 * add_cost); for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) { reg = gen_rtx (REG, mode, 10000); div_cost[(int) mode] = rtx_cost (gen_rtx (UDIV, mode, reg, reg), SET); mul_cost[(int) mode] = rtx_cost (gen_rtx (MULT, mode, reg, reg), SET); wider_mode = GET_MODE_WIDER_MODE (mode); if (wider_mode != VOIDmode) { mul_widen_cost[(int) wider_mode] = rtx_cost (gen_rtx (MULT, wider_mode, gen_rtx (ZERO_EXTEND, wider_mode, reg), gen_rtx (ZERO_EXTEND, wider_mode, reg)), SET); mul_highpart_cost[(int) mode] = rtx_cost (gen_rtx (TRUNCATE, mode, gen_rtx (LSHIFTRT, wider_mode, gen_rtx (MULT, wider_mode, gen_rtx (ZERO_EXTEND, wider_mode, reg), gen_rtx (ZERO_EXTEND, wider_mode, reg)), GEN_INT (GET_MODE_BITSIZE (mode)))), SET); } } /* Free the objects we just allocated. */ end_sequence (); 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 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 (BYTES_BIG_ENDIAN && GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0))) bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0)); 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 && (GET_CODE (op0) != MEM || ! SLOW_UNALIGNED_ACCESS || (offset * BITS_PER_UNIT % bitsize == 0 && align % GET_MODE_SIZE (fieldmode) == 0)) && 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 && (BYTES_BIG_ENDIAN ? bitpos + bitsize == unit : bitpos == 0) && 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. However, only do that if the value is not BLKmode. */ int backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode; 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 = (backwards ? nwords - i - 1 : i); int bit_offset = (backwards ? 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, (GET_MODE (value) == VOIDmode ? fieldmode : GET_MODE (value))), 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); } /* If VALUE is a floating-point mode, access it as an integer of the corresponding size. This can occur on a machine with 64 bit registers that uses SFmode for float. This can also occur for unaligned float structure fields. */ if (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT) { if (GET_CODE (value) != REG) value = copy_to_reg (value); value = gen_rtx (SUBREG, word_mode, value, 0); } /* 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) && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) && (bitsize + bitpos
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -