📄 expmed.c
字号:
/* BITPOS is the distance between our msb and that of the containing datum. Convert it to the distance from the lsb. */ bitpos = total_bits - bitsize - bitpos;#endif /* Now BITPOS is always the distance between our lsb and that of OP0. */ /* Shift VALUE left by BITPOS bits. If VALUE is not constant, we must first convert its mode to MODE. */ if (GET_CODE (value) == CONST_INT) { register int v = INTVAL (value); if (bitsize < HOST_BITS_PER_INT) v &= (1 << bitsize) - 1; if (v == 0) all_zero = 1; else if (bitsize < HOST_BITS_PER_INT && v == (1 << bitsize) - 1) all_one = 1; value = gen_rtx (CONST_INT, VOIDmode, v << bitpos); } else { int must_and = (GET_MODE_BITSIZE (GET_MODE (value)) != bitsize); if (GET_MODE (value) != mode) { if ((GET_CODE (value) == REG || GET_CODE (value) == SUBREG) && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (value))) value = gen_lowpart (mode, value); else value = convert_to_mode (mode, value, 1); } if (must_and && bitsize < HOST_BITS_PER_INT) value = expand_bit_and (mode, value, gen_rtx (CONST_INT, VOIDmode, (1 << bitsize) - 1), 0); if (bitpos > 0) value = expand_shift (LSHIFT_EXPR, mode, value, build_int_2 (bitpos, 0), 0, 1); } /* Now clear the chosen bits in OP0, except that if VALUE is -1 we need not bother. */ subtarget = op0; if (! all_one) subtarget = expand_bit_and (mode, op0, gen_rtx (CONST_INT, VOIDmode, (~ (((unsigned) ~0 >> (HOST_BITS_PER_INT - bitsize)) << bitpos)) & ((GET_MODE_BITSIZE (mode) == HOST_BITS_PER_INT) ? -1 : ((1 << GET_MODE_BITSIZE (mode)) - 1))), subtarget); /* Now logical-or VALUE into OP0, unless it is zero. */ if (! all_zero) subtarget = expand_binop (mode, ior_optab, subtarget, value, op0, 1, OPTAB_LIB_WIDEN); if (op0 != subtarget) emit_move_insn (op0, subtarget);}/* Store a bit field that is split across two words. OP0 is the REG, SUBREG or MEM rtx for the first of the two words. BITSIZE is the field width; BITPOS the position of its first bit (within the word). VALUE is the value to store. */static voidstore_split_bit_field (op0, bitsize, bitpos, value, align) rtx op0; int bitsize, bitpos; rtx value; int align;{ /* BITSIZE_1 is size of the part in the first word. */ int bitsize_1 = BITS_PER_WORD - bitpos; /* BITSIZE_2 is size of the rest (in the following word). */ int bitsize_2 = bitsize - bitsize_1; rtx part1, part2; /* Alignment of VALUE, after conversion. */ int valalign = GET_MODE_SIZE (SImode); if (GET_MODE (value) != VOIDmode) value = convert_to_mode (SImode, value, 1); if (CONSTANT_P (value) && GET_CODE (value) != CONST_INT) value = copy_to_reg (value); /* Split the value into two parts: PART1 gets that which goes in the first word; PART2 the other. */#ifdef BYTES_BIG_ENDIAN /* PART1 gets the more significant part. */ if (GET_CODE (value) == CONST_INT) { part1 = gen_rtx (CONST_INT, VOIDmode, (unsigned) (INTVAL (value)) >> bitsize_2); part2 = gen_rtx (CONST_INT, VOIDmode, (unsigned) (INTVAL (value)) & ((1 << bitsize_2) - 1)); } else { part1 = extract_fixed_bit_field (SImode, value, 0, bitsize_1, BITS_PER_WORD - bitsize, 0, 1, valalign); part2 = extract_fixed_bit_field (SImode, value, 0, bitsize_2, BITS_PER_WORD - bitsize_2, 0, 1, valalign); }#else /* PART1 gets the less significant part. */ if (GET_CODE (value) == CONST_INT) { part1 = gen_rtx (CONST_INT, VOIDmode, (unsigned) (INTVAL (value)) & ((1 << bitsize_1) - 1)); part2 = gen_rtx (CONST_INT, VOIDmode, (unsigned) (INTVAL (value)) >> bitsize_1); } else { part1 = extract_fixed_bit_field (SImode, value, 0, bitsize_1, 0, 0, 1, valalign); part2 = extract_fixed_bit_field (SImode, value, 0, bitsize_2, bitsize_1, 0, 1, valalign); }#endif /* Store PART1 into the first word. */ store_fixed_bit_field (op0, 0, bitsize_1, bitpos, part1, align); /* Offset op0 to get to the following word. */ if (GET_CODE (op0) == MEM) op0 = change_address (op0, SImode, plus_constant (XEXP (op0, 0), UNITS_PER_WORD)); else if (GET_CODE (op0) == REG) op0 = gen_rtx (SUBREG, SImode, op0, 1); else if (GET_CODE (op0) == SUBREG) op0 = gen_rtx (SUBREG, SImode, SUBREG_REG (op0), SUBREG_WORD (op0) + 1); /* Store PART2 into the second word. */ store_fixed_bit_field (op0, 0, bitsize_2, 0, part2, align);}/* Generate code to extract a byte-field from STR_RTX containing BITSIZE bits, starting at BITNUM, and put it in TARGET if possible (if TARGET is nonzero). Regardless of TARGET, we return the rtx for where the value is placed. It may be a QUEUED. STR_RTX is the structure containing the byte (a REG or MEM). UNSIGNEDP is nonzero if this is an unsigned bit field. MODE is the natural mode of the field value once extracted. TMODE is the mode the caller would like the value to have; but the value may be returned with type MODE instead. ALIGN is the alignment that STR_RTX is known to have, measured in bytes. TOTAL_SIZE is the total size in bytes of the structure, if known. Otherwise it is -1. If a TARGET is specified and we can store in it at no extra cost, we do so, and return TARGET. Otherwise, we return a REG of mode TMODE or MODE, with TMODE preferred if they are equally easy. */rtxextract_bit_field (str_rtx, bitsize, bitnum, unsignedp, target, mode, tmode, align, total_size) rtx str_rtx; register int bitsize; int bitnum; int unsignedp; rtx target; enum machine_mode mode, tmode; 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 spec_target = target; rtx bitsize_rtx, bitpos_rtx; rtx spec_target_subreg = 0; /* 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)); if (tmode == VOIDmode) tmode = mode; 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); } #ifdef 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)); /* 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 /* Extracting a full-word or multi-word value from a structure in a register. This can be done with just SUBREG. So too extracting a subword value in the least significant part of the register. */ if (GET_CODE (op0) == REG && ((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode) && bitpos % BITS_PER_WORD == 0) || ((bitsize == GET_MODE_BITSIZE (mode) || bitsize == GET_MODE_BITSIZE (QImode) || bitsize == GET_MODE_BITSIZE (HImode))#ifdef BYTES_BIG_ENDIAN && bitpos + bitsize == BITS_PER_WORD#else && bitpos == 0#endif ))) { enum machine_mode mode1 = mode; if (bitsize == GET_MODE_BITSIZE (QImode)) mode1 = QImode; if (bitsize == GET_MODE_BITSIZE (HImode)) mode1 = HImode; if (mode1 != GET_MODE (op0)) { if (GET_CODE (op0) == SUBREG) PUT_MODE (op0, mode1); else op0 = gen_rtx (SUBREG, mode1, op0, offset); } if (mode1 != mode) return convert_to_mode (tmode, op0, unsignedp); return op0; } /* Handle fields bigger than a word. */ if (bitsize > BITS_PER_WORD) { int low_size = BITS_PER_WORD; int low_pos = bitpos + offset * unit; rtx target_low_part, low_part; int high_size = bitsize - low_size; int high_pos; rtx target_high_part, high_part;#ifdef BYTES_BIG_ENDIAN high_pos = low_pos; low_pos += high_size;#else high_pos = low_pos + low_size;#endif if (target == 0 || GET_CODE (target) != REG) target = gen_reg_rtx (mode); /* Extract the low part of the bitfield, and make sure to store it in the low part of TARGET. */ target_low_part = gen_lowpart (SImode, target); low_part = extract_bit_field (op0, low_size, low_pos, 1, target_low_part, SImode, SImode, align, total_size); if (low_part != target_low_part) emit_move_insn (target_low_part, low_part); /* Likewise for the high part. */ target_high_part = gen_highpart (SImode, target); high_part = extract_bit_field (op0, high_size, high_pos, unsignedp, target_high_part, SImode, SImode, align, total_size); if (high_part != target_high_part) emit_move_insn (target_high_part, high_part); return target; } /* From here on we know the desired field is smaller than a word so we can assume it is an integer. So we can safely extract it as one size of integer, if necessary, and then truncate or extend to the size that is wanted. */ /* 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 (str_rtx, 1); } /* Now OFFSET is nonzero only for memory operands. */ if (unsignedp) {#ifdef HAVE_extzv if (HAVE_extzv) { int xbitpos = bitpos, xoffset = offset; rtx last = get_last_insn(); rtx xop0 = op0; rtx xtarget = target; rtx xspec_target = spec_target; rtx xspec_target_subreg = spec_target_subreg; rtx pat; if (GET_CODE (xop0) == MEM) { extern int volatile_ok; int save_volatile_ok = volatile_ok; volatile_ok = 1; /* Is the memory operand acceptable? */ if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][1]) (xop0, GET_MODE (xop0)))) { /* No, load into a reg and extract from there. */ enum machine_mode bestmode = VOIDmode, trymode; /* Don't use a mode bigger than the one of the value to be fetched. That mode must be okay, since a bit field can be that big. */ int maxsize = GET_MODE_SIZE (insn_operand_mode[(int) CODE_FOR_extzv][0]); /* This used to use the mode desired for operand 1, 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 (); unit = GET_MODE_BITSIZE (bestmode); /* Compute offset as multiple of this unit, counting in bytes. */ 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); /* Now ref the register in the mode extzv wants. */ /* We used to use the mode from operand 1 in the md, but that is often QImode because that's needed for MEM. Here we need SImode instead. */ if (bestmode != SImode) xop0 = gen_rtx (SUBREG, SImode, xop0, 0);#ifdef BYTES_BIG_ENDIAN if (GET_MODE_BITSIZE (GET_MODE (xop0)) > unit) xbitpos += GET_MODE_BITSIZE (GET_MODE (xop0)) - unit;#endif } else /* Get ref to first byte containing part of the field. */ xop0 = change_address (xop0, QImode, plus_constant (XEXP (xop0, 0), xoffset)); volatile_ok = save_volatile_ok; } /* If op0 is a register, we need it in SImode to make it acceptable to the format of extzv. */ if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != SImode) abort (); 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); } if (xtarget == 0 || (flag_force_mem && GET_CODE (xtarget) == MEM)) xtarget = xspec_target = gen_reg_rtx (tmode); if (GET_MODE (xtarget) != SImode) { if (GET_CODE (xtarget) == REG) xspec_target_subreg = xtarget = gen_lowpart (SImode, xtarget); else xtarget = gen_reg_rtx (SImode); } /* If this machine's extzv insists on a register target, make sure we have one. */ if (! (*insn_operand_predicate[(int) CODE_FOR_extzv][0]) (xtarget, SImode)) xtarget = gen_reg_rtx (SImode); /* 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 bitsize_rtx = gen_rtx (CONST_INT, VOIDmode, bitsize); bitpos_rtx = gen_rtx (CONST_INT, VOIDmode, xbitpos); pat = gen_extzv (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, 1, align); } } else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -