📄 expmed.c
字号:
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 (xop0) == 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 (xop0)); else bestmode = GET_MODE (xop0); if (bestmode == VOIDmode || (SLOW_UNALIGNED_ACCESS && GET_MODE_SIZE (bestmode) > align)) goto extv_loses; /* Compute offset as multiple of this unit, counting in bytes. */ unit = GET_MODE_BITSIZE (bestmode); xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode); xbitpos = bitnum % unit; xop0 = change_address (xop0, bestmode, plus_constant (XEXP (xop0, 0), xoffset)); /* Fetch it to a register in that size. */ xop0 = force_reg (bestmode, xop0); /* XBITPOS counts within UNIT, which is what is expected. */ } else /* Get ref to first byte containing part of the field. */ xop0 = change_address (xop0, byte_mode, plus_constant (XEXP (xop0, 0), xoffset)); } /* If op0 is a register, we need it in MAXMODE (which is usually SImode) to make it acceptable to the format of extv. */ if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode) abort (); 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; /* XBITPOS counts within a size of UNIT. Adjust to count within a size of MAXMODE. */ if (BITS_BIG_ENDIAN && GET_CODE (xop0) != MEM) xbitpos += (GET_MODE_BITSIZE (maxmode) - unit); unit = GET_MODE_BITSIZE (maxmode); if (xtarget == 0 || (flag_force_mem && GET_CODE (xtarget) == MEM)) xtarget = xspec_target = gen_reg_rtx (tmode); if (GET_MODE (xtarget) != maxmode) { if (GET_CODE (xtarget) == REG) { int wider = (GET_MODE_SIZE (maxmode) > GET_MODE_SIZE (GET_MODE (xtarget))); xtarget = gen_lowpart (maxmode, xtarget); if (wider) xspec_target_subreg = xtarget; } else xtarget = gen_reg_rtx (maxmode); } /* If this machine's extv insists on a register target, make sure we have one. */ if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][0]) (xtarget, maxmode))) xtarget = gen_reg_rtx (maxmode); bitsize_rtx = GEN_INT (bitsize); bitpos_rtx = GEN_INT (xbitpos); pat = gen_extv (protect_from_queue (xtarget, 1), xop0, bitsize_rtx, bitpos_rtx); if (pat) { emit_insn (pat); target = xtarget; spec_target = xspec_target; spec_target_subreg = xspec_target_subreg; } else { delete_insns_since (last); target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, target, 0, align); } } else extv_loses:#endif target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, target, 0, align); } if (target == spec_target) return target; if (target == spec_target_subreg) return spec_target; if (GET_MODE (target) != tmode && GET_MODE (target) != mode) { /* If the target mode is floating-point, first convert to the integer mode of that size and then access it as a floating-point value via a SUBREG. */ if (GET_MODE_CLASS (tmode) == MODE_FLOAT) { target = convert_to_mode (mode_for_size (GET_MODE_BITSIZE (tmode), MODE_INT, 0), target, unsignedp); if (GET_CODE (target) != REG) target = copy_to_reg (target); return gen_rtx (SUBREG, tmode, target, 0); } else return convert_to_mode (tmode, target, unsignedp); } return target;}/* Extract a bit field using shifts and boolean operations Returns an rtx to represent the value. OP0 addresses a register (word) or memory (byte). BITPOS says which bit within the word or byte the bit field starts in. OFFSET says how many bytes farther the bit field starts; it is 0 if OP0 is a register. BITSIZE says how many bits long the bit field is. (If OP0 is a register, it may be narrower than a full word, but BITPOS still counts within a full word, which is significant on bigendian machines.) UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value). If TARGET is nonzero, attempts to store the value there and return TARGET, but this is not guaranteed. If TARGET is not used, create a pseudo-reg of mode TMODE for the value. ALIGN is the alignment that STR_RTX is known to have, measured in bytes. */static rtxextract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, target, unsignedp, align) enum machine_mode tmode; register rtx op0, target; register int offset, bitsize, bitpos; int unsignedp; int align;{ int total_bits = BITS_PER_WORD; enum machine_mode mode; if (GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG) { /* Special treatment for a bit field split across two registers. */ if (bitsize + bitpos > BITS_PER_WORD) return extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align); } else { /* Get the proper mode to use for this field. We want a mode that includes the entire field. If such a mode would be larger than a word, we won't be doing the extraction the normal way. */ mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT, align * BITS_PER_UNIT, word_mode, GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0)); if (mode == VOIDmode) /* The only way this should occur is if the field spans word boundaries. */ return extract_split_bit_field (op0, bitsize, bitpos + offset * BITS_PER_UNIT, unsignedp, align); total_bits = GET_MODE_BITSIZE (mode); /* Make sure bitpos is valid for the chosen mode. Adjust BITPOS to be be in the range 0 to total_bits-1, and put any excess bytes in OFFSET. */ if (bitpos >= total_bits) { offset += (bitpos / total_bits) * (total_bits / BITS_PER_UNIT); bitpos -= ((bitpos / total_bits) * (total_bits / BITS_PER_UNIT) * BITS_PER_UNIT); } /* Get ref to an aligned byte, halfword, or 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 % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT; offset -= (offset % (total_bits / BITS_PER_UNIT)); op0 = change_address (op0, mode, plus_constant (XEXP (op0, 0), offset)); } mode = GET_MODE (op0); if (BYTES_BIG_ENDIAN) { /* BITPOS is the distance between our msb and that of OP0. Convert it to the distance from the lsb. */ bitpos = total_bits - bitsize - bitpos; } /* Now BITPOS is always the distance between the field's lsb and that of OP0. We have reduced the big-endian case to the little-endian case. */ if (unsignedp) { if (bitpos) { /* If the field does not already start at the lsb, shift it so it does. */ tree amount = build_int_2 (bitpos, 0); /* Maybe propagate the target for the shift. */ /* But not if we will return it--could confuse integrate.c. */ rtx subtarget = (target != 0 && GET_CODE (target) == REG && !REG_FUNCTION_VALUE_P (target) ? target : 0); if (tmode != mode) subtarget = 0; op0 = expand_shift (RSHIFT_EXPR, mode, op0, amount, subtarget, 1); } /* Convert the value to the desired mode. */ if (mode != tmode) op0 = convert_to_mode (tmode, op0, 1); /* Unless the msb of the field used to be the msb when we shifted, mask out the upper bits. */ if (GET_MODE_BITSIZE (mode) != bitpos + bitsize#if 0#ifdef SLOW_ZERO_EXTEND /* Always generate an `and' if we just zero-extended op0 and SLOW_ZERO_EXTEND, since it will combine fruitfully with the zero-extend. */ || tmode != mode#endif#endif ) return expand_binop (GET_MODE (op0), and_optab, op0, mask_rtx (GET_MODE (op0), 0, bitsize, 0), target, 1, OPTAB_LIB_WIDEN); return op0; } /* To extract a signed bit-field, first shift its msb to the msb of the word, then arithmetic-shift its lsb to the lsb of the word. */ op0 = force_reg (mode, op0); if (mode != tmode) target = 0; /* Find the narrowest integer mode that contains the field. */ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) if (GET_MODE_BITSIZE (mode) >= bitsize + bitpos) { op0 = convert_to_mode (mode, op0, 0); break; } if (GET_MODE_BITSIZE (mode) != (bitsize + bitpos)) { tree amount = build_int_2 (GET_MODE_BITSIZE (mode) - (bitsize + bitpos), 0); /* Maybe propagate the target for the shift. */ /* But not if we will return the result--could confuse integrate.c. */ rtx subtarget = (target != 0 && GET_CODE (target) == REG && ! REG_FUNCTION_VALUE_P (target) ? target : 0); op0 = expand_shift (LSHIFT_EXPR, mode, op0, amount, subtarget, 1); } return expand_shift (RSHIFT_EXPR, mode, op0, build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0), target, 0);}/* Return a constant integer (CONST_INT or CONST_DOUBLE) mask value of mode MODE with BITSIZE ones followed by BITPOS zeros, or the complement of that if COMPLEMENT. The mask is truncated if necessary to the width of mode MODE. The mask is zero-extended if BITSIZE+BITPOS is too small for MODE. */static rtxmask_rtx (mode, bitpos, bitsize, complement) enum machine_mode mode; int bitpos, bitsize, complement;{ HOST_WIDE_INT masklow, maskhigh; if (bitpos < HOST_BITS_PER_WIDE_INT) masklow = (HOST_WIDE_INT) -1 << bitpos; else masklow = 0; if (bitpos + bitsize < HOST_BITS_PER_WIDE_INT) masklow &= ((unsigned HOST_WIDE_INT) -1 >> (HOST_BITS_PER_WIDE_INT - bitpos - bitsize)); if (bitpos <= HOST_BITS_PER_WIDE_INT) maskhigh = -1; else maskhigh = (HOST_WIDE_INT) -1 << (bitpos - HOST_BITS_PER_WIDE_INT); if (bitpos + bitsize > HOST_BITS_PER_WIDE_INT) maskhigh &= ((unsigned HOST_WIDE_INT) -1 >> (2 * HOST_BITS_PER_WIDE_INT - bitpos - bitsize)); else maskhigh = 0; if (complement) { maskhigh = ~maskhigh; masklow = ~masklow; } return immed_double_const (masklow, maskhigh, mode);}/* Return a constant integer (CONST_INT or CONST_DOUBLE) rtx with the value VALUE truncated to BITSIZE bits and then shifted left BITPOS bits. */static rtxlshift_value (mode, value, bitpos, bitsize) enum machine_mode mode; rtx value; int bitpos, bitsize;{ unsigned HOST_WIDE_INT v = INTVAL (value); HOST_WIDE_INT low, high; if (bitsize < HOST_BITS_PER_WIDE_INT) v &= ~((HOST_WIDE_INT) -1 << bitsize); if (bitpos < HOST_BITS_PER_WIDE_INT) { low = v << bitpos; high = (bitpos > 0 ? (v >> (HOST_BITS_PER_WIDE_INT - bitpos)) : 0); } else { low = 0; high = v << (bitpos - HOST_BITS_PER_WIDE_INT); } return immed_double_const (low, high, mode);}/* Extract a bit field that is split across two words and return an RTX for the result. OP0 is the REG, SUBREG or MEM rtx for the first of the two words. BITSIZE is the field width; BITPOS, position of its first bit, in the word. UNSIGNEDP is 1 if should zero-extend the contents; else sign-extend. ALIGN is the known alignment of OP0, measured in bytes. This is also the size of the memory objects to be used. */static rtxextract_split_bit_field (op0, bitsize, bitpos, unsignedp, align) rtx op0; int bitsize, bitpos, unsignedp, align;{ int unit; int bitsdone = 0; rtx result; int first = 1; /* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that much at a time. */ if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) unit = BITS_PER_WORD; else unit = MIN (align * BITS_PER_UNIT, BITS_PER_WORD); while (bitsdone < bitsize) { int thissize; rtx part, word; int thispos; int offset; offset = (bitpos + bitsdone) / unit; thispos = (bitpos + bitsdone) % unit; /* THISSIZE must not overrun a word boundary. Otherwise, extract_fixed_bit_field will call us again, and we will mutually recurse forever. */ thissize = MIN (bitsize - bitsdone, BITS_PER_WORD); thissize = MIN (thissize, unit - thispos); /* If OP0 is a register, then handle OFFSET here. When handling multiword bitfields, extract_bit_field may pass down a word_mode SUBREG of a larger REG for a bitfield that actually
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -